From 2016979c835dbf788de8eef1871c46698fe45f7d Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 17 Aug 2012 15:19:33 -0600 Subject: [PATCH 0001/3476] Added form validation --- devshop_projects/devshop_projects.module | 113 ++++++++++++++++++----- 1 file changed, 91 insertions(+), 22 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 3176f7064..be71461f2 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -157,15 +157,24 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ */ function devshop_projects_form(&$node) { - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project.'), - '#size' => 40, - '#default_value' => $node->title, - '#maxlength' => 255, - ); + if(!$node->nid) { + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project.'), + '#size' => 40, + '#default_value' => $node->title, + '#maxlength' => 255, + ); + } + else { + $form['title'] = array( + '#type' => 'item', + '#title' => t('Project Code Name'), + '#value' => $node->title + ); + } $form['git_url'] = array( '#type' => 'textfield', @@ -179,16 +188,25 @@ function devshop_projects_form(&$node) { '#weight' => 1, ); - $form['code_path'] = array( - '#type' => 'textfield', - '#title' => t('Code path'), - '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $node->code_path, - '#maxlength' => 255, - '#weight' => 2, - ); + if(!$node->nid) { + $form['code_path'] = array( + '#type' => 'textfield', + '#title' => t('Code path'), + '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->code_path, + '#maxlength' => 255, + '#weight' => 2, + ); + } + else { + $form['code_path'] = array( + '#type' => 'item', + '#title' => t('Code path'), + '#value' => $node->code_path + ); + } $sites = array(0 => 'None'); $r = db_query("SELECT n.nid, n.title FROM {node} AS n " . @@ -206,6 +224,7 @@ function devshop_projects_form(&$node) { '#options' => $sites, '#description' => t('Select site that is going to be your development site.'), '#default_value' => $node->dev_site, + '#weight' => 20, ); $form['test_site'] = array( @@ -214,6 +233,7 @@ function devshop_projects_form(&$node) { '#options' => $sites, '#description' => t('Select site that is going to be your test site.'), '#default_value' => $node->test_site, + '#weight' => 21, ); $form['live_site'] = array( @@ -222,6 +242,7 @@ function devshop_projects_form(&$node) { '#options' => $sites, '#description' => t('Select site that is going to be your live site.'), '#default_value' => $node->live_site, + '#weight' => 22, ); return $form; @@ -252,7 +273,7 @@ function devshop_projects_insert($node) { db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path) ". "VALUES (%d, '%s', '%s')", - $node->nid, $node->git_url, $node->code_path); + $node->nid, $node->git_url, hosting_path_normalize($node->code_path)); _devshop_projects_insert_sites($node); // Save hosting context @@ -282,7 +303,7 @@ function devshop_projects_update($node) { else { db_query("UPDATE {hosting_devshop_project} " . "SET git_url = '%s', code_path = '%s' WHERE nid=%d", - $node->git_url, $node->code_path); + $node->git_url, hosting_path_normalize($node->code_path)); db_query('DELETE FROM {hosting_devshop_project_sites} WHERE nid = %d', $node->nid); _devshop_projects_insert_sites($node); } @@ -300,7 +321,55 @@ function devshop_projects_delete($node) { * Implementation of hook_validate(). */ function devshop_projects_validate($node, &$form) { -// TODO + // No validation if op == Delete + if ($node->op == t('Delete')) { + return; + } + + // Full validation on when a creating a new node + $add = (arg(1) == 'add' ? TRUE : FALSE); + + // Title (project code) must not have any spaces + if(strpos($node->title, ' ') != FALSE) { + form_set_error('title', t('Project code name must not contain any white spaces.')); + } + + // The project code name must not be in the hosting_context table + if ($result = db_fetch_object(db_query("SELECT name FROM {hosting_context} WHERE name = '%s'", $node->title))) { + form_set_error('title', t('Project code name existing in hosting context table.')); + } + + // The project code name must be unique + if ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid))) { + form_set_error('title', t('Project code name is already is use by another project')); + } + + // Make sure the path is unique. + $cp = hosting_path_normalize($node->code_path); + if ($add && $result = db_fetch_object(db_query("SELECT code_path FROM {hosting_devshop_project} WHERE code_path = '%s' AND nid <> %d", $cp, $node->nid))) { + form_set_error('code_path', t('Code path is already in use by another project')); + } + + // Directory must not exist + if ($add && file_exists($cp)) { + form_set_error('code_path', t('Code path directory already exists.')); + } + + // Sites may not be used in more than one environement + if($node->dev_site != 0 && $node->dev_site == $node->test_site) { + form_set_error('dev_site', t('Site used more than once.')); + form_set_error('test_site', t('Site used more than once.')); + } + + if($node->dev_site != 0 && $node->dev_site == $node->live_site) { + form_set_error('dev_site', t('Site used more than once.')); + form_set_error('live_site', t('Site used more than once.')); + } + + if($node->test_site != 0 && $node->test_site == $node->live_site) { + form_set_error('test_site', t('Site used more than once.')); + form_set_error('live_site', t('Site used more than once.')); + } } /** From fce26050bdbfab20a74d792f3e20e5ddd45d40b1 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 17 Aug 2012 16:39:37 -0600 Subject: [PATCH 0002/3476] Project list page done --- devshop_projects/devshop_projects.module | 40 +++++++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 995554428..6a50b8cf1 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -58,6 +58,13 @@ function devshop_projects_menu() { 'access arguments' => array('view projects'), ); + $items['hosting/projects/detail/%'] = array( + 'title' => 'Projects Details', + 'description' => 'Display details of a projects', + 'page callback' => 'devshop_projects_project_detail', + 'access arguments' => array('view projects'), + ); + return ($items); } @@ -68,12 +75,25 @@ function devshop_projects_menu() { */ function devshop_projects_projects_view() { - if ($list = drupal_get_form('devshop_projects_projects_list_form')) { - return $list; + + $header = array('Projects', 'Sites'); + + $r = db_query("SELECT * FROM {hosting_devshop_project}"); + $rows = array(); + + while(($proj = db_fetch_object($r))) { + $node = node_load($proj->nid); + $row = array(); + $row[] = $node->title; + $row[] = db_result(db_query("SELECT COUNT(*) " . + "FROM {hosting_devshop_project_sites} " . + "WHERE nid = %d", $node->nid)); + $rows[] = $row; } - $create_project_link = l(t('Create a site now?'), 'node/add/project'); - return t("No projects have been created yet. !link", array( - '!link' => $create_project_link)); + + $output = theme('table', $header, $rows, array('class' => 'hosting-table')); + $output .= l(t('Create a new project?'), 'node/add/devshop-project'); + return $output; } /** @@ -458,3 +478,13 @@ function hosting_devshop_projects_form_alter(&$form, &$form_state, $form_id) { $form['buttons']['delete']['#type'] = 'hidden'; } } + +function devshop_projects_project_detail() { + + $nid = arg(3); // ok, I'm cheating + if(!($node = node_load($nid)) || $node->type != 'devshop_project') { + return t('Project not found!'); + } + + return "hello"; +} From 0df5743ab424159f6a97c4c98891213709b74a85 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 11:42:35 -0400 Subject: [PATCH 0003/3476] Starting to come together... project is being created, but only with a patch to tasks.hosting.inc!! :( --- devshop_projects/devshop_projects.module | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6a50b8cf1..897ea5600 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -10,13 +10,24 @@ */ function devshop_projects_hosting_tasks() { $tasks = array(); - $tasks['devshop_project']['devshop-create'] = array( + $tasks['project']['devshop-create'] = array( 'title' => t('Create Project'), 'description' => t('Clones the repo, and creates the platforms.'), ); return $tasks; } +/** + * Implements hook_hosting_CONTEXTTYPE_context_options() + */ +function devshop_projects_hosting_project_context_options(&$task) { + $task->context_options['server'] = '@server_master'; + $task->context_options['path'] = trim($task->ref->code_path, " "); + if ($task->ref->git_url) { + $task->context_options['git_url'] = $task->ref->git_url; + } +} + /** * Implementation of hook_perm() */ @@ -31,7 +42,7 @@ function devshop_projects_perm() { */ function devshop_projects_node_info() { #configuration - $types["devshop_project"] = array( + $types["project"] = array( "type" => 'devshop_project', "name" => 'DevShop Project', "module" => 'devshop_projects', @@ -304,8 +315,8 @@ function devshop_projects_insert($node) { // Create hostmaster task // @TODO: This fails because there is no alias being saved for this node... $args = array(); - $args[] = $node->git_url; - $args[] = $node->code_path; + $args['git-url'] = $node->git_url; + $args['path'] = $node->code_path; hosting_add_task($node->nid, 'devshop-create', $args); } @@ -404,6 +415,7 @@ function devshop_projects_load($node) { 'WHERE nid = %d', $node->nid)); $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); if (is_array($hosting_name) && is_array($additions)) { + $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; $additions += $hosting_name; } $env = array('dev' => 'dev_site', From 2c32e6368ab09938104a2f3419299e4cc59e2ac5 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 11:55:18 -0400 Subject: [PATCH 0004/3476] Fixing properties of project context! --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 897ea5600..480f3f4c7 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -22,7 +22,7 @@ function devshop_projects_hosting_tasks() { */ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['server'] = '@server_master'; - $task->context_options['path'] = trim($task->ref->code_path, " "); + $task->context_options['code_path'] = trim($task->ref->code_path, " "); if ($task->ref->git_url) { $task->context_options['git_url'] = $task->ref->git_url; } From 5c0764d7cac31fcce8b92d42c02ee4d7bb781715 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 12:00:04 -0400 Subject: [PATCH 0005/3476] adding a quick readme ;) --- devshop_projects/README.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 devshop_projects/README.txt diff --git a/devshop_projects/README.txt b/devshop_projects/README.txt new file mode 100644 index 000000000..b68ad016b --- /dev/null +++ b/devshop_projects/README.txt @@ -0,0 +1,2 @@ +This module requires one tiny patch to hosting_tasks.module... + From ca7535d96eda36a5c24876dcaca5c1bdac0afe00 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 12:10:13 -0400 Subject: [PATCH 0006/3476] project_name! --- devshop_projects/devshop_projects.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 480f3f4c7..9c2a99a77 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -22,6 +22,7 @@ function devshop_projects_hosting_tasks() { */ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['server'] = '@server_master'; + $task->context_options['project_name'] = $task->ref->title; $task->context_options['code_path'] = trim($task->ref->code_path, " "); if ($task->ref->git_url) { $task->context_options['git_url'] = $task->ref->git_url; From 712a38c8cb988818ad6f08ee05eeef204427655d Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 12:35:59 -0400 Subject: [PATCH 0007/3476] removing environment type dropdown --- devshop_projects/devshop_projects.module | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9c2a99a77..b3ee87d6d 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -170,17 +170,17 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#type' => 'fieldset', '#title' => t('DevShop'), ); - - $etype = array('dev' => 'Dev', - 'test' => 'Test', - 'live' => 'Live'); - - $form['devshop']['environement'] = array( - '#title' => 'Environment Type', - '#type' => 'select', - '#description' => t('Select the environement type for this site'), - '#options' => $etype, - ); +// @TODO: Remove. This will be done automatically. +// $etype = array('dev' => 'Dev', +// 'test' => 'Test', +// 'live' => 'Live'); +// +// $form['devshop']['environement'] = array( +// '#title' => 'Environment Type', +// '#type' => 'select', +// '#description' => t('Select the environement type for this site'), +// '#options' => $etype, +// ); } } From 8c9949022382b3ac7dbc0dd9cfe716fa524ec6e9 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 12:42:50 -0400 Subject: [PATCH 0008/3476] rearranging form and improving comments --- devshop_projects/devshop_projects.module | 143 ++++++++++++----------- 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index b3ee87d6d..b11e8ba59 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -189,6 +189,18 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ */ function devshop_projects_form(&$node) { + $form['git_url'] = array( + '#type' => 'textfield', + '#title' => t('Git URL'), + '#required' => TRUE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->git_url, + '#maxlength' => 255, + '#weight' => 1, + ); + if(!$node->nid) { $form['title'] = array( '#type' => 'textfield', @@ -208,17 +220,6 @@ function devshop_projects_form(&$node) { ); } - $form['git_url'] = array( - '#type' => 'textfield', - '#title' => t('Git URL'), - '#required' => TRUE, - '#description' => t(''), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $node->git_url, - '#maxlength' => 255, - '#weight' => 1, - ); if(!$node->nid) { $form['code_path'] = array( @@ -239,64 +240,71 @@ function devshop_projects_form(&$node) { '#value' => $node->code_path ); } - - $sites = array(0 => 'None'); - $r = db_query("SELECT n.nid, n.title FROM {node} AS n " . - "LEFT JOIN {hosting_site} AS h ON n.nid = h.nid " . - "WHERE n.type = 'site' AND h.status = 1 " . - "ORDER BY n.title ASC"); - - while($sa = db_fetch_object($r)) { - $sites[$sa->nid] = $sa->title; - } - - $form['dev_site'] = array( - '#type' => 'select', - '#title' => t('DEV Site'), - '#options' => $sites, - '#description' => t('Select site that is going to be your development site.'), - '#default_value' => $node->dev_site, - '#weight' => 20, - ); - - $form['test_site'] = array( - '#type' => 'select', - '#title' => t('TEST Site'), - '#options' => $sites, - '#description' => t('Select site that is going to be your test site.'), - '#default_value' => $node->test_site, - '#weight' => 21, - ); - - $form['live_site'] = array( - '#type' => 'select', - '#title' => t('LIVE Site'), - '#options' => $sites, - '#description' => t('Select site that is going to be your live site.'), - '#default_value' => $node->live_site, - '#weight' => 22, - ); + + //@TODO: Remove. I think that instead, we should let the script create the platforms. + // We have to pause at that point anyway, so the user can choose the available install profiles for their three sites. + // Lets let provision-devshop-create import all three platforms and verify, then let the users MIGRATE their existing + // sites ONTO the new platforms, IF they wish. In my opinion, migration of the old sites to our automatically built + // platforms is a better way to go. + // +// +// $sites = array(0 => 'None'); +// $r = db_query("SELECT n.nid, n.title FROM {node} AS n " . +// "LEFT JOIN {hosting_site} AS h ON n.nid = h.nid " . +// "WHERE n.type = 'site' AND h.status = 1 " . +// "ORDER BY n.title ASC"); +// +// while($sa = db_fetch_object($r)) { +// $sites[$sa->nid] = $sa->title; +// } +// +// $form['dev_site'] = array( +// '#type' => 'select', +// '#title' => t('DEV Site'), +// '#options' => $sites, +// '#description' => t('Select site that is going to be your development site.'), +// '#default_value' => $node->dev_site, +// '#weight' => 20, +// ); +// +// $form['test_site'] = array( +// '#type' => 'select', +// '#title' => t('TEST Site'), +// '#options' => $sites, +// '#description' => t('Select site that is going to be your test site.'), +// '#default_value' => $node->test_site, +// '#weight' => 21, +// ); +// +// $form['live_site'] = array( +// '#type' => 'select', +// '#title' => t('LIVE Site'), +// '#options' => $sites, +// '#description' => t('Select site that is going to be your live site.'), +// '#default_value' => $node->live_site, +// '#weight' => 22, +// ); return $form; } - -/** - * Insert the sites into the sites table - */ -function _devshop_projects_insert_sites($node) { - - foreach(array('dev_site' => 'dev', - 'test_site' => 'test', - 'live_site' => 'live') as $k => $v) { - if($node->$k) { - $snode = node_load($node->$k); - db_query("INSERT INTO {hosting_devshop_project_sites} " . - "(nid, site_nid, platform_nid, env_type) " . - "VALUES (%d, %d, %d, '%s')", - $node->nid, $node->$k, $snode->platform, $v); - } - } -} +// +///** +// * Insert the sites into the sites table +// */ +//function _devshop_projects_insert_sites($node) { +// +// foreach(array('dev_site' => 'dev', +// 'test_site' => 'test', +// 'live_site' => 'live') as $k => $v) { +// if($node->$k) { +// $snode = node_load($node->$k); +// db_query("INSERT INTO {hosting_devshop_project_sites} " . +// "(nid, site_nid, platform_nid, env_type) " . +// "VALUES (%d, %d, %d, '%s')", +// $node->nid, $node->$k, $snode->platform, $v); +// } +// } +//} /** * Implementation of hook_insert(). @@ -306,7 +314,7 @@ function devshop_projects_insert($node) { db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path) ". "VALUES (%d, '%s', '%s')", $node->nid, $node->git_url, hosting_path_normalize($node->code_path)); - _devshop_projects_insert_sites($node); +// _devshop_projects_insert_sites($node); // Save hosting context if ((!$node->old_vid)) { @@ -314,7 +322,6 @@ function devshop_projects_insert($node) { } // Create hostmaster task - // @TODO: This fails because there is no alias being saved for this node... $args = array(); $args['git-url'] = $node->git_url; $args['path'] = $node->code_path; From 27b26b1e9476d275dfbaa9b8e0cdb26228662e81 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 12:48:53 -0400 Subject: [PATCH 0009/3476] unsetting menu options in project form --- devshop_projects/devshop_projects.module | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index b11e8ba59..7ec4e6559 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -164,23 +164,11 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { * Implements hook_form_alter(). */ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ - if ($form_id == 'site_node_form'){ - $node = $form['#node']; - $form['devshop'] = array( - '#type' => 'fieldset', - '#title' => t('DevShop'), - ); -// @TODO: Remove. This will be done automatically. -// $etype = array('dev' => 'Dev', -// 'test' => 'Test', -// 'live' => 'Live'); -// -// $form['devshop']['environement'] = array( -// '#title' => 'Environment Type', -// '#type' => 'select', -// '#description' => t('Select the environement type for this site'), -// '#options' => $etype, -// ); + if ($form_id == 'project_node_form'){ + unset($form['menu']); + unset($form['revision_information']); + unset($form['author']); + unset($form['options']); } } From f38c4def164bf10014ab4b577281036c1f278e1f Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 12:53:44 -0400 Subject: [PATCH 0010/3476] taking domain name --- devshop_projects/devshop_projects.module | 73 ++++++++++++------------ 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 7ec4e6559..1e43f3565 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -188,45 +188,42 @@ function devshop_projects_form(&$node) { '#maxlength' => 255, '#weight' => 1, ); + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project.'), + '#size' => 40, + '#default_value' => $node->title, + '#maxlength' => 255, + ); + $form['code_path'] = array( + '#type' => 'textfield', + '#title' => t('Code path'), + '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->code_path, + '#maxlength' => 255, + '#weight' => 2, + ); + $form['domain'] = array( + '#type' => 'textfield', + '#title' => t('Primary Domain'), + '#description' => t('The domain name all sites will be built under.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->domain, + '#maxlength' => 255, + '#weight' => 2, + ); - if(!$node->nid) { - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project.'), - '#size' => 40, - '#default_value' => $node->title, - '#maxlength' => 255, - ); - } - else { - $form['title'] = array( - '#type' => 'item', - '#title' => t('Project Code Name'), - '#value' => $node->title - ); - } - - - if(!$node->nid) { - $form['code_path'] = array( - '#type' => 'textfield', - '#title' => t('Code path'), - '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $node->code_path, - '#maxlength' => 255, - '#weight' => 2, - ); - } - else { - $form['code_path'] = array( - '#type' => 'item', - '#title' => t('Code path'), - '#value' => $node->code_path - ); + // Don't allow editing + if ($node->nid) { + $form['code_path']['#type'] = $form['title']['#type'] = $form['git_url']['#type'] = 'item'; + $form['code_path']['#value'] = $form['code_path']['#default_value']; + $form['title']['#value'] = $form['title']['#default_value']; + $form['git_url']['#value'] = $form['git_url']['#default_value']; } //@TODO: Remove. I think that instead, we should let the script create the platforms. From 387f2e76bacdfb98392db19931525f6617a436ef Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 12:55:02 -0400 Subject: [PATCH 0011/3476] base URL schema --- devshop_projects/devshop_projects.install | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index a91141e29..e57c508fc 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -27,6 +27,11 @@ function devshop_projects_schema() { 'size' => 'big', 'not null' => FALSE, ), + 'base_url' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), ), 'primary key' => array('nid'), ); From 6300145d74b4bab82a3305edc54bfe2ad0c39112 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 13:00:51 -0400 Subject: [PATCH 0012/3476] adding base_url to the context object --- devshop_projects/devshop_projects.module | 25 +++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 1e43f3565..540114ec4 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -296,9 +296,9 @@ function devshop_projects_form(&$node) { */ function devshop_projects_insert($node) { - db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path) ". - "VALUES (%d, '%s', '%s')", - $node->nid, $node->git_url, hosting_path_normalize($node->code_path)); + db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url) ". + "VALUES (%d, '%s', '%s', '%s')", + $node->nid, $node->git_url, hosting_path_normalize($node->code_path), $node->base_url); // _devshop_projects_insert_sites($node); // Save hosting context @@ -307,10 +307,7 @@ function devshop_projects_insert($node) { } // Create hostmaster task - $args = array(); - $args['git-url'] = $node->git_url; - $args['path'] = $node->code_path; - hosting_add_task($node->nid, 'devshop-create', $args); + hosting_add_task($node->nid, 'devshop-create'); } /** @@ -322,14 +319,14 @@ function devshop_projects_insert($node) { function devshop_projects_update($node) { // if this is a new node or we're adding a new revision, if (!empty($node->revision)) { - hosting_devshop_project_insert($node); + //hosting_devshop_project_insert($node); } else { - db_query("UPDATE {hosting_devshop_project} " . - "SET git_url = '%s', code_path = '%s' WHERE nid=%d", - $node->git_url, hosting_path_normalize($node->code_path)); - db_query('DELETE FROM {hosting_devshop_project_sites} WHERE nid = %d', $node->nid); - _devshop_projects_insert_sites($node); + //db_query("UPDATE {hosting_devshop_project} " . + // "SET git_url = '%s', code_path = '%s' WHERE nid=%d", + // $node->git_url, hosting_path_normalize($node->code_path)); + //db_query('DELETE FROM {hosting_devshop_project_sites} WHERE nid = %d', $node->nid); + //_devshop_projects_insert_sites($node); } } @@ -403,7 +400,7 @@ function devshop_projects_validate($node, &$form) { * Node object */ function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT git_url, code_path ' . + $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url ' . 'FROM {hosting_devshop_project} ' . 'WHERE nid = %d', $node->nid)); $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); From d002737ccac21eb9faf5c220146da50b852dbd73 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 13:02:31 -0400 Subject: [PATCH 0013/3476] fixing base_url --- devshop_projects/devshop_projects.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 540114ec4..4d0cc33ca 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -207,13 +207,13 @@ function devshop_projects_form(&$node) { '#maxlength' => 255, '#weight' => 2, ); - $form['domain'] = array( + $form['base_url'] = array( '#type' => 'textfield', '#title' => t('Primary Domain'), '#description' => t('The domain name all sites will be built under.'), '#required' => TRUE, '#size' => 40, - '#default_value' => $node->domain, + '#default_value' => $node->base_url, '#maxlength' => 255, '#weight' => 2, ); From 6a0195d8d1fb69395e81c34d98eba2cd8fdbf299 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 13:57:56 -0400 Subject: [PATCH 0014/3476] putting the context options hooks in place, but I believe we still have to modify the context objects. --- devshop_projects/devshop_projects.module | 25 +++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 4d0cc33ca..a5e92aaf4 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -18,7 +18,10 @@ function devshop_projects_hosting_tasks() { } /** - * Implements hook_hosting_CONTEXTTYPE_context_options() + * Implements hook_hosting_project_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For project entities. */ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['server'] = '@server_master'; @@ -29,6 +32,26 @@ function devshop_projects_hosting_project_context_options(&$task) { } } +/** + * Implements hook_hosting_site_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For site entities. + */ +function devshop_projects_hosting_site_context_options(&$task) { + $task->context_options['project'] = $task->ref->git_url; +} + +/** + * Implements hook_hosting_site_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For site entities. + */ +function devshop_projects_hosting_platform_context_options(&$task) { + $task->context_options['project'] = $task->ref->project; +} + /** * Implementation of hook_perm() */ From 072d191d1d8884520da31568f57d8521d8337fb6 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 14:07:21 -0400 Subject: [PATCH 0015/3476] fixing "new project" link --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a5e92aaf4..e09f1f21a 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -127,7 +127,7 @@ function devshop_projects_projects_view() { } $output = theme('table', $header, $rows, array('class' => 'hosting-table')); - $output .= l(t('Create a new project?'), 'node/add/devshop-project'); + $output .= l(t('Create a new project?'), 'node/add/project'); return $output; } From ad226638ed46888b54d4c81f4250d78687cdebb2 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 14:14:33 -0400 Subject: [PATCH 0016/3476] changing schema --- devshop_projects/devshop_projects.install | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index e57c508fc..bfc3c3190 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -38,23 +38,24 @@ function devshop_projects_schema() { $schema['hosting_devshop_project_sites'] = array( 'fields' => array( - 'nid' => array( + 'project_nid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Project/Node ID.', ), - 'site_nid' => array( + 'object_nid' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Site NID.', ), - 'platform_nid' => array( - 'type' => 'int', + 'object_type' => array( + 'type' => 'varchar', 'not null' => TRUE, - 'default' => 0, + 'length' => 10, + 'default' => '', 'description' => 'The node ID of the platform.', ), 'env_type' => array( @@ -65,7 +66,7 @@ function devshop_projects_schema() { 'description' => 'Environment type: dev, test, live, or sandbox.', ), ), - 'primary key' => array('nid', 'site_nid', 'platform_nid', 'env_type'), + 'primary key' => array('object_nid'), ); return $schema; From 664486ea87602779073a0a716014a724c9a6ee65 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 14:23:10 -0400 Subject: [PATCH 0017/3476] updating schema --- devshop_projects/devshop_projects.install | 2 +- devshop_projects/devshop_projects.module | 20 +++++--------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index bfc3c3190..b0bc27f79 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -36,7 +36,7 @@ function devshop_projects_schema() { 'primary key' => array('nid'), ); - $schema['hosting_devshop_project_sites'] = array( + $schema['hosting_devshop_project_object'] = array( 'fields' => array( 'project_nid' => array( 'type' => 'int', diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index e09f1f21a..41a60e8c1 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -121,7 +121,7 @@ function devshop_projects_projects_view() { $row = array(); $row[] = $node->title; $row[] = db_result(db_query("SELECT COUNT(*) " . - "FROM {hosting_devshop_project_sites} " . + "FROM {hosting_devshop_project_object}" . "WHERE nid = %d", $node->nid)); $rows[] = $row; } @@ -340,17 +340,7 @@ function devshop_projects_insert($node) { * database updates. */ function devshop_projects_update($node) { - // if this is a new node or we're adding a new revision, - if (!empty($node->revision)) { - //hosting_devshop_project_insert($node); - } - else { - //db_query("UPDATE {hosting_devshop_project} " . - // "SET git_url = '%s', code_path = '%s' WHERE nid=%d", - // $node->git_url, hosting_path_normalize($node->code_path)); - //db_query('DELETE FROM {hosting_devshop_project_sites} WHERE nid = %d', $node->nid); - //_devshop_projects_insert_sites($node); - } + db_query("UPDATE {hosting_devshop_project} SET git_url = '%s', code_path = '%s' WHERE nid=%d", $node->git_url, hosting_path_normalize($node->code_path)); } /** @@ -358,7 +348,7 @@ function devshop_projects_update($node) { */ function devshop_projects_delete($node) { db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); - db_query('DELETE FROM {hosting_devshop_project_sites} WHERE nid = %d', $node->nid); + db_query('DELETE FROM {hosting_devshop_project_object} WHERE nid = %d', $node->nid); } /** @@ -435,8 +425,8 @@ function devshop_projects_load($node) { 'test' => 'test_site', 'live' => 'live_site'); - $r = db_query("SELECT * FROM {hosting_devshop_project_sites} " . - "WHERE nid = %d", $node->nid); + $r = db_query("SELECT * FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d", $node->nid); while(($e = db_fetch_object($r))) { if(!empty($env[$e->env_type])) { From b26500e6558fe53c749c84135e61a80f61abd302 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 14:25:40 -0400 Subject: [PATCH 0018/3476] fixing project nids --- devshop_projects/devshop_projects.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 41a60e8c1..7a0e7f6e2 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -122,7 +122,7 @@ function devshop_projects_projects_view() { $row[] = $node->title; $row[] = db_result(db_query("SELECT COUNT(*) " . "FROM {hosting_devshop_project_object}" . - "WHERE nid = %d", $node->nid)); + "WHERE project_nid = %d", $node->nid)); $rows[] = $row; } @@ -348,7 +348,7 @@ function devshop_projects_update($node) { */ function devshop_projects_delete($node) { db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); - db_query('DELETE FROM {hosting_devshop_project_object} WHERE nid = %d', $node->nid); + db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); } /** From d572a1b0a2ac569f04ce938ae98d55b8491e0e38 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 14:39:09 -0400 Subject: [PATCH 0019/3476] creating rough outline of status field --- devshop_projects/devshop_projects.module | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 7a0e7f6e2..e9e3642dc 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -476,8 +476,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->content['info']['sites'] = array( '#type' => 'item', - '#title' => t('Project Members'), - '#value' => $table, + '#title' => t('Status'), + '#value' => devshop_projects_project_status($node), '#weight' => -7 ); @@ -485,15 +485,25 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } /** - * Hide the delete button on project nodes. + * Status display */ -function hosting_devshop_projects_form_alter(&$form, &$form_state, $form_id) { - // Remove delete button from the edit form - if ($form_id == 'devshop_project_node_form') { - $form['buttons']['delete']['#type'] = 'hidden'; +function devshop_projects_project_status(){ + + if ($create_task_exists) { + $msg = t('Git Cloning...'); + } elseif ($platforms_exist) { + $msg = t('Verifying Platforms...'); + } elseif ($platforms_verified) { + $msg = t('Platforms Verified!'); + $msg .= drupal_get_form('devshop_projects_project_site_install'); + } else { + $msg = t('@TODO'); } + + return $msg; } + function devshop_projects_project_detail() { $nid = arg(3); // ok, I'm cheating From d6fd4e204b9c2da1e0de5be681619a26d7c2a732 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 15:39:00 -0400 Subject: [PATCH 0020/3476] adding comment --- devshop_projects/devshop_projects.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index e9e3642dc..10b1592fe 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -187,6 +187,8 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { * Implements hook_form_alter(). */ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ + + // Removing unneccesary fieldgroups if ($form_id == 'project_node_form'){ unset($form['menu']); unset($form['revision_information']); From c77a1cd1192022a41c3d631330d84a951e9a4026 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 15:43:59 -0400 Subject: [PATCH 0021/3476] adding base URL to display of project --- devshop_projects/devshop_projects.module | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 10b1592fe..5fe24f742 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -462,8 +462,14 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#value' => filter_xss($node->code_path), '#weight' => -8 ); - - $header = array('Environement', 'Site'); + $url = 'http://' . $node->base_url; + $node->content['info']['base_url'] = array( + '#type' => 'item', + '#title' => t('Base URL'), + '#value' => l($url, $url), + '#weight' => -10 + ); + $header = array('Environment', 'Site'); $rows = array(); foreach(array('dev_site' => 'DEV Site', 'test_site' => 'TEST Site', From 0c2a51363bc87003486bd57d93252f45d8c50f0f Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 16:28:15 -0400 Subject: [PATCH 0022/3476] inserting into hosting_devshop_project_object --- devshop_projects/devshop_projects.module | 56 +++++++++--------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 5fe24f742..55cf48717 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -144,45 +144,31 @@ function devshop_projects_projects_list_form($form_state, $filter_by = NULL, $fi */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - $etype = array('dev' => 'Dev', - 'test' => 'Test', - 'live' => 'Live'); - - if ($node->type == 'site') { - switch ($op) { - case 'view': - if (!$a3) { //!teaser - $node->content['info']['environment'] = array( - '#type' => 'item', - '#title' => t('Environment Type'), - '#value' => t($etype[$node->environment]), - '#description' => t('') - ); - - } - break; - - case 'load': - if (!empty($url)){ - $node->git_url = $url; - } - break; - - case 'insert': - case 'update': -/* - db_query("DELETE FROM {hosting_platform_git} " . - "WHERE nid = %d", $node->nid); - db_query("INSERT INTO {hosting_platform_git} " . - "(nid, git_url, cloned) " . - "VALUES (%d, '%s', 0)", - $node->nid, $node->git_url); -*/ - break; + // On insert of a platform, determine project and save to hosting_devshop_project_objects + if ($node->type == 'platform'){ + if ($op == 'insert') { + $path = explode('/', $node->publish_path); + $environment = array_pop($path); + $project_path = implode('/', $path); + + $project = devshop_project_load_by_path($project_path); + + // Save to table + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $project->type, $environment); + // dsm("INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES ($project->nid, $node->nid, $project->type, $environment"); } } } +/** + * Helper to load a project node by path + */ +function devshop_project_load_by_path($project_path){ + $nid = db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s"', array($project_path))); + return node_load($nid); +} + + /** * Implements hook_form_alter(). */ From ff7b6670e76ac7475aacabc247bd1ff990d6e7b1 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 16:30:01 -0400 Subject: [PATCH 0023/3476] loading project nid into platform node --- devshop_projects/devshop_projects.module | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 55cf48717..fc67598ba 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -157,6 +157,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $project->type, $environment); // dsm("INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES ($project->nid, $node->nid, $project->type, $environment"); } + + // Load Project + if ($op == 'load'){ + $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); + return $data; + } } } From 6f03e82c02393194a69846e548183600645692a4 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 16:32:19 -0400 Subject: [PATCH 0024/3476] displaying a link to the project on a platform node --- devshop_projects/devshop_projects.module | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index fc67598ba..1fb29bd0d 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -163,6 +163,18 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); return $data; } + + // Load Project + if ($op == 'view'){ + $project = node_load($node->project_nid); + $node->content['info']['project'] = array( + '#type' => 'item', + '#title' => t('Project'), + '#value' => l($project->title, "node/$project->nid"), + '#weight' => -10 + ); + return $data; + } } } From 9a21888c13fe1431fb3353e8187cbf99fde98203 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 16:35:32 -0400 Subject: [PATCH 0025/3476] fixing row for projects page --- devshop_projects/devshop_projects.module | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 1fb29bd0d..3fb50de6f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -119,10 +119,8 @@ function devshop_projects_projects_view() { while(($proj = db_fetch_object($r))) { $node = node_load($proj->nid); $row = array(); - $row[] = $node->title; - $row[] = db_result(db_query("SELECT COUNT(*) " . - "FROM {hosting_devshop_project_object}" . - "WHERE project_nid = %d", $node->nid)); + $row[] = l($node->title, "node/$proj->nid"); + $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid)); $rows[] = $row; } From adb16e724407f34841a838348baec2eff6c5bcc5 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 16:46:50 -0400 Subject: [PATCH 0026/3476] adding hosting context deletion --- devshop_projects/devshop_projects.module | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 3fb50de6f..6aaffdf49 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -70,7 +70,7 @@ function devshop_projects_node_info() { "type" => 'devshop_project', "name" => 'DevShop Project', "module" => 'devshop_projects', - "has_title" => TRUE, + "has_title" => TRUE, "title_label" => t('Project Codename'), "description" => t('Create a project'), "has_body" => 0, @@ -353,6 +353,8 @@ function devshop_projects_update($node) { function devshop_projects_delete($node) { db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); + + hosting_context_delete($node->nid); } /** From 869578f02fbcc43ebd228a26c627b3a23c57e1c8 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 17:00:34 -0400 Subject: [PATCH 0027/3476] fixing node loading and cleaning up --- devshop_projects/devshop_projects.module | 39 +++++++++++------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6aaffdf49..24acc3097 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -142,8 +142,8 @@ function devshop_projects_projects_list_form($form_state, $filter_by = NULL, $fi */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - // On insert of a platform, determine project and save to hosting_devshop_project_objects if ($node->type == 'platform'){ + // On insert of a platform, determine project and save to hosting_devshop_project_objects if ($op == 'insert') { $path = explode('/', $node->publish_path); $environment = array_pop($path); @@ -152,17 +152,16 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $project = devshop_project_load_by_path($project_path); // Save to table - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $project->type, $environment); - // dsm("INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES ($project->nid, $node->nid, $project->type, $environment"); + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $environment); } - // Load Project + // Load Platform if ($op == 'load'){ $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); return $data; } - // Load Project + // View Platform if ($op == 'view'){ $project = node_load($node->project_nid); $node->content['info']['project'] = array( @@ -427,18 +426,14 @@ function devshop_projects_load($node) { $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; $additions += $hosting_name; } - $env = array('dev' => 'dev_site', - 'test' => 'test_site', - 'live' => 'live_site'); - - $r = db_query("SELECT * FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d", $node->nid); + + $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); - while(($e = db_fetch_object($r))) { - if(!empty($env[$e->env_type])) { - $additions[$env[$e->env_type]] = $e->site_nid; - } + $objects = array(); + while($project_object = db_fetch_object($query)) { + $objects[$project_object->object_type][$project_object->object_nid] = $project_object->env_type; } + $additions['project_objects'] = $objects; return $additions; } @@ -473,14 +468,14 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#value' => l($url, $url), '#weight' => -10 ); - $header = array('Environment', 'Site'); + + + + $header = array('Object', 'Type'); $rows = array(); - foreach(array('dev_site' => 'DEV Site', - 'test_site' => 'TEST Site', - 'live_site' => 'LIVE Site') as $k => $v) { - - $row = array($v, $node->$k ? node_load($node->$k)->title : - "Not assigned yet"); + foreach($node->project_objects as $nid => $object) { + $object = node_load($nid); + $row = array(l($object->title, $object->nid), $object->type); $rows[] = $row; } From c2c6f876fae5440afc3f1bd254712faddcf9ab2b Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 17:05:33 -0400 Subject: [PATCH 0028/3476] displaying platforms on project page --- devshop_projects/devshop_projects.module | 35 ++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 24acc3097..a642692e5 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -469,25 +469,26 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -10 ); - - - $header = array('Object', 'Type'); - $rows = array(); - foreach($node->project_objects as $nid => $object) { - $object = node_load($nid); - $row = array(l($object->title, $object->nid), $object->type); - $rows[] = $row; - } - - $table = theme('table', $header, $rows); + foreach($node->project_objects as $type => $objects) { + $header = array($type ."s"); + $rows = array(); + foreach($objects as $nid => $object) { + $object = node_load($nid); + $row = array(l($object->title, $object->nid)); + $rows[] = $row; + + } + $table = theme('table', $header, $rows); - $node->content['info']['sites'] = array( - '#type' => 'item', - '#title' => t('Status'), - '#value' => devshop_projects_project_status($node), - '#weight' => -7 - ); + $node->content['info'][$type] = array( + '#type' => 'item', + '#value' => $table, + ); + + } + + return $node; } From a112bd9de14fdbced5c8f74490d9bc6b1bc46766 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 17:06:50 -0400 Subject: [PATCH 0029/3476] fixing link on project view page --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a642692e5..59c1f75b8 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -474,7 +474,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $rows = array(); foreach($objects as $nid => $object) { $object = node_load($nid); - $row = array(l($object->title, $object->nid)); + $row = array(l($object->title, "node/$object->nid")); $rows[] = $row; } From ea6e5c9fa7c24c382393f97df0436ebdb4e28bee Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 17:39:30 -0400 Subject: [PATCH 0030/3476] adding site install submit form --- devshop_projects/devshop_projects.module | 66 +++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 59c1f75b8..28ea3f009 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -483,8 +483,20 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->content['info'][$type] = array( '#type' => 'item', '#value' => $table, + ); + } + + // MAIN DISPLAY + if (!isset($node->project_objects['site'])){ + $node->content['ready'] = array( + '#type' => 'item', + '#value' => t("Your project's code is ready! Now you must create your sites."), ); - + $node->content['install'] =array( + '#type' => 'item', + '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), + ); + } @@ -492,6 +504,58 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { return $node; } +/** + * Form for site installation + */ +function devshop_projects_install_sites_form($form_state, $project_node) { + // Load all install profiles found on dev platform + $platform_nid = key($project_node->project_objects['platform']); + + $profiles = array_combine(hosting_get_profiles($platform_nid, 'short_name'), hosting_get_profiles($platform_nid)); + + $form = array(); + $form['install_profile'] = array( + '#type' => 'radios', + '#title' => t('Choose your install profile'), + '#options' => $profiles, + ); + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Create dev, test, and live sites'), + ); + return $form; +} + +/** + * Validates for site installation + */ +function devshop_projects_install_sites_form_validate(&$form_state){ + if (empty($form_state['values']['install_profile'])){ + form_set_error('install_profile', t('You must choose an install profile')); + } + + +} +/** + * Submit for site installation + * + * I believe, based on the fact that provision has to create + * the site aliases before it runs provision-install, we have + * to have a new hostmaster task here. + * + * If we save the install_profile in the project context, + * the task could easily create the new sites. + */ +function devshop_projects_install_sites_form_submit(&$form_state){ + // @TODO: Save a "save-project" task that saves the installation + // profile to the project context. + // hostmaster_add_task($form_state['values']['nid'], 'devshop-save-project', ) +} + /** * Status display */ From 0c2d8539ad6a60fa9279e9f041b90c5844e35e91 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 17:54:20 -0400 Subject: [PATCH 0031/3476] adding install profile to project info --- devshop_projects/devshop_projects.install | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index b0bc27f79..0988a55ea 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -32,6 +32,11 @@ function devshop_projects_schema() { 'size' => 'big', 'not null' => FALSE, ), + 'install_profile' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), ), 'primary key' => array('nid'), ); From e3e7faef3c28f946084665ddee20b600a2b26fdf Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 17:57:03 -0400 Subject: [PATCH 0032/3476] getting install profile --- devshop_projects/devshop_projects.module | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 28ea3f009..d0e493a36 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -26,6 +26,7 @@ function devshop_projects_hosting_tasks() { function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['server'] = '@server_master'; $task->context_options['project_name'] = $task->ref->title; + $task->context_options['install_profile'] = $task->ref->install_profile; $task->context_options['code_path'] = trim($task->ref->code_path, " "); if ($task->ref->git_url) { $task->context_options['git_url'] = $task->ref->git_url; @@ -418,7 +419,7 @@ function devshop_projects_validate($node, &$form) { * Node object */ function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url ' . + $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile ' . 'FROM {hosting_devshop_project} ' . 'WHERE nid = %d', $node->nid)); $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); From 29463a50321b0bededff09057017c5c14aaa5405 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 17:58:47 -0400 Subject: [PATCH 0033/3476] important TODO --- devshop_projects/devshop_projects.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index d0e493a36..4dc2eb249 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -493,6 +493,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#type' => 'item', '#value' => t("Your project's code is ready! Now you must create your sites."), ); + + // @TODO: Only show once the platforms have been verified! $node->content['install'] =array( '#type' => 'item', '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), From f477f551e3490abefd5316cd3d1b09c2318bb1fe Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 18:15:42 -0400 Subject: [PATCH 0034/3476] saving install profile works --- devshop_projects/devshop_projects.module | 55 ++++++++++++++++-------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 4dc2eb249..38f356af7 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -337,16 +337,6 @@ function devshop_projects_insert($node) { hosting_add_task($node->nid, 'devshop-create'); } -/** - * Implementation of hook_update(). - * - * As an existing node is being updated in the database, we need to do our own - * database updates. - */ -function devshop_projects_update($node) { - db_query("UPDATE {hosting_devshop_project} SET git_url = '%s', code_path = '%s' WHERE nid=%d", $node->git_url, hosting_path_normalize($node->code_path)); -} - /** * Implementation of hook_delete(). */ @@ -470,6 +460,13 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -10 ); + + $node->content['info']['install_profile'] = array( + '#type' => 'item', + '#title' => t('Install profile'), + '#value' => ($node->install_profile), + '#weight' => -8 + ); foreach($node->project_objects as $type => $objects) { $header = array($type ."s"); $rows = array(); @@ -488,13 +485,11 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } // MAIN DISPLAY + + //If there are no sites yet, we must display the install form. if (!isset($node->project_objects['site'])){ - $node->content['ready'] = array( - '#type' => 'item', - '#value' => t("Your project's code is ready! Now you must create your sites."), - ); - // @TODO: Only show once the platforms have been verified! + // Site install form $node->content['install'] =array( '#type' => 'item', '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), @@ -511,12 +506,29 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { * Form for site installation */ function devshop_projects_install_sites_form($form_state, $project_node) { + + $form = array(); + + //Bail if no platforms yet. + if (!isset($project_node->project_objects['platform'])){ + $form['note'] = array( + '#type' => 'item', + '#title' => t('Clone & Verify'), + '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + ); + return $form; + } + // Load all install profiles found on dev platform $platform_nid = key($project_node->project_objects['platform']); - $profiles = array_combine(hosting_get_profiles($platform_nid, 'short_name'), hosting_get_profiles($platform_nid)); + $form['ready'] = array( + '#type' => 'item', + '#value' => t("Your project's code is ready! Now you must create your sites."), + ); - $form = array(); + $profiles = array_combine(hosting_get_profiles($platform_nid, 'short_name'), hosting_get_profiles($platform_nid)); + $form['install_profile'] = array( '#type' => 'radios', '#title' => t('Choose your install profile'), @@ -536,7 +548,7 @@ function devshop_projects_install_sites_form($form_state, $project_node) { /** * Validates for site installation */ -function devshop_projects_install_sites_form_validate(&$form_state){ +function devshop_projects_install_sites_form_validate(&$form, &$form_state){ if (empty($form_state['values']['install_profile'])){ form_set_error('install_profile', t('You must choose an install profile')); } @@ -553,7 +565,12 @@ function devshop_projects_install_sites_form_validate(&$form_state){ * If we save the install_profile in the project context, * the task could easily create the new sites. */ -function devshop_projects_install_sites_form_submit(&$form_state){ +function devshop_projects_install_sites_form_submit(&$form, &$form_state){ + $node = node_load($form_state['values']['nid']); + + // Save installation profile to database + db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $node->nid); + // @TODO: Save a "save-project" task that saves the installation // profile to the project context. // hostmaster_add_task($form_state['values']['nid'], 'devshop-save-project', ) From bbfc6ab73a7ed44ef5dc3f5189b42a7d2eca079d Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 18:19:35 -0400 Subject: [PATCH 0035/3476] getting installation profile to show up and affect the form --- devshop_projects/devshop_projects.module | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 38f356af7..a85011e43 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -460,13 +460,14 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -10 ); - - $node->content['info']['install_profile'] = array( - '#type' => 'item', - '#title' => t('Install profile'), - '#value' => ($node->install_profile), - '#weight' => -8 - ); + if (!empty($node->install_profile)){ + $node->content['info']['install_profile'] = array( + '#type' => 'item', + '#title' => t('Install profile'), + '#value' => ($node->install_profile), + '#weight' => -8 + ); + } foreach($node->project_objects as $type => $objects) { $header = array($type ."s"); $rows = array(); @@ -518,6 +519,15 @@ function devshop_projects_install_sites_form($form_state, $project_node) { ); return $form; } + // Bail if project doesn't have sites and already has install profile + if (!isset($project_node->project_objects['site']) && isset($project_node->install_profile)){ + $form['note'] = array( + '#type' => 'item', + '#title' => t('Sites Install'), + '#value' => t('Your Live, Dev, and Test sites are being installed.'), + ); + return $form; + } // Load all install profiles found on dev platform $platform_nid = key($project_node->project_objects['platform']); From 58717d119da8df7e7511b2648c56e0ae3694eaa5 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 18:22:14 -0400 Subject: [PATCH 0036/3476] adding hostmaster task devshop-install --- devshop_projects/devshop_projects.module | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a85011e43..3f3f54750 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -14,6 +14,10 @@ function devshop_projects_hosting_tasks() { 'title' => t('Create Project'), 'description' => t('Clones the repo, and creates the platforms.'), ); + $tasks['project']['devshop-install'] = array( + 'title' => t('Install Sites'), + 'description' => t('Runs drupal installation on each of the three platforms.'), + ); return $tasks; } From 1596715d2d024d3e2cf2deb33e2fc06b581f2ac2 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 18:23:37 -0400 Subject: [PATCH 0037/3476] creating hosting task on submit of install profile form --- devshop_projects/devshop_projects.module | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 3f3f54750..7f08fc0ca 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -585,9 +585,8 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ // Save installation profile to database db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $node->nid); - // @TODO: Save a "save-project" task that saves the installation - // profile to the project context. - // hostmaster_add_task($form_state['values']['nid'], 'devshop-save-project', ) + // Create a "devshop-install" task + hosting_add_task($node->nid, 'devshop-install'); } /** From b488cae8e40edec50d7b1891b8474d6b64fde09c Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 18:31:00 -0400 Subject: [PATCH 0038/3476] fixing context options saving --- devshop_projects/devshop_projects.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 7f08fc0ca..a53985985 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -31,6 +31,7 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['server'] = '@server_master'; $task->context_options['project_name'] = $task->ref->title; $task->context_options['install_profile'] = $task->ref->install_profile; + $task->context_options['base_url'] = $task->ref->base_url; $task->context_options['code_path'] = trim($task->ref->code_path, " "); if ($task->ref->git_url) { $task->context_options['git_url'] = $task->ref->git_url; From 1be35c56c32e6498dee668c5f5457e84a2a1f88f Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sat, 18 Aug 2012 18:37:19 -0400 Subject: [PATCH 0039/3476] cleaner context saving --- devshop_projects/devshop_projects.module | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a53985985..4fe24c4f1 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -33,9 +33,7 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['install_profile'] = $task->ref->install_profile; $task->context_options['base_url'] = $task->ref->base_url; $task->context_options['code_path'] = trim($task->ref->code_path, " "); - if ($task->ref->git_url) { - $task->context_options['git_url'] = $task->ref->git_url; - } + $task->context_options['git_url'] = $task->ref->git_url; } /** From f4b20d16abefde849d7db9746378223b26987315 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 19 Aug 2012 12:08:16 -0400 Subject: [PATCH 0040/3476] Site Installation form! Choose your install profile and AWAY YOU GO --- devshop_projects/devshop_projects.module | 46 ++++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 4fe24c4f1..b5043ec31 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -1,4 +1,5 @@ project_objects['site']) && isset($project_node->install_profile)){ + if (!empty($project->install_profile) || isset($project_node->project_objects['site'])){ $form['note'] = array( '#type' => 'item', '#title' => t('Sites Install'), @@ -540,7 +541,7 @@ function devshop_projects_install_sites_form($form_state, $project_node) { '#value' => t("Your project's code is ready! Now you must create your sites."), ); - $profiles = array_combine(hosting_get_profiles($platform_nid, 'short_name'), hosting_get_profiles($platform_nid)); + $profiles = array_combine((array) hosting_get_profiles($platform_nid, 'short_name'), (array) hosting_get_profiles($platform_nid)); $form['install_profile'] = array( '#type' => 'radios', @@ -568,6 +569,7 @@ function devshop_projects_install_sites_form_validate(&$form, &$form_state){ } + /** * Submit for site installation * @@ -579,13 +581,43 @@ function devshop_projects_install_sites_form_validate(&$form, &$form_state){ * the task could easily create the new sites. */ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ - $node = node_load($form_state['values']['nid']); + global $user; + $project_node = node_load($form_state['values']['nid']); + // Save installation profile to database - db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $node->nid); - - // Create a "devshop-install" task - hosting_add_task($node->nid, 'devshop-install'); + db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); + + // Create the site nodes + foreach ($project_node->project_objects['platform'] as $nid => $env){ + $node = new stdClass(); + $node->type = 'site'; + $node->status = 1; + $node->uid = $user->uid; + $node->title = $env .'.'. $project_node->base_url; + + // Aegir properties + // @TODO: better client support + $node->client = HOSTING_DEFAULT_CLIENT; + $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); + + $node->platform = $nid; + $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $form_state['values']['install_profile'])); + + //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); + + // @TODO: Improve site language handling? + $node->site_language = !empty($user->language)? $user->language: 'en'; + + // Save the node + if ($node = node_submit($node)) { + node_save($node); + + //And save the association to the project + //INSERT INTO `tesla`.`hosting_devshop_project_object` (`project_nid`, `object_nid`, `object_type`, `env_type`) VALUES ('123', '434', 'ag', 'ag'); + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); + } + } } /** From 2a5a59f17a47c3932e418a9cccab1283a920f86c Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 19 Aug 2012 12:15:45 -0400 Subject: [PATCH 0041/3476] Loading and displaying project info --- devshop_projects/devshop_projects.module | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index b5043ec31..6875e2fd8 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -149,6 +149,8 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'platform'){ // On insert of a platform, determine project and save to hosting_devshop_project_objects + // @TODO: This is how we save the project path at the moment because + // the platform node is coming from hosting-import... if ($op == 'insert') { $path = explode('/', $node->publish_path); $environment = array_pop($path); @@ -159,21 +161,31 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Save to table db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $environment); } + } + + //Platforms and Sites + if ($node->type == 'platform' || $node->type == 'site'){ - // Load Platform + // Load Project info if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); return $data; } - // View Platform + // Display Project info if ($op == 'view'){ $project = node_load($node->project_nid); $node->content['info']['project'] = array( '#type' => 'item', '#title' => t('Project'), '#value' => l($project->title, "node/$project->nid"), - '#weight' => -10 + '#weight' => -12 + ); + $node->content['info']['env'] = array( + '#type' => 'item', + '#title' => t('Environement Type'), + '#value' => $node->project_environment, + '#weight' => -11 ); return $data; } From ba16de6732bc1335983872bda608c836e5cb6f5a Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 19 Aug 2012 12:19:43 -0400 Subject: [PATCH 0042/3476] moving git commit log to project nodes --- devshop_log/devshop_log.module | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 4c538a793..3c2b6b743 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -27,7 +27,7 @@ function _devsop_log_access_callback($nid) { } $node = node_load($nid); - if (!$node || $node->type != 'platform') { + if (!$node || $node->type != 'project') { return FALSE; } return TRUE; @@ -114,9 +114,12 @@ function devshop_git_log_settings() { */ function _devshop_log_load($nid) { - + + $project_node = node_load($nid); + $project_platforms = array_flip($project_node->project_objects['platform']); + // load the platform node - if (!($node = node_load($nid))) { + if (!($node = node_load($project_platforms['dev']))) { return "Unable to load platform node!"; } From f0743466b4e8b05fc5d674b454a424f953f890d3 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 19 Aug 2012 12:20:16 -0400 Subject: [PATCH 0043/3476] cleaning up tab title --- devshop_log/devshop_log.module | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 3c2b6b743..d60d96808 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -39,8 +39,8 @@ function _devsop_log_access_callback($nid) { function devshop_log_menu() { $items['admin/hosting/gitlog'] = array( - 'title' => 'DevShop Git Log', - 'description' => 'Configure Git Commit Log View', + 'title' => 'DevShop Commit Log', + 'description' => 'Configure Commit Log View', 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_git_log_settings'), 'type' => MENU_LOCAL_TASK, @@ -48,8 +48,8 @@ function devshop_log_menu() { ); $items['node/%/gitlog'] = array( - 'title' => 'Git Commit Log', - 'description' => 'View Git commit log entries', + 'title' => 'Commit Log', + 'description' => 'View commit log entries', 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_log_view_form', 1), 'type' => MENU_LOCAL_TASK, From b0a236429d10f6437e0ec32bfe5e98381d0d1cb1 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Mon, 20 Aug 2012 15:01:06 -0600 Subject: [PATCH 0044/3476] Removed lots of dead code --- devshop_projects/devshop_projects.module | 87 ------------------------ 1 file changed, 87 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6875e2fd8..444498959 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -98,13 +98,6 @@ function devshop_projects_menu() { 'access arguments' => array('view projects'), ); - $items['hosting/projects/detail/%'] = array( - 'title' => 'Projects Details', - 'description' => 'Display details of a projects', - 'page callback' => 'devshop_projects_project_detail', - 'access arguments' => array('view projects'), - ); - return ($items); } @@ -134,14 +127,6 @@ function devshop_projects_projects_view() { return $output; } -/** - * Create a form for building a list of projects. - */ -function devshop_projects_projects_list_form($form_state, $filter_by = NULL, $filter_value = NULL) { - - return array(); -} - /** * hook_nodeapi() */ @@ -269,70 +254,8 @@ function devshop_projects_form(&$node) { $form['git_url']['#value'] = $form['git_url']['#default_value']; } - //@TODO: Remove. I think that instead, we should let the script create the platforms. - // We have to pause at that point anyway, so the user can choose the available install profiles for their three sites. - // Lets let provision-devshop-create import all three platforms and verify, then let the users MIGRATE their existing - // sites ONTO the new platforms, IF they wish. In my opinion, migration of the old sites to our automatically built - // platforms is a better way to go. - // -// -// $sites = array(0 => 'None'); -// $r = db_query("SELECT n.nid, n.title FROM {node} AS n " . -// "LEFT JOIN {hosting_site} AS h ON n.nid = h.nid " . -// "WHERE n.type = 'site' AND h.status = 1 " . -// "ORDER BY n.title ASC"); -// -// while($sa = db_fetch_object($r)) { -// $sites[$sa->nid] = $sa->title; -// } -// -// $form['dev_site'] = array( -// '#type' => 'select', -// '#title' => t('DEV Site'), -// '#options' => $sites, -// '#description' => t('Select site that is going to be your development site.'), -// '#default_value' => $node->dev_site, -// '#weight' => 20, -// ); -// -// $form['test_site'] = array( -// '#type' => 'select', -// '#title' => t('TEST Site'), -// '#options' => $sites, -// '#description' => t('Select site that is going to be your test site.'), -// '#default_value' => $node->test_site, -// '#weight' => 21, -// ); -// -// $form['live_site'] = array( -// '#type' => 'select', -// '#title' => t('LIVE Site'), -// '#options' => $sites, -// '#description' => t('Select site that is going to be your live site.'), -// '#default_value' => $node->live_site, -// '#weight' => 22, -// ); - return $form; } -// -///** -// * Insert the sites into the sites table -// */ -//function _devshop_projects_insert_sites($node) { -// -// foreach(array('dev_site' => 'dev', -// 'test_site' => 'test', -// 'live_site' => 'live') as $k => $v) { -// if($node->$k) { -// $snode = node_load($node->$k); -// db_query("INSERT INTO {hosting_devshop_project_sites} " . -// "(nid, site_nid, platform_nid, env_type) " . -// "VALUES (%d, %d, %d, '%s')", -// $node->nid, $node->$k, $snode->platform, $v); -// } -// } -//} /** * Implementation of hook_insert(). @@ -651,13 +574,3 @@ function devshop_projects_project_status(){ return $msg; } - -function devshop_projects_project_detail() { - - $nid = arg(3); // ok, I'm cheating - if(!($node = node_load($nid)) || $node->type != 'devshop_project') { - return t('Project not found!'); - } - - return "hello"; -} From 9eb2586f3b92bf54ef7c87b589f9bf3c232dc02e Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Mon, 20 Aug 2012 17:12:31 -0600 Subject: [PATCH 0045/3476] Broke up module into smaller, more manageable files: form.inc, node.inc, and task.inc --- devshop_projects/devshop_projects.form.inc | 244 ++++++++++ devshop_projects/devshop_projects.module | 531 +-------------------- devshop_projects/devshop_projects.node.inc | 238 +++++++++ devshop_projects/devshop_projects.task.inc | 57 +++ 4 files changed, 544 insertions(+), 526 deletions(-) create mode 100644 devshop_projects/devshop_projects.form.inc create mode 100644 devshop_projects/devshop_projects.node.inc create mode 100644 devshop_projects/devshop_projects.task.inc diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc new file mode 100644 index 000000000..fe34e4d8e --- /dev/null +++ b/devshop_projects/devshop_projects.form.inc @@ -0,0 +1,244 @@ + 'textfield', + '#title' => t('Git URL'), + '#required' => TRUE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->git_url, + '#maxlength' => 255, + '#weight' => 1, + ); + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project.'), + '#size' => 40, + '#default_value' => $node->title, + '#maxlength' => 255, + ); + $form['code_path'] = array( + '#type' => 'textfield', + '#title' => t('Code path'), + '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->code_path, + '#maxlength' => 255, + '#weight' => 2, + ); + $form['base_url'] = array( + '#type' => 'textfield', + '#title' => t('Primary Domain'), + '#description' => t('The domain name all sites will be built under.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->base_url, + '#maxlength' => 255, + '#weight' => 2, + ); + + // Don't allow editing + if ($node->nid) { + $form['code_path']['#type'] = $form['title']['#type'] = $form['git_url']['#type'] = 'item'; + $form['code_path']['#value'] = $form['code_path']['#default_value']; + $form['title']['#value'] = $form['title']['#default_value']; + $form['git_url']['#value'] = $form['git_url']['#default_value']; + } + + return $form; +} + +/** + * Implementation of hook_validate(). + */ +function devshop_projects_validate($node, &$form) { + // No validation if op == Delete + if ($node->op == t('Delete')) { + return; + } + + // Full validation on when a creating a new node + $add = (arg(1) == 'add' ? TRUE : FALSE); + + // Title (project code) must not have any spaces + if(strpos($node->title, ' ') != FALSE) { + form_set_error('title', t('Project code name must not contain any white spaces.')); + } + + // The project code name must not be in the hosting_context table + if ($result = db_fetch_object(db_query("SELECT name FROM {hosting_context} WHERE name = '%s'", $node->title))) { + form_set_error('title', t('Project code name existing in hosting context table.')); + } + + // The project code name must be unique + if ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid))) { + form_set_error('title', t('Project code name is already is use by another project')); + } + + // Make sure the path is unique. + $cp = hosting_path_normalize($node->code_path); + if ($add && $result = db_fetch_object(db_query("SELECT code_path FROM {hosting_devshop_project} WHERE code_path = '%s' AND nid <> %d", $cp, $node->nid))) { + form_set_error('code_path', t('Code path is already in use by another project')); + } + + // Directory must not exist + if ($add && file_exists($cp)) { + form_set_error('code_path', t('Code path directory already exists.')); + } + + // Sites may not be used in more than one environement + if($node->dev_site != 0 && $node->dev_site == $node->test_site) { + form_set_error('dev_site', t('Site used more than once.')); + form_set_error('test_site', t('Site used more than once.')); + } + + if($node->dev_site != 0 && $node->dev_site == $node->live_site) { + form_set_error('dev_site', t('Site used more than once.')); + form_set_error('live_site', t('Site used more than once.')); + } + + if($node->test_site != 0 && $node->test_site == $node->live_site) { + form_set_error('test_site', t('Site used more than once.')); + form_set_error('live_site', t('Site used more than once.')); + } +} + +/** + * Form for site installation + */ +function devshop_projects_install_sites_form($form_state, $project_node) { + + $form = array(); + + //Bail if no platforms yet. + if (!isset($project_node->project_objects['platform'])){ + $form['note'] = array( + '#type' => 'item', + '#title' => t('Clone & Verify'), + '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + ); + return $form; + } + // Bail if project doesn't have sites and already has install profile + if (!empty($project->install_profile) || isset($project_node->project_objects['site'])){ + $form['note'] = array( + '#type' => 'item', + '#title' => t('Sites Install'), + '#value' => t('Your Live, Dev, and Test sites are being installed.'), + ); + return $form; + } + + // Load all install profiles found on dev platform + $platform_nid = key($project_node->project_objects['platform']); + + $form['ready'] = array( + '#type' => 'item', + '#value' => t("Your project's code is ready! Now you must create your sites."), + ); + + $profiles = array_combine((array) hosting_get_profiles($platform_nid, 'short_name'), (array) hosting_get_profiles($platform_nid)); + + $form['install_profile'] = array( + '#type' => 'radios', + '#title' => t('Choose your install profile'), + '#options' => $profiles, + ); + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Create dev, test, and live sites'), + ); + return $form; +} + +/** + * Validates for site installation + */ +function devshop_projects_install_sites_form_validate(&$form, &$form_state){ + if (empty($form_state['values']['install_profile'])){ + form_set_error('install_profile', t('You must choose an install profile')); + } + + +} + +/** + * Submit for site installation + * + * I believe, based on the fact that provision has to create + * the site aliases before it runs provision-install, we have + * to have a new hostmaster task here. + * + * If we save the install_profile in the project context, + * the task could easily create the new sites. + */ +function devshop_projects_install_sites_form_submit(&$form, &$form_state){ + + global $user; + $project_node = node_load($form_state['values']['nid']); + + // Save installation profile to database + db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); + + // Create the site nodes + foreach ($project_node->project_objects['platform'] as $nid => $env){ + $node = new stdClass(); + $node->type = 'site'; + $node->status = 1; + $node->uid = $user->uid; + $node->title = $env .'.'. $project_node->base_url; + + // Aegir properties + // @TODO: better client support + $node->client = HOSTING_DEFAULT_CLIENT; + $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); + + $node->platform = $nid; + $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $form_state['values']['install_profile'])); + + //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); + + // @TODO: Improve site language handling? + $node->site_language = !empty($user->language)? $user->language: 'en'; + + // Save the node + if ($node = node_submit($node)) { + node_save($node); + + //And save the association to the project + //INSERT INTO `tesla`.`hosting_devshop_project_object` (`project_nid`, `object_nid`, `object_type`, `env_type`) VALUES ('123', '434', 'ag', 'ag'); + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); + } + } +} diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 444498959..18b2f6945 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -2,60 +2,13 @@ /** * @file devshop_projects.module - * a module in the DevShop module group which enables the user to create proects and group - * sites/platforms into project groups. + * a module in the DevShop module group which enables the user to create + * proects and group sites/platforms into project groups. */ -/** - * Implementation of hook_hosting_tasks() - */ -function devshop_projects_hosting_tasks() { - $tasks = array(); - $tasks['project']['devshop-create'] = array( - 'title' => t('Create Project'), - 'description' => t('Clones the repo, and creates the platforms.'), - ); - $tasks['project']['devshop-install'] = array( - 'title' => t('Install Sites'), - 'description' => t('Runs drupal installation on each of the three platforms.'), - ); - return $tasks; -} - -/** - * Implements hook_hosting_project_context_options() - * - * This transfers data from the node to the aegir context object (the alias!) - * For project entities. - */ -function devshop_projects_hosting_project_context_options(&$task) { - $task->context_options['server'] = '@server_master'; - $task->context_options['project_name'] = $task->ref->title; - $task->context_options['install_profile'] = $task->ref->install_profile; - $task->context_options['base_url'] = $task->ref->base_url; - $task->context_options['code_path'] = trim($task->ref->code_path, " "); - $task->context_options['git_url'] = $task->ref->git_url; -} - -/** - * Implements hook_hosting_site_context_options() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_site_context_options(&$task) { - $task->context_options['project'] = $task->ref->git_url; -} - -/** - * Implements hook_hosting_site_context_options() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_platform_context_options(&$task) { - $task->context_options['project'] = $task->ref->project; -} +include_once('devshop_projects.form.inc'); +include_once('devshop_projects.node.inc'); +include_once('devshop_projects.task.inc'); /** * Implementation of hook_perm() @@ -66,26 +19,6 @@ function devshop_projects_perm() { ); } -/** - * Implementation of hook_node_info(). - */ -function devshop_projects_node_info() { - #configuration - $types["project"] = array( - "type" => 'devshop_project', - "name" => 'DevShop Project', - "module" => 'devshop_projects', - "has_title" => TRUE, - "title_label" => t('Project Codename'), - "description" => t('Create a project'), - "has_body" => 0, - "body_label" => '', - "min_word_count" => 0 - ); - - return $types; -} - /** * Implementation of hook_menu() */ @@ -101,460 +34,6 @@ function devshop_projects_menu() { return ($items); } -/* - * Callback that is invoked when the user points the brower to - * hosting/projects. It displays a nice tabulated list of projects - * and cool thing you can do with them. - */ - -function devshop_projects_projects_view() { - - $header = array('Projects', 'Sites'); - - $r = db_query("SELECT * FROM {hosting_devshop_project}"); - $rows = array(); - - while(($proj = db_fetch_object($r))) { - $node = node_load($proj->nid); - $row = array(); - $row[] = l($node->title, "node/$proj->nid"); - $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid)); - $rows[] = $row; - } - - $output = theme('table', $header, $rows, array('class' => 'hosting-table')); - $output .= l(t('Create a new project?'), 'node/add/project'); - return $output; -} - -/** - * hook_nodeapi() - */ -function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - - if ($node->type == 'platform'){ - // On insert of a platform, determine project and save to hosting_devshop_project_objects - // @TODO: This is how we save the project path at the moment because - // the platform node is coming from hosting-import... - if ($op == 'insert') { - $path = explode('/', $node->publish_path); - $environment = array_pop($path); - $project_path = implode('/', $path); - - $project = devshop_project_load_by_path($project_path); - - // Save to table - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $environment); - } - } - - //Platforms and Sites - if ($node->type == 'platform' || $node->type == 'site'){ - - // Load Project info - if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); - return $data; - } - - // Display Project info - if ($op == 'view'){ - $project = node_load($node->project_nid); - $node->content['info']['project'] = array( - '#type' => 'item', - '#title' => t('Project'), - '#value' => l($project->title, "node/$project->nid"), - '#weight' => -12 - ); - $node->content['info']['env'] = array( - '#type' => 'item', - '#title' => t('Environement Type'), - '#value' => $node->project_environment, - '#weight' => -11 - ); - return $data; - } - } -} - -/** - * Helper to load a project node by path - */ -function devshop_project_load_by_path($project_path){ - $nid = db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s"', array($project_path))); - return node_load($nid); -} - - -/** - * Implements hook_form_alter(). - */ -function devshop_projects_form_alter(&$form, &$form_state, $form_id){ - - // Removing unneccesary fieldgroups - if ($form_id == 'project_node_form'){ - unset($form['menu']); - unset($form['revision_information']); - unset($form['author']); - unset($form['options']); - } -} - -/** - * Implementation of hook_form(). - */ -function devshop_projects_form(&$node) { - - $form['git_url'] = array( - '#type' => 'textfield', - '#title' => t('Git URL'), - '#required' => TRUE, - '#description' => t(''), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $node->git_url, - '#maxlength' => 255, - '#weight' => 1, - ); - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project.'), - '#size' => 40, - '#default_value' => $node->title, - '#maxlength' => 255, - ); - $form['code_path'] = array( - '#type' => 'textfield', - '#title' => t('Code path'), - '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $node->code_path, - '#maxlength' => 255, - '#weight' => 2, - ); - $form['base_url'] = array( - '#type' => 'textfield', - '#title' => t('Primary Domain'), - '#description' => t('The domain name all sites will be built under.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $node->base_url, - '#maxlength' => 255, - '#weight' => 2, - ); - - // Don't allow editing - if ($node->nid) { - $form['code_path']['#type'] = $form['title']['#type'] = $form['git_url']['#type'] = 'item'; - $form['code_path']['#value'] = $form['code_path']['#default_value']; - $form['title']['#value'] = $form['title']['#default_value']; - $form['git_url']['#value'] = $form['git_url']['#default_value']; - } - - return $form; -} - -/** - * Implementation of hook_insert(). - */ -function devshop_projects_insert($node) { - - db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url) ". - "VALUES (%d, '%s', '%s', '%s')", - $node->nid, $node->git_url, hosting_path_normalize($node->code_path), $node->base_url); -// _devshop_projects_insert_sites($node); - - // Save hosting context - if ((!$node->old_vid)) { - hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); - } - - // Create hostmaster task - hosting_add_task($node->nid, 'devshop-create'); -} - -/** - * Implementation of hook_delete(). - */ -function devshop_projects_delete($node) { - db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); - db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); - - hosting_context_delete($node->nid); -} - -/** - * Implementation of hook_validate(). - */ -function devshop_projects_validate($node, &$form) { - // No validation if op == Delete - if ($node->op == t('Delete')) { - return; - } - - // Full validation on when a creating a new node - $add = (arg(1) == 'add' ? TRUE : FALSE); - - // Title (project code) must not have any spaces - if(strpos($node->title, ' ') != FALSE) { - form_set_error('title', t('Project code name must not contain any white spaces.')); - } - - // The project code name must not be in the hosting_context table - if ($result = db_fetch_object(db_query("SELECT name FROM {hosting_context} WHERE name = '%s'", $node->title))) { - form_set_error('title', t('Project code name existing in hosting context table.')); - } - - // The project code name must be unique - if ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid))) { - form_set_error('title', t('Project code name is already is use by another project')); - } - - // Make sure the path is unique. - $cp = hosting_path_normalize($node->code_path); - if ($add && $result = db_fetch_object(db_query("SELECT code_path FROM {hosting_devshop_project} WHERE code_path = '%s' AND nid <> %d", $cp, $node->nid))) { - form_set_error('code_path', t('Code path is already in use by another project')); - } - - // Directory must not exist - if ($add && file_exists($cp)) { - form_set_error('code_path', t('Code path directory already exists.')); - } - - // Sites may not be used in more than one environement - if($node->dev_site != 0 && $node->dev_site == $node->test_site) { - form_set_error('dev_site', t('Site used more than once.')); - form_set_error('test_site', t('Site used more than once.')); - } - - if($node->dev_site != 0 && $node->dev_site == $node->live_site) { - form_set_error('dev_site', t('Site used more than once.')); - form_set_error('live_site', t('Site used more than once.')); - } - - if($node->test_site != 0 && $node->test_site == $node->live_site) { - form_set_error('test_site', t('Site used more than once.')); - form_set_error('live_site', t('Site used more than once.')); - } -} - -/** - * Implementation of hook_load(). - * - * @param node - * Node object - */ -function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile ' . - 'FROM {hosting_devshop_project} ' . - 'WHERE nid = %d', $node->nid)); - $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); - if (is_array($hosting_name) && is_array($additions)) { - $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; - $additions += $hosting_name; - } - - $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); - - $objects = array(); - while($project_object = db_fetch_object($query)) { - $objects[$project_object->object_type][$project_object->object_nid] = $project_object->env_type; - } - $additions['project_objects'] = $objects; - return $additions; -} - -/** - * Implementation of hook_view(). - */ -function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { - modalframe_parent_js(); - - $node->content['info'] = array( - '#prefix' => '
', - '#suffix' => '
' - ); - - $node->content['info']['git_url'] = array( - '#type' => 'item', - '#title' => t('Git URL'), - '#value' => $node->git_url, - '#weight' => -10 - ); - - $node->content['info']['code_path'] = array( - '#type' => 'item', - '#title' => t('Code path'), - '#value' => filter_xss($node->code_path), - '#weight' => -8 - ); - $url = 'http://' . $node->base_url; - $node->content['info']['base_url'] = array( - '#type' => 'item', - '#title' => t('Base URL'), - '#value' => l($url, $url), - '#weight' => -10 - ); - - if (!empty($node->install_profile)){ - $node->content['info']['install_profile'] = array( - '#type' => 'item', - '#title' => t('Install profile'), - '#value' => ($node->install_profile), - '#weight' => -8 - ); - } - foreach($node->project_objects as $type => $objects) { - $header = array($type ."s"); - $rows = array(); - foreach($objects as $nid => $object) { - $object = node_load($nid); - $row = array(l($object->title, "node/$object->nid")); - $rows[] = $row; - - } - $table = theme('table', $header, $rows); - - $node->content['info'][$type] = array( - '#type' => 'item', - '#value' => $table, - ); - } - - // MAIN DISPLAY - - //If there are no sites yet, we must display the install form. - if (!isset($node->project_objects['site'])){ - - // Site install form - $node->content['install'] =array( - '#type' => 'item', - '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), - ); - - - } - - - return $node; -} - -/** - * Form for site installation - */ -function devshop_projects_install_sites_form($form_state, $project_node) { - - $form = array(); - - //Bail if no platforms yet. - if (!isset($project_node->project_objects['platform'])){ - $form['note'] = array( - '#type' => 'item', - '#title' => t('Clone & Verify'), - '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), - ); - return $form; - } - // Bail if project doesn't have sites and already has install profile - if (!empty($project->install_profile) || isset($project_node->project_objects['site'])){ - $form['note'] = array( - '#type' => 'item', - '#title' => t('Sites Install'), - '#value' => t('Your Live, Dev, and Test sites are being installed.'), - ); - return $form; - } - - // Load all install profiles found on dev platform - $platform_nid = key($project_node->project_objects['platform']); - - $form['ready'] = array( - '#type' => 'item', - '#value' => t("Your project's code is ready! Now you must create your sites."), - ); - - $profiles = array_combine((array) hosting_get_profiles($platform_nid, 'short_name'), (array) hosting_get_profiles($platform_nid)); - - $form['install_profile'] = array( - '#type' => 'radios', - '#title' => t('Choose your install profile'), - '#options' => $profiles, - ); - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Create dev, test, and live sites'), - ); - return $form; -} - -/** - * Validates for site installation - */ -function devshop_projects_install_sites_form_validate(&$form, &$form_state){ - if (empty($form_state['values']['install_profile'])){ - form_set_error('install_profile', t('You must choose an install profile')); - } - - -} - -/** - * Submit for site installation - * - * I believe, based on the fact that provision has to create - * the site aliases before it runs provision-install, we have - * to have a new hostmaster task here. - * - * If we save the install_profile in the project context, - * the task could easily create the new sites. - */ -function devshop_projects_install_sites_form_submit(&$form, &$form_state){ - - global $user; - $project_node = node_load($form_state['values']['nid']); - - // Save installation profile to database - db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); - - // Create the site nodes - foreach ($project_node->project_objects['platform'] as $nid => $env){ - $node = new stdClass(); - $node->type = 'site'; - $node->status = 1; - $node->uid = $user->uid; - $node->title = $env .'.'. $project_node->base_url; - - // Aegir properties - // @TODO: better client support - $node->client = HOSTING_DEFAULT_CLIENT; - $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); - - $node->platform = $nid; - $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $form_state['values']['install_profile'])); - - //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); - - // @TODO: Improve site language handling? - $node->site_language = !empty($user->language)? $user->language: 'en'; - - // Save the node - if ($node = node_submit($node)) { - node_save($node); - - //And save the association to the project - //INSERT INTO `tesla`.`hosting_devshop_project_object` (`project_nid`, `object_nid`, `object_type`, `env_type`) VALUES ('123', '434', 'ag', 'ag'); - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); - } - } -} - /** * Status display */ diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc new file mode 100644 index 000000000..fc17f707e --- /dev/null +++ b/devshop_projects/devshop_projects.node.inc @@ -0,0 +1,238 @@ + 'devshop_project', + "name" => 'DevShop Project', + "module" => 'devshop_projects', + "has_title" => TRUE, + "title_label" => t('Project Codename'), + "description" => t('Create a project'), + "has_body" => 0, + "body_label" => '', + "min_word_count" => 0 + ); + + return $types; +} + + +/** + * hook_nodeapi() + */ +function devshop_projects_nodeapi(&$node, $op, $a3 = null) { + + if ($node->type == 'platform'){ + // On insert of a platform, determine project and save to hosting_devshop_project_objects + // @TODO: This is how we save the project path at the moment because + // the platform node is coming from hosting-import... + if ($op == 'insert') { + $path = explode('/', $node->publish_path); + $environment = array_pop($path); + $project_path = implode('/', $path); + + $project = devshop_project_load_by_path($project_path); + + // Save to table + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $environment); + } + } + + //Platforms and Sites + if ($node->type == 'platform' || $node->type == 'site'){ + + // Load Project info + if ($op == 'load'){ + $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); + return $data; + } + + // Display Project info + if ($op == 'view'){ + $project = node_load($node->project_nid); + $node->content['info']['project'] = array( + '#type' => 'item', + '#title' => t('Project'), + '#value' => l($project->title, "node/$project->nid"), + '#weight' => -12 + ); + $node->content['info']['env'] = array( + '#type' => 'item', + '#title' => t('Environement Type'), + '#value' => $node->project_environment, + '#weight' => -11 + ); + return $data; + } + } +} + +/** + * Helper to load a project node by path + */ +function devshop_project_load_by_path($project_path){ + $nid = db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s"', array($project_path))); + return node_load($nid); +} + +/** + * Implementation of hook_insert(). + */ +function devshop_projects_insert($node) { + + db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url) ". + "VALUES (%d, '%s', '%s', '%s')", + $node->nid, $node->git_url, hosting_path_normalize($node->code_path), $node->base_url); +// _devshop_projects_insert_sites($node); + + // Save hosting context + if ((!$node->old_vid)) { + hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); + } + + // Create hostmaster task + hosting_add_task($node->nid, 'devshop-create'); +} + +/** + * Implementation of hook_delete(). + */ +function devshop_projects_delete($node) { + db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); + db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); + + hosting_context_delete($node->nid); +} + +/** + * Implementation of hook_load(). + * + * @param node + * Node object + */ +function devshop_projects_load($node) { + $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile ' . + 'FROM {hosting_devshop_project} ' . + 'WHERE nid = %d', $node->nid)); + $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); + if (is_array($hosting_name) && is_array($additions)) { + $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; + $additions += $hosting_name; + } + + $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); + + $objects = array(); + while($project_object = db_fetch_object($query)) { + $objects[$project_object->object_type][$project_object->object_nid] = $project_object->env_type; + } + $additions['project_objects'] = $objects; + return $additions; +} + +/** + * Implementation of hook_view(). + */ +function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { + modalframe_parent_js(); + + $node->content['info'] = array( + '#prefix' => '
', + '#suffix' => '
' + ); + + $node->content['info']['git_url'] = array( + '#type' => 'item', + '#title' => t('Git URL'), + '#value' => $node->git_url, + '#weight' => -10 + ); + + $node->content['info']['code_path'] = array( + '#type' => 'item', + '#title' => t('Code path'), + '#value' => filter_xss($node->code_path), + '#weight' => -8 + ); + $url = 'http://' . $node->base_url; + $node->content['info']['base_url'] = array( + '#type' => 'item', + '#title' => t('Base URL'), + '#value' => l($url, $url), + '#weight' => -10 + ); + + if (!empty($node->install_profile)){ + $node->content['info']['install_profile'] = array( + '#type' => 'item', + '#title' => t('Install profile'), + '#value' => ($node->install_profile), + '#weight' => -8 + ); + } + foreach($node->project_objects as $type => $objects) { + $header = array($type ."s"); + $rows = array(); + foreach($objects as $nid => $object) { + $object = node_load($nid); + $row = array(l($object->title, "node/$object->nid")); + $rows[] = $row; + + } + $table = theme('table', $header, $rows); + + $node->content['info'][$type] = array( + '#type' => 'item', + '#value' => $table, + ); + } + + // MAIN DISPLAY + + //If there are no sites yet, we must display the install form. + if (!isset($node->project_objects['site'])){ + + // Site install form + $node->content['install'] =array( + '#type' => 'item', + '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), + ); + + } + + return $node; +} + +/* + * Callback that is invoked when the user points the brower to + * hosting/projects. It displays a nice tabulated list of projects + * and cool thing you can do with them. + */ + +function devshop_projects_projects_view() { + + $header = array('Projects', 'Sites'); + + $r = db_query("SELECT * FROM {hosting_devshop_project}"); + $rows = array(); + + while(($proj = db_fetch_object($r))) { + $node = node_load($proj->nid); + $row = array(); + $row[] = l($node->title, "node/$proj->nid"); + $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid)); + $rows[] = $row; + } + + $output = theme('table', $header, $rows, array('class' => 'hosting-table')); + $output .= l(t('Create a new project?'), 'node/add/project'); + return $output; +} diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc new file mode 100644 index 000000000..ea8ccfe62 --- /dev/null +++ b/devshop_projects/devshop_projects.task.inc @@ -0,0 +1,57 @@ + t('Create Project'), + 'description' => t('Clones the repo, and creates the platforms.'), + ); + $tasks['project']['devshop-install'] = array( + 'title' => t('Install Sites'), + 'description' => t('Runs drupal installation on each of the three platforms.'), + ); + return $tasks; +} + +/** + * Implements hook_hosting_project_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For project entities. + */ +function devshop_projects_hosting_project_context_options(&$task) { + $task->context_options['server'] = '@server_master'; + $task->context_options['project_name'] = $task->ref->title; + $task->context_options['install_profile'] = $task->ref->install_profile; + $task->context_options['base_url'] = $task->ref->base_url; + $task->context_options['code_path'] = trim($task->ref->code_path, " "); + $task->context_options['git_url'] = $task->ref->git_url; +} + +/** + * Implements hook_hosting_site_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For site entities. + */ +function devshop_projects_hosting_site_context_options(&$task) { + $task->context_options['project'] = $task->ref->git_url; +} + +/** + * Implements hook_hosting_site_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For site entities. + */ +function devshop_projects_hosting_platform_context_options(&$task) { + $task->context_options['project'] = $task->ref->project; +} + From cb5bb0a0da130fc538e61d930996eab12847d77a Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Tue, 28 Aug 2012 18:24:47 -0400 Subject: [PATCH 0046/3476] adding provision_save option to the devshop-create task --- devshop_projects/devshop_projects.task.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index ea8ccfe62..7b28e8ba1 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -12,6 +12,7 @@ function devshop_projects_hosting_tasks() { $tasks['project']['devshop-create'] = array( 'title' => t('Create Project'), 'description' => t('Clones the repo, and creates the platforms.'), + 'provision_save' => TRUE, ); $tasks['project']['devshop-install'] = array( 'title' => t('Install Sites'), From 411b2eb0a96a57ffd6195d10d708bb045194dba8 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Tue, 28 Aug 2012 18:59:24 -0400 Subject: [PATCH 0047/3476] adding required patches to hostmaster --- 1513678-hosting-task-names-1.9.patch | 13 +++ 1760962-hosting-task-provision-save-1.9.patch | 98 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 1513678-hosting-task-names-1.9.patch create mode 100644 1760962-hosting-task-provision-save-1.9.patch diff --git a/1513678-hosting-task-names-1.9.patch b/1513678-hosting-task-names-1.9.patch new file mode 100644 index 000000000..587800543 --- /dev/null +++ b/1513678-hosting-task-names-1.9.patch @@ -0,0 +1,13 @@ +diff --git a/modules/hosting/task/hosting_task.module b/modules/hosting/task/hosting_task.module +index f698fe3..b037ab5 100644 +--- a/modules/hosting/task/hosting_task.module ++++ b/modules/hosting/task/hosting_task.module +@@ -388,7 +388,7 @@ function hosting_task_confirm_form($form_state, $node, $task) { + $form['nid'] = array('#type' => 'value', '#value' => $node->nid); + $form['task'] = array('#type' => 'value', '#value' => $task); + $form['parameters'] = array('#tree' => TRUE); +- $func = 'hosting_task_' . $task . '_form'; ++ $func = 'hosting_task_' . str_replace('-', '_', $task) . '_form'; + if (function_exists($func)) { + $form['parameters'] += $func($node); + } diff --git a/1760962-hosting-task-provision-save-1.9.patch b/1760962-hosting-task-provision-save-1.9.patch new file mode 100644 index 000000000..8e470a1ed --- /dev/null +++ b/1760962-hosting-task-provision-save-1.9.patch @@ -0,0 +1,98 @@ +diff --git a/modules/hosting/platform/hosting_platform.module b/modules/hosting/platform/hosting_platform.module +index 91d4319..a4cb8ed 100644 +--- a/modules/hosting/platform/hosting_platform.module ++++ b/modules/hosting/platform/hosting_platform.module +@@ -63,6 +63,7 @@ function hosting_platform_hosting_tasks() { + 'title' => t('Verify'), + 'description' => t('Verify that the platform is correctly installed and working.'), + 'weight' => 10, ++ 'provision_save' => TRUE, + ); + $tasks['platform']['delete'] = array( + 'title' => t('Delete'), +diff --git a/modules/hosting/server/hosting_server.module b/modules/hosting/server/hosting_server.module +index 5edddf7..b879054 100644 +--- a/modules/hosting/server/hosting_server.module ++++ b/modules/hosting/server/hosting_server.module +@@ -53,6 +53,7 @@ function hosting_server_hosting_tasks() { + 'title' => t('Verify'), + 'description' => t('Verify that the server is correctly installed and working.'), + 'weight' => 10, ++ 'provision_save' => TRUE, + ); + + /** +diff --git a/modules/hosting/site/hosting_site.module b/modules/hosting/site/hosting_site.module +index bd77390..c30b8af 100644 +--- a/modules/hosting/site/hosting_site.module ++++ b/modules/hosting/site/hosting_site.module +@@ -208,6 +208,7 @@ function hosting_site_hosting_tasks() { + $tasks['site']['verify'] = array( + 'title' => t('Verify'), + 'description' => t('Confirm that the site has been correctly installed and regenerate all configuration files to match the hosting front end.'), ++ 'provision_save' => TRUE, + ); + + $tasks['site']['disable'] = array( +@@ -243,14 +244,16 @@ function hosting_site_hosting_tasks() { + $tasks['site']['install'] = array( + 'title' => t('Install'), + 'description' => t('Install a site'), +- 'hidden' => TRUE ++ 'hidden' => TRUE, ++ 'provision_save' => TRUE, + ); + + $tasks['site']['import'] = array( + 'title' => t('Import'), + 'description' => t('Import an existing site into Aegir'), +- 'hidden' => TRUE +- ); ++ 'hidden' => TRUE, ++ 'provision_save' => TRUE, ++); + + return $tasks; + } +diff --git a/modules/hosting/task.hosting.inc b/modules/hosting/task.hosting.inc +index 31dd56f..0d0fa5c 100644 +--- a/modules/hosting/task.hosting.inc ++++ b/modules/hosting/task.hosting.inc +@@ -72,6 +72,14 @@ function drush_hosting_task_validate($id, $type = null) { + else { + drush_set_error('HOSTING_INVALID_TASK', t("This task is not valid")); + } ++ ++ // Load Task Info ++ $tasks_info = hosting_available_tasks($task->ref->type); ++ ++ // Find task type and pass through if it needs provision_save ++ if (isset($tasks_info[$task->task_type])){ ++ $task->task_info = $tasks_info[$task->task_type]; ++ } + } + + /** +@@ -90,9 +98,8 @@ function drush_hosting_task() { + + // Make sure argument order is correct + ksort($task->args); +- + // On install/verify, save the named context +- if ($task->task_type === 'install' || $task->task_type === 'verify' || $task->task_type === 'import') { ++ if (!empty($task->task_info['provision_save'])) { + // XXX: we copy module_invoke_all() here because it doesn't pass by + // reference and it breaks under PHP 5.3 + $hook = 'hosting_' . $task->ref->type . '_context_options'; +diff --git a/modules/hosting/task/hosting_task.module b/modules/hosting/task/hosting_task.module +index f698fe3..ca3c863 100644 +--- a/modules/hosting/task/hosting_task.module ++++ b/modules/hosting/task/hosting_task.module +@@ -1149,7 +1149,6 @@ function hosting_task_fetch_tasks($rid) { + + $return[$type] = $task; + } +- + return $return; + } + From df7faa95ab10b7c660903ccc0142fb5efff745c9 Mon Sep 17 00:00:00 2001 From: Aegir user Date: Thu, 30 Aug 2012 17:13:42 +0200 Subject: [PATCH 0048/3476] by jacintocapote: Change pull to project instead of platform --- devshop_pull/devshop_pull.inc | 24 ++++++++----- devshop_pull/devshop_pull.info | 4 +-- devshop_pull/devshop_pull.install | 4 +-- devshop_pull/devshop_pull.module | 51 +++++++++++++++------------ devshop_pull/hosting.feature.pull.inc | 2 +- 5 files changed, 49 insertions(+), 36 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 3508988dc..383950989 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -7,7 +7,7 @@ * * @TODO: Save "Last Pull" and "Last Pull Status" when git pull task is complete. */ -function devshop_pull_callback($platform, $hash) { +function devshop_pull_callback($project, $hash) { // Get the access from list $acl = explode("\n", trim(variable_get('devshop_pull_ip_acl', ''))); @@ -30,20 +30,21 @@ function devshop_pull_callback($platform, $hash) { print "$message
"; - if (strlen($platform) < 1 || strlen($hash) != 32) { + if (strlen($project) < 1 || strlen($hash) != 32) { $message = "Invalid/missing parameters in URL!"; watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); print "$message
"; return; } - // Based on the platform name, get the node ID for the platform node + // Based on the project name, get the node ID for the project node. + // Is need remove the prefix for find the nid. $pnid = db_result(db_query( - "SELECT nid FROM {hosting_context} WHERE name = '%s'", $platform)); + "SELECT nid FROM {hosting_context} WHERE name = '%s'", str_replace('project_', '', $project))); // Load the entire node if (!$pnode = node_load($pnid)) { - $message = "Unable to load platform node!"; + $message = "Unable to load project node!"; watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); print "$message
"; return; @@ -59,15 +60,20 @@ function devshop_pull_callback($platform, $hash) { // Make sure the platform has pull callback enabled if ($pnode->pull_method != DEVSHOP_PULL_CALLBACK){ - $message = "Platform is NOT configured to use Pull Code URL callback!"; + $message = "Project is NOT configured to use Pull Code URL callback!"; watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); print "$message
"; return; } - // Iterate through all of the sites that use this platform + //Search dev platform of this project + $platforms = $pnode->project_objects['platform']; + + $platform = array_search('dev', $platforms); + + // Iterate through all of the sites that use platform dev $r = db_query("SELECT nid FROM {hosting_site} " . - "WHERE platform = %d AND status = 1", $pnid); + "WHERE platform = %d AND status = 1", $platform); while ($site = db_fetch_object($r)) { $message = "Queuing a pull task for node id $site->nid"; @@ -78,7 +84,7 @@ function devshop_pull_callback($platform, $hash) { } /** - * Create the full URL that is displayed in the platform node view + * Create the full URL that is displayed in the project node view * and given to the GitHub WebHook to invoke a pull after a commit. */ function _devshop_pull_callback_url($node) { diff --git a/devshop_pull/devshop_pull.info b/devshop_pull/devshop_pull.info index 473238252..db0e4c677 100644 --- a/devshop_pull/devshop_pull.info +++ b/devshop_pull/devshop_pull.info @@ -1,4 +1,4 @@ name = DevShop Pull -description = Provides a variety of methods to trigger a Pull Code task on DevShop platforms. +description = Provides a variety of methods to trigger a Pull Code task on DevShop projects. core = 6.x -package = DevShop \ No newline at end of file +package = DevShop diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index 7fd9641df..bd0546b33 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -10,7 +10,7 @@ function devshop_pull_schema() { $schema['hosting_devshop_pull'] = array( 'fields' => array( - 'platform_nid' => array( + 'project_nid' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, @@ -36,7 +36,7 @@ function devshop_pull_schema() { 'default' => 0, ), ), - 'primary key' => array('platform_nid'), + 'primary key' => array('project_nid'), ); return $schema; diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 4819b932c..05a6c3ef0 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -47,11 +47,11 @@ function devshop_pull_hosting_queues() { $items['pull'] = array( 'type' => 'batch', 'name' => t('Pull queue'), - 'description' => t('Run git pull on platforms configured to do so.'), + 'description' => t('Run git pull on projects configured to do so.'), 'total_items' => devshop_pull_get_sites(10), 'frequency' => strtotime("1 minute", 0), - 'singular' => t('platform'), - 'plural' => t('platforms'), + 'singular' => t('project'), + 'plural' => t('projects'), ); return $items; } @@ -60,7 +60,7 @@ function devshop_pull_hosting_queues() { * Implements hook_form_alter(). */ function devshop_pull_form_alter(&$form, &$form_state, $form_id){ - if ($form_id == 'platform_node_form'){ + if ($form_id == 'project_node_form'){ // Get node $node = $form['#node']; @@ -87,7 +87,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id){ $form['pull_reset'] = array( '#title' => 'Hard Reset on Pull', '#type' => 'checkbox', - '#description' => t('Reset any changes to platform files. WARNING: Any uncommitted changes to the platform files will be discarded.'), + '#description' => t('Reset any changes to project files. WARNING: Any uncommitted changes to the project files will be discarded.'), '#default_value' => $node->pull_reset, ); } @@ -97,15 +97,15 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id){ * Implements hook_nodeapi() */ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { - if ($node->type == 'platform' || $node->type == 'site') { + if ($node->type == 'project' || $node->type == 'site') { switch ($op) { case 'view': if (!$a3) { //!teaser - if($node->type == 'platform') { + if($node->type == 'project') { $pnode = $node; } else { - $pnode = node_load($node->platform); + $pnode = node_load($node->project_nid); } // @TODO: PULL QUEUE must be enabled first! Show a notice to the user if Pull Queue @@ -150,9 +150,9 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { } break; case 'load': - if ($node->type == 'platform') { - $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull} WHERE platform_nid = %d', $node->nid)); - if (!empty($data->platform_nid)){ + if ($node->type == 'project') { + $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull} WHERE project_nid = %d', $node->nid)); + if (!empty($data->project_nid)){ $node->pull_method = $data->pull_method; $node->pull_reset = $data->pull_reset; $node->last_pull = $data->last_pull; @@ -162,9 +162,9 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { break; case 'insert': case 'update': - if ($node->type == 'platform') { - db_query('DELETE FROM {hosting_devshop_pull} WHERE platform_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull} (platform_nid, pull_method, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_method, $node->pull_reset); + if ($node->type == 'project') { + db_query('DELETE FROM {hosting_devshop_pull} WHERE project_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull} (project_nid, pull_method, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_method, $node->pull_reset); } break; } @@ -182,7 +182,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { * @TODO Convert to check pull queue sites. */ function devshop_pull_get_sites($limit = 5) { - $result = db_query("SELECT COUNT(n.nid) FROM {node} n LEFT JOIN {hosting_platform} s ON n.nid=s.nid LEFT JOIN {hosting_devshop_pull} d ON n.nid = d.platform_nid WHERE s.status = %d AND d.pull_method = %d ORDER BY d.last_pull ASC, n.nid ASC", array(HOSTING_PLATFORM_ENABLED, DEVSHOP_PULL_QUEUE)); + $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); return db_result($result); } @@ -192,15 +192,16 @@ function devshop_pull_get_sites($limit = 5) { */ function hosting_pull_queue($count) { - $result = db_query("SELECT d.* FROM {node} n LEFT JOIN {hosting_platform} s ON n.nid=s.nid LEFT JOIN {hosting_devshop_pull} d ON n.nid = d.platform_nid WHERE s.status = %d AND d.pull_method = %d ORDER BY d.last_pull ASC, n.nid ASC", array(HOSTING_PLATFORM_ENABLED, DEVSHOP_PULL_QUEUE)); + $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); - while ($platform = db_fetch_object($result)) { + while ($project = db_fetch_object($result)) { - devshop_pull_task($platform->platform_nid); + devshop_pull_task($project->project_nid); + module_invoke_all('devshop_pull', $project); //@TODO: Set last_pull_status // watchdog('devshop', 'Pull FAILED on platform: @alias', array('@alias' => $platform_name)); - // db_query('UPDATE {hosting_devshop_pull} SET last_pull = %d, last_pull_status = %d WHERE platform_nid = %d', time(), 0, $platform->platform_nid); + // db_query('UPDATE {hosting_devshop_pull} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $platform->project_nid); } } @@ -216,9 +217,15 @@ function devshop_pull_task($nid){ $nids = array(); if ($node->type == 'site'){ $nids[] = $node->nid; - } else { + } + else { + //Search dev platform of this project + $platforms = $node->project_objects['platform']; + + $platform = array_search('dev', $platforms); + $r = db_query("SELECT nid FROM {hosting_site} " . - "WHERE platform = %d AND status = 1", $node->nid); + "WHERE platform = %d AND status = 1", $platform); while ($site = db_fetch_object($r)) { $nids[] = $site->nid; } @@ -235,4 +242,4 @@ function devshop_pull_task($nid){ foreach ($nids as $nid){ hosting_add_task($nid, 'devshop-pull', $args); } -} \ No newline at end of file +} diff --git a/devshop_pull/hosting.feature.pull.inc b/devshop_pull/hosting.feature.pull.inc index 0c4abf484..ddb63f83c 100644 --- a/devshop_pull/hosting.feature.pull.inc +++ b/devshop_pull/hosting.feature.pull.inc @@ -7,7 +7,7 @@ function devshop_pull_hosting_feature() { // From hosting_example_hosting_feature(). $features['pull'] = array( 'title' => t('DevShop Pull Queue'), - 'description' => t('Provides a queue for pulling a Git powered platform.'), + 'description' => t('Provides a queue for pulling a Git powered project.'), 'status' => HOSTING_FEATURE_ENABLED, ); return $features; From 1c9334def249a922a2c413e9895da1dcfe78fd85 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Tue, 4 Sep 2012 15:38:29 -0600 Subject: [PATCH 0049/3476] If a site is in a project, the only allow other sites in that project to be the source for a Sync Content task. --- devshop_projects/devshop_projects.form.inc | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index fe34e4d8e..5190c01b2 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -16,6 +16,41 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ unset($form['author']); unset($form['options']); } + + if ($form_id == 'hosting_task_confirm_form'){ + // Get nid + $nid = $form['nid']['#value']; + + // See if this site is already in a project + $query = db_query("SELECT project_nid " . + "FROM {hosting_devshop_project_object} " . + "WHERE object_nid = %d " . + "AND object_type = 'site'", $nid); + + if($proj = db_fetch_object($query)) { + + // Only make sites in this project a source for content + $query = db_query("SELECT object_nid " . + "FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d " . + "AND object_type = 'site' " . + "AND object_nid <> %d", $proj->project_nid, $nid); + + $options = array(); + while($s = db_fetch_object($query)) { + $node = node_load($s->object_nid); + $options['@' . $node->title] = '@' . $node->title; + } + + $form['parameters']['source'] = array( + '#type' => 'radios', + '#title' => t('Source site alias'), + '#options' => $options, + '#description' => t('Select site alias to sync from.'), + ); + } + } + } /** From d0865c655e9389d0ea8a33358a37e419e967c746 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 7 Sep 2012 17:17:54 -0600 Subject: [PATCH 0050/3476] Platform create form done, much left to do! --- devshop_projects/devshop_projects.form.inc | 121 +++++++++++++++++++++ devshop_projects/devshop_projects.module | 9 ++ devshop_projects/devshop_projects.node.inc | 11 ++ 3 files changed, 141 insertions(+) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 5190c01b2..d302426df 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -277,3 +277,124 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ } } } + +/** + * Form for platform creation + */ +function devshop_projects_platform_create_form($form_state, $project_nid) { + + $project_node = node_load($project_nid); + + $form = array(); + + //Bail if no platforms yet. + if (!isset($project_node->project_objects['platform'])){ + $form['note'] = array( + '#type' => 'item', + '#title' => t('Clone & Verify'), + '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + ); + return $form; + } + // ZZZ + $form['platform_name'] = array( + '#type' => 'textfield', + '#title' => t('Platform Name'), + '#required' => TRUE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => '', + '#maxlength' => 255, + ); + $form['git_url'] = array( + '#type' => 'textfield', + '#title' => t('Git URL'), + '#required' => FALSE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => '', + '#maxlength' => 255, + ); + $form['branch'] = array( + '#type' => 'textfield', + '#title' => t('Branch'), + '#required' => TRUE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => 'master', + '#maxlength' => 255, + ); + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Create platform'), + ); + return $form; +} + +/** + * Validates for platform create + */ +function devshop_projects_platform_create_form_validate(&$form, &$form_state){ + if (empty($form_state['values']['platform_name'])){ + form_set_error('install_profile', t('You must include a platform name')); + } + + +} + +/** + * Submit for site installation + * + * I believe, based on the fact that provision has to create + * the site aliases before it runs provision-install, we have + * to have a new hostmaster task here. + * + * If we save the install_profile in the project context, + * the task could easily create the new sites. + */ +function devshop_projects_platform_create_form_submit(&$form, &$form_state){ + + global $user; + $project_node = node_load($form_state['values']['nid']); + + // Save installation profile to database + db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); + + // Create the site nodes + foreach ($project_node->project_objects['platform'] as $nid => $env){ + $node = new stdClass(); + $node->type = 'site'; + $node->status = 1; + $node->uid = $user->uid; + $node->title = $env .'.'. $project_node->base_url; + + // Aegir properties + // @TODO: better client support + $node->client = HOSTING_DEFAULT_CLIENT; + $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); + + $node->platform = $nid; + $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $form_state['values']['install_profile'])); + + //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); + + // @TODO: Improve site language handling? + $node->site_language = !empty($user->language)? $user->language: 'en'; + + // Save the node + if ($node = node_submit($node)) { + node_save($node); + + //And save the association to the project + //INSERT INTO `tesla`.`hosting_devshop_project_object` (`project_nid`, `object_nid`, `object_type`, `env_type`) VALUES ('123', '434', 'ag', 'ag'); + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); + } + } +} diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 18b2f6945..79cace6de 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -31,6 +31,15 @@ function devshop_projects_menu() { 'access arguments' => array('view projects'), ); + $items['hosting/projects/platform/create/%'] = array( + 'title' => 'Create and add a platform', + 'description' => 'Create and add a platform to an existing project', + //'page callback' => 'devshop_projects_platform_create', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('devshop_projects_platform_create_form', 4), + 'access arguments' => array('view projects'), + ); + return ($items); } diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index fc17f707e..e2e42611d 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -178,6 +178,16 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -8 ); } + + if (isset($node->project_objects['platform'])){ + $node->content['info']['add_platform'] = array( + '#type' => 'item', + '#value' => l(t('Create and add additional platforms'), + "hosting/projects/platform/create/$node->nid"), + ); + } + + foreach($node->project_objects as $type => $objects) { $header = array($type ."s"); $rows = array(); @@ -193,6 +203,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#type' => 'item', '#value' => $table, ); + } // MAIN DISPLAY From be931f7934930164de47604ee9b33ffa8fdfa2c9 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 11:00:36 -0400 Subject: [PATCH 0051/3476] adding Fastangels patch for hosting hook invokations --- 1778400-hosting-tasks-names-hooks-1.9.patch | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 1778400-hosting-tasks-names-hooks-1.9.patch diff --git a/1778400-hosting-tasks-names-hooks-1.9.patch b/1778400-hosting-tasks-names-hooks-1.9.patch new file mode 100644 index 000000000..3eb543088 --- /dev/null +++ b/1778400-hosting-tasks-names-hooks-1.9.patch @@ -0,0 +1,20 @@ +diff --git a/modules/hosting/task.hosting.inc b/modules/hosting/task.hosting.inc +index 31dd56f..6c95989 100644 +--- a/modules/hosting/task.hosting.inc ++++ b/modules/hosting/task.hosting.inc +@@ -128,7 +128,7 @@ function drush_hosting_task() { + */ + function drush_hosting_hosting_task_rollback() { + $task =& drush_get_context('HOSTING_TASK'); +- module_invoke_all(sprintf("hosting_%s_task_rollback", $task->task_type), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); ++ module_invoke_all(sprintf("hosting_%s_task_rollback", str_replace('-', '_', $task->task_type))), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); + } + + /** +@@ -139,5 +139,5 @@ function drush_hosting_hosting_task_rollback() { + function drush_hosting_post_hosting_task($task) { + $task =& drush_get_context('HOSTING_TASK'); + +- module_invoke_all(sprintf("post_hosting_%s_task", $task->task_type), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); ++ module_invoke_all(sprintf("post_hosting_%s_task", str_replace('-', '_', $task->task_type)), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); + } From ec0a2f5fb771dfa8a81d21eea6aadb867f4079a5 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 16:11:36 -0400 Subject: [PATCH 0052/3476] - Better logic and output of installation form - Adding project tasks to node view. - Adding a copy/paste replacement for hosting_task_table for projects --- devshop_projects/devshop_projects.form.inc | 60 +++++++++++++++++----- devshop_projects/devshop_projects.node.inc | 38 +++++++++----- devshop_projects/devshop_projects.task.inc | 51 ++++++++++++++++++ 3 files changed, 124 insertions(+), 25 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index d302426df..d515109a2 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -171,22 +171,46 @@ function devshop_projects_validate($node, &$form) { function devshop_projects_install_sites_form($form_state, $project_node) { $form = array(); + $platforms_ready = TRUE; + $sites_ready = TRUE; + $sites_installing = FALSE; - //Bail if no platforms yet. - if (!isset($project_node->project_objects['platform'])){ + // PLATFORMS STATUS: Determine if all platforms are verified. + if (isset($project_node->project_objects['platform'])){ + foreach ($project_node->project_objects['platform'] as $nid => $env){ + $platform_nodes[$env] = node_load($nid); + if ($platform_nodes[$env]->platform_status == 0){ + $platforms_ready = FALSE; + } + } + } else { + $platforms_ready = FALSE; + } + // SITES STATUS: Determine if sites exist and are enabled + if ($platforms_ready && isset($project_node->project_objects['site'])){ + foreach ($project_node->project_objects['site'] as $nid => $env){ + $site_nodes[$env] = node_load($nid); + if ($site_nodes[$env]->site_status == 0){ + $sites_ready = FALSE; + $sites_installing = TRUE; + } + } + } else { + $sites_ready = FALSE; + } + // OUTPUT + if ($platforms_ready == FALSE){ $form['note'] = array( - '#type' => 'item', - '#title' => t('Clone & Verify'), - '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + '#type' => 'item', + '#title' => t('Clone & Verify'), + '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), ); return $form; - } - // Bail if project doesn't have sites and already has install profile - if (!empty($project->install_profile) || isset($project_node->project_objects['site'])){ + } elseif ($sites_installing) { $form['note'] = array( - '#type' => 'item', - '#title' => t('Sites Install'), - '#value' => t('Your Live, Dev, and Test sites are being installed.'), + '#type' => 'item', + '#title' => t('Sites Installing'), + '#value' => t('Your Live, Dev, and Test sites are being installed!'), ); return $form; } @@ -196,15 +220,25 @@ function devshop_projects_install_sites_form($form_state, $project_node) { $form['ready'] = array( '#type' => 'item', - '#value' => t("Your project's code is ready! Now you must create your sites."), + '#value' => t("Drupal Code has been verified! Now you must create your sites by choosing your installation profile!"), ); $profiles = array_combine((array) hosting_get_profiles($platform_nid, 'short_name'), (array) hosting_get_profiles($platform_nid)); - + + // Sensible default? + // Lets go with standard for now... we can update later. + if (isset($profiles['standard'])) { + $default_profile = 'standard'; + } + // If 'drupal' profile exists, it is likely drupal6! + elseif (isset($profiles['drupal'])) { + $default_profile = 'drupal'; + } $form['install_profile'] = array( '#type' => 'radios', '#title' => t('Choose your install profile'), '#options' => $profiles, + '#default_value' => $default_profile, ); $form['nid'] = array( '#type' => 'value', diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index e2e42611d..26b6cc9ea 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -70,6 +70,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { '#value' => $node->project_environment, '#weight' => -11 ); + return $data; } } @@ -206,19 +207,33 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } - // MAIN DISPLAY - - //If there are no sites yet, we must display the install form. - if (!isset($node->project_objects['site'])){ - - // Site install form - $node->content['install'] =array( - '#type' => 'item', - '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), - ); + - } + //Tasks + $node->content['tasks_view'] = array( + '#type' => 'item', + '#value' => devshop_projects_hosting_task_table($node), + '#prefix' => '
', + '#suffix' => '
', + '#weight' => 10 + ); + $settings['hostingTaskRefresh'] = array( + 'nid' => $node->nid, + 'changed' => $node->changed, + ); + drupal_add_js($settings, 'setting'); + drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); + // MAIN DISPLAY + $node->content['devshop'] = array( + '#type' => 'fieldset', + '#weight' => 100, + ); + // Site install form + $node->content['devshop']['install'] = array( + '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), + '#type' => 'markup', + ); return $node; } @@ -227,7 +242,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { * hosting/projects. It displays a nice tabulated list of projects * and cool thing you can do with them. */ - function devshop_projects_projects_view() { $header = array('Projects', 'Sites'); diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 7b28e8ba1..40a046c98 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -56,3 +56,54 @@ function devshop_projects_hosting_platform_context_options(&$task) { $task->context_options['project'] = $task->ref->project; } +/** + * A replacement for hosting_task_table, to allow us to add + * tasks from other nodes. + */ +function devshop_projects_hosting_task_table($node) { + $output = ''; + + + $headers[] = t('Task'); + $headers[] = array( + 'data' => t('Actions'), + 'class' => 'hosting-actions', + ); + + $tasklist = hosting_task_fetch_tasks($node->nid); + + foreach ($tasklist as $task => $info) { + $row = array(); + + if (!isset($info['nid']) && !$info['task_permitted']) { + // just don't show those tasks, since we'll not be able to run them + continue; + } + + $row['type'] = array( + 'data' => $info['title'], + 'class' => 'hosting-status', + ); + $actions = array(); + + if (isset($info['task_status']) && ($info['task_status'] == 0)) { + $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']); + } + else { + $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); + } + + $actions['log'] = _hosting_task_button(t('View'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); + $row['actions'] = array( + 'data' => implode('', $actions), + 'class' => 'hosting-actions', + ); + + $rows[] = array( + 'data' => $row, + 'class' => $info['class'], + ); + } + $output .= theme('table', $headers, $rows, array('class' => 'hosting-table')); + return $output; +} \ No newline at end of file From fce5abd63ff86be439b2c8dbb847fa2523885f85 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 16:20:28 -0400 Subject: [PATCH 0053/3476] do not show form if sites are ready --- devshop_projects/devshop_projects.form.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index d515109a2..f4d5fde5f 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -198,6 +198,7 @@ function devshop_projects_install_sites_form($form_state, $project_node) { } else { $sites_ready = FALSE; } + // OUTPUT if ($platforms_ready == FALSE){ $form['note'] = array( @@ -213,6 +214,8 @@ function devshop_projects_install_sites_form($form_state, $project_node) { '#value' => t('Your Live, Dev, and Test sites are being installed!'), ); return $form; + } elseif ($sites_ready) { + return; } // Load all install profiles found on dev platform From 04d096bd2e64428a8aae5214c2271ce93877d6ec Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 16:24:41 -0400 Subject: [PATCH 0054/3476] Loading up tasks from our primary sites! --- devshop_projects/devshop_projects.task.inc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 40a046c98..cee9205ca 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -72,6 +72,27 @@ function devshop_projects_hosting_task_table($node) { $tasklist = hosting_task_fetch_tasks($node->nid); + // Get tasklists for all sites + $tasks = array(); + $site_nids = array_flip($node->project_objects['site']); + foreach ($node->project_objects['site'] as $nid => $env){ + $tasks[$env] = hosting_task_fetch_tasks($nid); + $site_nodes[$env] = node_load($nid); + } + // Add our own specific tasks + $tasklist['devshop-commit'] = $tasks['dev']['devshop-commit']; + $tasklist['devshop-sync'] = $tasks['test']['devshop-sync']; + $tasklist['devshop-pull'] = $tasks['live']['devshop-pull']; + + // Enhance titles + $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, "node/" . $site_nids['dev']); + $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, "node/" . $site_nids['test']); + $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, "node/" . $site_nids['live']); + + // Override some + unset($tasklist['devshop-create']['task_permitted']); + unset($tasklist['devshop-create']['nid']); + foreach ($tasklist as $task => $info) { $row = array(); From ea042b88c6917c255bb269145d74f525de4f1bd8 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 17:00:59 -0400 Subject: [PATCH 0055/3476] adding hook menu alter to hijack aegir JS --- devshop_projects/devshop_projects.module | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 79cace6de..9fff977ee 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -43,6 +43,51 @@ function devshop_projects_menu() { return ($items); } +/** + * Implementation of hook_menu_alter() + */ +function devshop_projects_menu_alter(&$items) { + //$items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; +} + +/** + * Replacement menu callback for hosting_js_page(). + * + * This is used for Project Tasks. Since the tasks are really tasks on the site nodes, + * we have to modify the JS call to modify what node gets passed in. + */ +function devshop_projects_hosting_js_page(){ + $args = func_get_args(); + $nid = &$args[1]; + $task = &$args[2]; + + // Get task type from $task argument... For whatver reason, they don't make it an argument + list($aegir_context, $task_type) = explode('_', $task); + + // If this is coming from a project page: + if ($aegir_context == 'project'){ + // Change the task object from project to site + $task = str_replace('project', 'site', $task); + + // Load the project + $project = node_load($nid); + $sites = array_flip($project->project_objects['site']); + + // Change the NID based on the task type + if ($task_type == 'devshop-commit') { + $nid = $sites['dev']; + } elseif ($task_type == 'devshop-sync') { + $nid = $sites['test']; + } elseif ($task_type == 'devshop-pull') { + $nid = $sites['live']; + } + } + + $output .= kpr($args); + $output .= call_user_func_array('hosting_js_page', $args); + return $output; +} + /** * Status display */ From 142026855ef2616370aa69ed74d1e3def4eac6d7 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 17:21:25 -0400 Subject: [PATCH 0056/3476] fixing up Sync Form --- devshop_tasks/devshop_tasks.module | 58 ++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index a214787ff..434fb1199 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -143,26 +143,46 @@ function hosting_task_devshop_sync_form($node) { '#type' => 'textfield', '#description' => t('Enter a site alias to sync from. The alias must exist in the local system.'), ); -*/ - - $sites = array(); - $r = db_query("SELECT n.title FROM {node} AS n " . - "LEFT JOIN {hosting_site} AS h ON n.nid = h.nid " . - "WHERE n.type = 'site' AND h.status = 1 AND n.title <> '%s'" . - "ORDER BY n.title ASC", - $node->title); - - while($sa = db_fetch_object($r)) { - $sites['@' . $sa->title] = '@' . $sa->title; + */ + // Get nid + $nid = $node->nid; + + // See if this site is already in a project + $query = db_query("SELECT project_nid " . + "FROM {hosting_devshop_project_object} " . + "WHERE object_nid = %d " . + "AND object_type = 'site'", $nid); + + if($proj = db_fetch_object($query)) { + + // Only make sites in this project a source for content + $query = db_query("SELECT object_nid " . + "FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d " . + "AND object_type = 'site' " . + "AND object_nid <> %d", $proj->project_nid, $nid); + + $options = array(); + while($s = db_fetch_object($query)) { + $site_node = node_load($s->object_nid); + $options['@' . $site_node->title] = '@' . $site_node->title; + } + + $form['note'] = array( + '#value' => '

'. t('This will DESTROY the database for !site and replace it with the database for the selected Source Site.', array('!site' => l($node->title, "node/$nid"))) . '

', + '#type' => 'markup', + '#weight' => 100, + ); + + $form['source'] = array( + '#type' => 'radios', + '#title' => t('Source Site'), + '#options' => $options, + '#default_value' => key($options), + '#description' => t('Select site to sync from.'), + ); } - - $form['source'] = array( - '#type' => 'select', - '#title' => t('Source site alias'), - '#options' => $sites, - '#description' => t('Select site alias to sync from.'), - ); - + $form['pull'] = array( '#title' => t('Pull code before content sync?'), '#type' => 'checkbox', From 621c97a4073a5b344be14b1b5a30fd847d8e26e4 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 17:21:55 -0400 Subject: [PATCH 0057/3476] Removing "Sync" source selector from devshop_projects to devshop_tasks. --- devshop_projects/devshop_projects.form.inc | 35 ---------------------- 1 file changed, 35 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index f4d5fde5f..e88e753da 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -16,41 +16,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ unset($form['author']); unset($form['options']); } - - if ($form_id == 'hosting_task_confirm_form'){ - // Get nid - $nid = $form['nid']['#value']; - - // See if this site is already in a project - $query = db_query("SELECT project_nid " . - "FROM {hosting_devshop_project_object} " . - "WHERE object_nid = %d " . - "AND object_type = 'site'", $nid); - - if($proj = db_fetch_object($query)) { - - // Only make sites in this project a source for content - $query = db_query("SELECT object_nid " . - "FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d " . - "AND object_type = 'site' " . - "AND object_nid <> %d", $proj->project_nid, $nid); - - $options = array(); - while($s = db_fetch_object($query)) { - $node = node_load($s->object_nid); - $options['@' . $node->title] = '@' . $node->title; - } - - $form['parameters']['source'] = array( - '#type' => 'radios', - '#title' => t('Source site alias'), - '#options' => $options, - '#description' => t('Select site alias to sync from.'), - ); - } - } - } /** From 29dee710affdb3790f60293589e0ef6c42b7dae9 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 17:22:10 -0400 Subject: [PATCH 0058/3476] adding back in the menu_alter --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9fff977ee..6ed7e85fc 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -47,7 +47,7 @@ function devshop_projects_menu() { * Implementation of hook_menu_alter() */ function devshop_projects_menu_alter(&$items) { - //$items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; + $items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; } /** From b98fd5d2b0633c4dd3a916340ed872c1f2561c92 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 17:24:12 -0400 Subject: [PATCH 0059/3476] removing kpr --- devshop_projects/devshop_projects.module | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6ed7e85fc..0ab9f7e23 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -83,7 +83,6 @@ function devshop_projects_hosting_js_page(){ } } - $output .= kpr($args); $output .= call_user_func_array('hosting_js_page', $args); return $output; } From d82df6528a1828a841e56b8ea8bf31cac2052e3e Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 17:52:29 -0400 Subject: [PATCH 0060/3476] adding JSON task list so project tasks reload properly --- devshop_projects/devshop_projects.module | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 0ab9f7e23..0d609cdfc 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -47,9 +47,25 @@ function devshop_projects_menu() { * Implementation of hook_menu_alter() */ function devshop_projects_menu_alter(&$items) { + $items['hosting/tasks/%node/list']['page callback'] = 'devshop_projects_hosting_task_ajax_list'; $items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; } +/** + * Replacement menu callback for hosting_task_ajax_list + */ +function devshop_projects_hosting_task_ajax_list($node) { + if ($node->type == 'project') { + module_load_include('tasks.inc', 'devshop_projects'); + $return['markup'] = devshop_projects_hosting_task_table($node); + $return['changed'] = $node->changed; + drupal_json($return); + exit(); + } else { + hosting_task_ajax_list($node); + } +} + /** * Replacement menu callback for hosting_js_page(). * From f7f30e35fbf5747c132e816b0e4a1da8e9057ef6 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:10:04 -0400 Subject: [PATCH 0061/3476] fixing patch --- 1778400-hosting-tasks-names-hooks-1.9.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1778400-hosting-tasks-names-hooks-1.9.patch b/1778400-hosting-tasks-names-hooks-1.9.patch index 3eb543088..7f0a775ef 100644 --- a/1778400-hosting-tasks-names-hooks-1.9.patch +++ b/1778400-hosting-tasks-names-hooks-1.9.patch @@ -7,7 +7,7 @@ index 31dd56f..6c95989 100644 function drush_hosting_hosting_task_rollback() { $task =& drush_get_context('HOSTING_TASK'); - module_invoke_all(sprintf("hosting_%s_task_rollback", $task->task_type), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); -+ module_invoke_all(sprintf("hosting_%s_task_rollback", str_replace('-', '_', $task->task_type))), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); ++ module_invoke_all(sprintf("hosting_%s_task_rollback", str_replace('-', '_', $task->task_type)), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); } /** From 821961c01a177a37c89fead47ab022d1f167094e Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:26:52 -0400 Subject: [PATCH 0062/3476] adding project status to the node load --- devshop_projects/devshop_projects.node.inc | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 26b6cc9ea..7e253a3dd 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -136,6 +136,43 @@ function devshop_projects_load($node) { $objects[$project_object->object_type][$project_object->object_nid] = $project_object->env_type; } $additions['project_objects'] = $objects; + + //Project status + $platforms_ready = TRUE; + $sites_ready = TRUE; + $sites_installing = FALSE; + + // PLATFORMS STATUS: Determine if all platforms are verified. + if (isset($additions['project_objects']['platform'])){ + foreach ($additions['project_objects']['platform'] as $nid => $env){ + $platform_nodes[$env] = node_load($nid); + if ($platform_nodes[$env]->platform_status == 0){ + $platforms_ready = FALSE; + } + } + } else { + $platforms_ready = FALSE; + } + // SITES STATUS: Determine if sites exist and are enabled + if ($platforms_ready && isset($additions['project_objects']['site'])){ + foreach ($additions['project_objects']['site'] as $nid => $env){ + $site_nodes[$env] = node_load($nid); + if ($site_nodes[$env]->site_status == 0){ + $sites_ready = FALSE; + $sites_installing = TRUE; + } + } + } else { + $sites_ready = FALSE; + } + + // Set status + $additions['project_status'] = $sites_ready? 'sites_ready': ( + $sites_installing? 'sites_installing': ( + $platforms_ready? 'platforms_ready': 'platforms_verifying' + ) + ); + return $additions; } From 8b55256256ac794d64372f88e9b279e7b7441a7a Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:30:17 -0400 Subject: [PATCH 0063/3476] returning if project status isn't ready for site tasks --- devshop_projects/devshop_projects.task.inc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index cee9205ca..46fc8e93f 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -74,11 +74,10 @@ function devshop_projects_hosting_task_table($node) { // Get tasklists for all sites $tasks = array(); - $site_nids = array_flip($node->project_objects['site']); - foreach ($node->project_objects['site'] as $nid => $env){ - $tasks[$env] = hosting_task_fetch_tasks($nid); - $site_nodes[$env] = node_load($nid); + if ($node->project_status != 'sites_installed'){ + return; } + // Add our own specific tasks $tasklist['devshop-commit'] = $tasks['dev']['devshop-commit']; $tasklist['devshop-sync'] = $tasks['test']['devshop-sync']; From f6bf435ee1373baf47d1d6de280c861b9611cb57 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:42:15 -0400 Subject: [PATCH 0064/3476] fixing up sites table. --- devshop_projects/devshop_projects.node.inc | 36 ++++++++++------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 7e253a3dd..799b8a317 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -225,27 +225,25 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } - - foreach($node->project_objects as $type => $objects) { - $header = array($type ."s"); - $rows = array(); - foreach($objects as $nid => $object) { - $object = node_load($nid); - $row = array(l($object->title, "node/$object->nid")); - $rows[] = $row; - - } - $table = theme('table', $header, $rows); - - $node->content['info'][$type] = array( - '#type' => 'item', - '#value' => $table, - ); - + $rows = array(); + foreach($node->project_objects['site'] as $nid => $env) { + $site = node_load($nid); + $row = array(); + $url = "http://$site->title"; + $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); + $row[] = l('Site', "node/$site->nid"); + $row[] = l('Platform', "node/$site->platform"); + $row[] = l('Download Database', "$url/admin/status/"); + $rows[] = $row; } - + $table = theme('table', $header, $rows); + + $node->content['sites'] = array( + '#type' => 'item', + '#value' => $table, + '#weight' => 11, + ); - //Tasks $node->content['tasks_view'] = array( '#type' => 'item', From a7b216cd32433cd4cb9d90878c5ab248da82806e Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:47:44 -0400 Subject: [PATCH 0065/3476] fixing tasks list of projects --- devshop_projects/devshop_projects.task.inc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 46fc8e93f..40034e537 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -63,7 +63,6 @@ function devshop_projects_hosting_platform_context_options(&$task) { function devshop_projects_hosting_task_table($node) { $output = ''; - $headers[] = t('Task'); $headers[] = array( 'data' => t('Actions'), @@ -72,10 +71,16 @@ function devshop_projects_hosting_task_table($node) { $tasklist = hosting_task_fetch_tasks($node->nid); + if ($node->project_status != 'sites_ready'){ + return; + } + + // Get tasklists for all sites $tasks = array(); - if ($node->project_status != 'sites_installed'){ - return; + foreach ($node->project_objects['site'] as $nid => $env){ + $site_nodes[$env] = node_load($nid); + $tasks[$env] = hosting_task_fetch_tasks($nid); } // Add our own specific tasks From 3ce95482ba2b890a3f0484200d0387bf959dce38 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:49:12 -0400 Subject: [PATCH 0066/3476] View log button --- devshop_projects/devshop_projects.task.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 40034e537..da8750ebd 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -118,7 +118,7 @@ function devshop_projects_hosting_task_table($node) { $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); } - $actions['log'] = _hosting_task_button(t('View'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); + $actions['log'] = _hosting_task_button(t('View Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); $row['actions'] = array( 'data' => implode('', $actions), 'class' => 'hosting-actions', From bb175f56ae3c896e8e3712b655897a3095623490 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:49:53 -0400 Subject: [PATCH 0067/3476] created a check if site has module function, using it to determine visiblity of Commit Features button --- devshop_tasks/devshop_tasks.module | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index 434fb1199..1c8e48a5c 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -10,22 +10,23 @@ */ function devshop_tasks_hosting_tasks() { $tasks = array(); + + // If on a node page... Hide innapropriate tasks + if (arg(0) == 'node' && is_numeric(arg(1))) { + $noded = node_load(arg(1)); + if ($node && $node->project) { + $pull = $node->pull; + } + } $tasks['site']['devshop-commit'] = array( 'title' => t('Commit Features'), 'description' => t('Recreates all Features and commits the result.'), 'dialog' => TRUE, + 'hidden' => !_devshop_tasks_site_has_module($node, 'features'), ); $pull = false; - if (arg(0) == 'node' && is_numeric(arg(1))) { - $node = node_load(arg(1)); - if ($node && $node->platform) { - $pnode = node_load($node->platform); - $pull = $pnode->pull; - } - } - $tasks['site']['devshop-pull'] = array( 'title' => t('Pull Code'), 'description' => t('Pull & verify site code and (optionally) run update.php, clear cache, and revert features.'), @@ -55,10 +56,10 @@ function devshop_tasks_perm() { /** * Check if a site has features diff enabled. */ -function _devshop_tasks_site_has_features_diff($node) { +function _devshop_tasks_site_has_module($node, $module) { $param = array( 'rid' => $node->nid, - 'p.short_name' => 'features_diff', + 'p.short_name' => $module, ); $package = hosting_package_instance_load($param); return $package->status; @@ -73,7 +74,7 @@ function hosting_task_devshop_commit_form($node) { $descr = 'A message describing this commit. Too see a diff output off all of the features, '; - if (_devshop_tasks_site_has_features_diff($node)) { + if (_devshop_tasks_site_has_module($node, 'features_diff')) { $descr .= 'click ' . l(t('here.'), "http://$node->hosting_name/features/diff/all", array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . ' Be patient. It takes a few moments for the diffs to be generatred.'; } else { From c8aedce34993c2f42d665edb526d74a2a5ee250c Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:51:32 -0400 Subject: [PATCH 0068/3476] removing old patch --- .../hostmaster-bettertaskforms.1513678.patch | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 devshop_tasks/hostmaster-bettertaskforms.1513678.patch diff --git a/devshop_tasks/hostmaster-bettertaskforms.1513678.patch b/devshop_tasks/hostmaster-bettertaskforms.1513678.patch deleted file mode 100644 index 38833f1d1..000000000 --- a/devshop_tasks/hostmaster-bettertaskforms.1513678.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git modules/hosting/task/hosting_task.module modules/hosting/task/hosting_task.module -index 144aac0..88e27f9 100644 ---- modules/hosting/task/hosting_task.module -+++ modules/hosting/task/hosting_task.module -@@ -368,7 +368,7 @@ function hosting_task_confirm_form($form_state, $node, $task) { - $form['nid'] = array('#type' => 'value', '#value' => $node->nid); - $form['task'] = array('#type' => 'value', '#value' => $task); - $form['parameters'] = array('#tree' => TRUE); -- $func = 'hosting_task_' . $task . '_form'; -+ $func = 'hosting_task_' . str_replace('-', '_', $task) . '_form'; - if (function_exists($func)) { - $form['parameters'] += $func($node); - } From 5ec06a7a0540688dd19701416151c1989885afe4 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:56:05 -0400 Subject: [PATCH 0069/3476] creating fieldset for environment links --- devshop_projects/devshop_projects.node.inc | 25 +++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 799b8a317..c672e7d32 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -217,14 +217,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } - if (isset($node->project_objects['platform'])){ - $node->content['info']['add_platform'] = array( - '#type' => 'item', - '#value' => l(t('Create and add additional platforms'), - "hosting/projects/platform/create/$node->nid"), - ); - } - $rows = array(); foreach($node->project_objects['site'] as $nid => $env) { $site = node_load($nid); @@ -239,10 +231,23 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $table = theme('table', $header, $rows); $node->content['sites'] = array( + '#type' => 'fieldset', + '#title' => t('Project Environments'), + '#weight' => 12, + ); + $node->content['sites']['table'] = array( '#type' => 'item', '#value' => $table, - '#weight' => 11, - ); + ); + + if ($node->project_status == 'sites_ready'){ + $node->content['sites']['add_platform'] = array( + '#type' => 'item', + '#value' => l(t('Create and add additional platforms'), + "hosting/projects/platform/create/$node->nid"), + ); + } + //Tasks $node->content['tasks_view'] = array( From f6e5378c87b830d653555dbf1ba63f23996e39a1 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:57:56 -0400 Subject: [PATCH 0070/3476] creating a fieldset for git pull info --- devshop_pull/devshop_pull.module | 61 +++++++++++++++++--------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 05a6c3ef0..fb8431a64 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -101,46 +101,51 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { switch ($op) { case 'view': if (!$a3) { //!teaser - if($node->type == 'project') { - $pnode = $node; - } - else { - $pnode = node_load($node->project_nid); - } - - // @TODO: PULL QUEUE must be enabled first! Show a notice to the user if Pull Queue - // is not enabled! - - $pull_method = ($pnode->pull_method == DEVSHOP_PULL_CALLBACK? t('URL Callback'): - ($pnode->pull_method == DEVSHOP_PULL_QUEUE? t('Hosting Queue'): - (t('Disabled')))); - - if ($pnode->pull_method == DEVSHOP_PULL_CALLBACK){ - module_load_include('inc', 'devshop_pull'); - $url = _devshop_pull_callback_url($pnode); - $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); - } + if($node->type == 'project') { + $pnode = $node; + } + else { + $pnode = node_load($node->project_nid); + } + + // @TODO: PULL QUEUE must be enabled first! Show a notice to the user if Pull Queue + // is not enabled! + + $pull_method = ($pnode->pull_method == DEVSHOP_PULL_CALLBACK? t('URL Callback'): + ($pnode->pull_method == DEVSHOP_PULL_QUEUE? t('Hosting Queue'): + (t('Disabled')))); + + if ($pnode->pull_method == DEVSHOP_PULL_CALLBACK){ + module_load_include('inc', 'devshop_pull'); + $url = _devshop_pull_callback_url($pnode); + $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); + } if (isset($pnode) && $pnode->pull_method != DEVSHOP_PULL_DISABLED){ - $node->content['info']['pull'] = array( - '#type' => 'item', - '#title' => t('Pull Method'), - '#weight' => 30, - '#value' => $pull_method, - ); - $node->content['info']['pull_reset'] = array( + $node->content['devshop_pull'] = array( + '#type' => 'fieldset', + '#title' => t('Pull Configuration'), + '#weight' => 12, + ); + $node->content['devshop_pull']['pull'] = array( + '#type' => 'item', + '#title' => t('Pull Method'), + '#weight' => 30, + '#value' => $pull_method, + ); + $node->content['devshop_pull']['pull_reset'] = array( '#type' => 'item', '#title' => t('Reset on Pull'), '#weight' => 31, '#value' => $pnode->pull_reset? t('Enabled'): t('Disabled'), ); - $node->content['info']['last_pull'] = array( + $node->content['devshop_pull']['last_pull'] = array( '#type' => 'item', '#title' => t('Last pull'), '#weight' => 32, '#value' => hosting_format_interval($pnode->last_pull), ); - $node->content['info']['last_pull_status'] = array( + $node->content['devshop_pull']['last_pull_status'] = array( '#type' => 'item', '#title' => t('Last pull status'), '#weight' => 33, From ea5eb42b7f86132a35b78de3319144dc9c4039b3 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 18:58:57 -0400 Subject: [PATCH 0071/3476] minor typo --- devshop_projects/devshop_projects.node.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index c672e7d32..7e173eeff 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -66,7 +66,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { ); $node->content['info']['env'] = array( '#type' => 'item', - '#title' => t('Environement Type'), + '#title' => t('Environment Type'), '#value' => $node->project_environment, '#weight' => -11 ); From cfc93529c909b29b9309407f6a16f6bde978f5b3 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 19:03:35 -0400 Subject: [PATCH 0072/3476] check for features before offering to revert it. --- devshop_tasks/devshop_tasks.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index 1c8e48a5c..ad64bec80 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -123,6 +123,7 @@ function hosting_task_devshop_pull_form($node) { '#title' => t('Revert all features after code pull?'), '#type' => 'checkbox', '#default_value' => 1, + '#access' => _devshop_tasks_site_has_module($node, 'features'), ); $form['cache'] = array( '#title' => t('Clear cache after code pull?'), @@ -198,6 +199,7 @@ function hosting_task_devshop_sync_form($node) { '#title' => t('Revert all features after content sync?'), '#type' => 'checkbox', '#default_value' => 1, + '#access' => _devshop_tasks_site_has_module($site_node, 'features'), ); $form['cache'] = array( '#title' => t('Clear cache after content sync?'), From a41c9943bd133365f803a1193abb5a9265fce8ba Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 19:08:00 -0400 Subject: [PATCH 0073/3476] typo --- devshop_tasks/devshop_tasks.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index ad64bec80..5c1d831ae 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -13,7 +13,7 @@ function devshop_tasks_hosting_tasks() { // If on a node page... Hide innapropriate tasks if (arg(0) == 'node' && is_numeric(arg(1))) { - $noded = node_load(arg(1)); + $node = node_load(arg(1)); if ($node && $node->project) { $pull = $node->pull; } From 63917c20188abfe98663b32aa2c547e80f68c72c Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 19:11:50 -0400 Subject: [PATCH 0074/3476] making links in tasks to go to site --- devshop_projects/devshop_projects.task.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index da8750ebd..2e767f460 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -89,9 +89,9 @@ function devshop_projects_hosting_task_table($node) { $tasklist['devshop-pull'] = $tasks['live']['devshop-pull']; // Enhance titles - $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, "node/" . $site_nids['dev']); - $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, "node/" . $site_nids['test']); - $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, "node/" . $site_nids['live']); + $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, 'http://'. $site_nodes['dev']->title, array('attributes' => array('target' => '_blank'))); + $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); + $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); // Override some unset($tasklist['devshop-create']['task_permitted']); From 128f1516e7d37cd12b56f11f82e353b95e0ec314 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 20:28:16 -0400 Subject: [PATCH 0075/3476] removing "node page" task hiding. --- devshop_tasks/devshop_tasks.module | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index 5c1d831ae..b58da1642 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -10,20 +10,11 @@ */ function devshop_tasks_hosting_tasks() { $tasks = array(); - - // If on a node page... Hide innapropriate tasks - if (arg(0) == 'node' && is_numeric(arg(1))) { - $node = node_load(arg(1)); - if ($node && $node->project) { - $pull = $node->pull; - } - } $tasks['site']['devshop-commit'] = array( 'title' => t('Commit Features'), 'description' => t('Recreates all Features and commits the result.'), 'dialog' => TRUE, - 'hidden' => !_devshop_tasks_site_has_module($node, 'features'), ); $pull = false; @@ -31,7 +22,6 @@ function devshop_tasks_hosting_tasks() { 'title' => t('Pull Code'), 'description' => t('Pull & verify site code and (optionally) run update.php, clear cache, and revert features.'), 'dialog' => TRUE, - 'hidden' => $pull, ); $tasks['site']['devshop-sync'] = array( From 51e72c02229d85408e4020029ef92f0cd41137e8 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Sun, 9 Sep 2012 20:31:10 -0400 Subject: [PATCH 0076/3476] changing button to "Log" --- devshop_projects/devshop_projects.task.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 2e767f460..b9bf85f4c 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -118,7 +118,7 @@ function devshop_projects_hosting_task_table($node) { $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); } - $actions['log'] = _hosting_task_button(t('View Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); + $actions['log'] = _hosting_task_button(t('Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); $row['actions'] = array( 'data' => implode('', $actions), 'class' => 'hosting-actions', From 0de39e6d8712ebc39ea54f9a234063b4e030a741 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Date: Mon, 10 Sep 2012 18:25:13 +0200 Subject: [PATCH 0077/3476] Jacinto. Add information when finished or failed pull --- devshop_pull/devshop_pull.inc | 3 +++ devshop_pull/devshop_pull.module | 22 +++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 383950989..7936f4322 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -75,6 +75,9 @@ function devshop_pull_callback($project, $hash) { $r = db_query("SELECT nid FROM {hosting_site} " . "WHERE platform = %d AND status = 1", $platform); + //Put timestamp + db_query('UPDATE {hosting_devshop_pull} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); + while ($site = db_fetch_object($r)) { $message = "Queuing a pull task for node id $site->nid"; watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 05a6c3ef0..158a218e2 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -187,6 +187,23 @@ function devshop_pull_get_sites($limit = 5) { return db_result($result); } +/** + * Implementation of hook_hosting_TASK_TYPE_task_rollback(). + */ +function devshop_pull_hosting_devshop_pull_task_rollback($task, $data) { + $project = node_load($task->ref->project_nid); + watchdog('devshop', 'Pull FAILED on project: @alias', array('@alias' => $project->title)); + db_query('UPDATE {hosting_devshop_pull} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $task->ref->project_nid); +} + +/** + * Implementation of hook_post_hosting_TASK_TYPE_task(). + */ +function devshop_pull_post_hosting_devshop_pull_task($task, $data) { + $project = node_load($task->ref->project_nid); + watchdog('devshop', 'Pull SUCCESS on project: @alias', array('@alias' => $project->title)); +} + /** * Implementation of hosting_QUEUE_TYPE_queue(). */ @@ -198,11 +215,6 @@ function hosting_pull_queue($count) { devshop_pull_task($project->project_nid); module_invoke_all('devshop_pull', $project); - - //@TODO: Set last_pull_status - // watchdog('devshop', 'Pull FAILED on platform: @alias', array('@alias' => $platform_name)); - // db_query('UPDATE {hosting_devshop_pull} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $platform->project_nid); - } } From a23b83d7f2ee8af2f2a63f77c1d45cdbd437f6a0 Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Tue, 11 Sep 2012 10:00:17 -0400 Subject: [PATCH 0078/3476] checking for sites existence before looping through them --- devshop_projects/devshop_projects.node.inc | 43 +++++++++++----------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 7e173eeff..5ae322d5d 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -218,28 +218,29 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $rows = array(); - foreach($node->project_objects['site'] as $nid => $env) { - $site = node_load($nid); - $row = array(); - $url = "http://$site->title"; - $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); - $row[] = l('Site', "node/$site->nid"); - $row[] = l('Platform', "node/$site->platform"); - $row[] = l('Download Database', "$url/admin/status/"); - $rows[] = $row; - } - $table = theme('table', $header, $rows); + if (isset($node->project_objects['site'])) { + foreach($node->project_objects['site'] as $nid => $env) { + $site = node_load($nid); + $row = array(); + $url = "http://$site->title"; + $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); + $row[] = l('Site', "node/$site->nid"); + $row[] = l('Platform', "node/$site->platform"); + $row[] = l('Download Database', "$url/admin/status/"); + $rows[] = $row; + } + $table = theme('table', $header, $rows); - $node->content['sites'] = array( - '#type' => 'fieldset', - '#title' => t('Project Environments'), - '#weight' => 12, - ); - $node->content['sites']['table'] = array( - '#type' => 'item', - '#value' => $table, - ); - + $node->content['sites'] = array( + '#type' => 'fieldset', + '#title' => t('Project Environments'), + '#weight' => 12, + ); + $node->content['sites']['table'] = array( + '#type' => 'item', + '#value' => $table, + ); + } if ($node->project_status == 'sites_ready'){ $node->content['sites']['add_platform'] = array( '#type' => 'item', From dee0675945c88c9711aa4ce276499e131424b819 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Tue, 11 Sep 2012 16:12:51 -0600 Subject: [PATCH 0079/3476] work in progress --- devshop_projects/devshop_projects.form.inc | 10 +-- devshop_projects/devshop_projects.task.inc | 80 +++++++++++++++++++++- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index e88e753da..81aabf5f5 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -284,7 +284,7 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ * Form for platform creation */ function devshop_projects_platform_create_form($form_state, $project_nid) { - + //print_r(hosting_task_fetch_tasks($project_nid)); $project_node = node_load($project_nid); $form = array(); @@ -352,14 +352,8 @@ function devshop_projects_platform_create_form_validate(&$form, &$form_state){ } /** - * Submit for site installation - * - * I believe, based on the fact that provision has to create - * the site aliases before it runs provision-install, we have - * to have a new hostmaster task here. + * Submit for platform create * - * If we save the install_profile in the project context, - * the task could easily create the new sites. */ function devshop_projects_platform_create_form_submit(&$form, &$form_state){ diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index b9bf85f4c..24cfd2426 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -18,6 +18,11 @@ function devshop_projects_hosting_tasks() { 'title' => t('Install Sites'), 'description' => t('Runs drupal installation on each of the three platforms.'), ); + $tasks['project']['devshop-platform-create'] = array( + 'title' => t('Create Platform'), + 'description' => t('Create a new platform to add to an existin project.'), + ); + return $tasks; } @@ -56,6 +61,7 @@ function devshop_projects_hosting_platform_context_options(&$task) { $task->context_options['project'] = $task->ref->project; } + /** * A replacement for hosting_task_table, to allow us to add * tasks from other nodes. @@ -70,7 +76,7 @@ function devshop_projects_hosting_task_table($node) { ); $tasklist = hosting_task_fetch_tasks($node->nid); - + print_r($tasklist); if ($node->project_status != 'sites_ready'){ return; } @@ -93,6 +99,12 @@ function devshop_projects_hosting_task_table($node) { $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); + $tasklist['devshop-platform-create']['nid'] = $node->nid; + $tasklist['devshop-platform-create']['task_status'] = 1; + $tasklist['devshop-platform-create']['exists'] = 1; + + + // Override some unset($tasklist['devshop-create']['task_permitted']); unset($tasklist['devshop-create']['nid']); @@ -100,7 +112,8 @@ function devshop_projects_hosting_task_table($node) { foreach ($tasklist as $task => $info) { $row = array(); - if (!isset($info['nid']) && !$info['task_permitted']) { + if (!isset($info['nid']) && !$info['task_permitted'] && + $task != 'devshop-platform-create') { // just don't show those tasks, since we'll not be able to run them continue; } @@ -131,4 +144,65 @@ function devshop_projects_hosting_task_table($node) { } $output .= theme('table', $headers, $rows, array('class' => 'hosting-table')); return $output; -} \ No newline at end of file +} + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form(). + * + * For "Commit" task. + */ +function hosting_task_devshop_platform_create_form($project_node) { + $form = array(); + + //Bail if no platforms yet. + if (!isset($project_node->project_objects['platform'])){ + $form['note'] = array( + '#type' => 'item', + '#title' => t('Clone & Verify'), + '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + ); + return $form; + } + + $form['platform_name'] = array( + '#type' => 'textfield', + '#title' => t('Platform Name'), + '#required' => TRUE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => '', + '#maxlength' => 255, + ); + $form['git_url'] = array( + '#type' => 'textfield', + '#title' => t('Git URL'), + '#required' => FALSE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => '', + '#maxlength' => 255, + ); + $form['branch'] = array( + '#type' => 'textfield', + '#title' => t('Branch'), + + '#required' => TRUE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => 'master', + '#maxlength' => 255, + ); + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Create platform'), + ); + return $form; +} + From 52685c2bc8432c0282c9500ad48fa252a58fa9ff Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Tue, 11 Sep 2012 18:57:53 -0400 Subject: [PATCH 0080/3476] commenting print_r --- devshop_projects/devshop_projects.task.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 24cfd2426..385712369 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -76,7 +76,7 @@ function devshop_projects_hosting_task_table($node) { ); $tasklist = hosting_task_fetch_tasks($node->nid); - print_r($tasklist); + //print_r($tasklist); if ($node->project_status != 'sites_ready'){ return; } From ee771cc32460248bfdc48790af25a5912ae9434a Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Thu, 13 Sep 2012 16:43:32 -0400 Subject: [PATCH 0081/3476] loading project_name into platforms and sites --- devshop_projects/devshop_projects.module | 6 ++++++ devshop_projects/devshop_projects.node.inc | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 0d609cdfc..6509dfbf4 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -122,3 +122,9 @@ function devshop_projects_project_status(){ return $msg; } +/** + * Implements hook_hosting_drush_aliases_name() + */ +function devshop_projects_hosting_drush_aliases_name($node) { + return $node->project_name .".". $node->project_environment; +} diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 5ae322d5d..7c22e6f13 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -51,7 +51,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load Project info if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); return $data; } From 890a547687ca0ecca60bf2e7a2c88a74a69934cc Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Thu, 13 Sep 2012 16:45:47 -0400 Subject: [PATCH 0082/3476] loading project_name into platform and site nodes, adding hook for hosting_drush_aliases --- devshop_projects/devshop_projects.module | 6 ------ devshop_projects/devshop_projects.node.inc | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6509dfbf4..0d609cdfc 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -122,9 +122,3 @@ function devshop_projects_project_status(){ return $msg; } -/** - * Implements hook_hosting_drush_aliases_name() - */ -function devshop_projects_hosting_drush_aliases_name($node) { - return $node->project_name .".". $node->project_environment; -} diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 7c22e6f13..5ae322d5d 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -51,7 +51,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load Project info if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); return $data; } From c74f4e9e97fdd150fc5363de296cfd908b7ba76c Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Thu, 13 Sep 2012 17:05:15 -0400 Subject: [PATCH 0083/3476] adding hook_hook_hosting_drush_aliases_name() --- devshop_projects/devshop_projects.module | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 0d609cdfc..f6e62e1eb 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -122,3 +122,11 @@ function devshop_projects_project_status(){ return $msg; } +/** + * Implements hook_hosting_drush_aliases_name() + */ +function devshop_projects_hosting_drush_aliases_name($node) { + if (isset($node->project_name)){ + return $node->project_name .".". $node->project_environment; + } +} From 90a923303b6f5844400a9162156958c5389790dd Mon Sep 17 00:00:00 2001 From: Jonathan Pugh Date: Thu, 13 Sep 2012 17:05:25 -0400 Subject: [PATCH 0084/3476] putting loading back in --- devshop_projects/devshop_projects.node.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 5ae322d5d..7c22e6f13 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -51,7 +51,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load Project info if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); return $data; } From 4bb7b640b23909ae2d79c94938dff263e73dbf0f Mon Sep 17 00:00:00 2001 From: helmo Date: Mon, 17 Sep 2012 09:52:44 -0500 Subject: [PATCH 0085/3476] Issue #1786746 by helmo: Fixed site counter incorrect on hosting/projects. --- devshop_projects/devshop_projects.node.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 7c22e6f13..478def3b7 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -294,7 +294,7 @@ function devshop_projects_projects_view() { $node = node_load($proj->nid); $row = array(); $row[] = l($node->title, "node/$proj->nid"); - $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid)); + $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); $rows[] = $row; } From c879b9537f2f79a116c6a39a8574c69d67426638 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Wed, 19 Sep 2012 09:53:58 -0600 Subject: [PATCH 0086/3476] Checking in --- devshop_projects/devshop_projects.form.inc | 47 ++++++---------------- devshop_projects/devshop_projects.module | 2 +- devshop_projects/devshop_projects.task.inc | 14 +++---- 3 files changed, 20 insertions(+), 43 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 81aabf5f5..b1628bc1f 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -357,40 +357,19 @@ function devshop_projects_platform_create_form_validate(&$form, &$form_state){ */ function devshop_projects_platform_create_form_submit(&$form, &$form_state){ - global $user; $project_node = node_load($form_state['values']['nid']); - - // Save installation profile to database - db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); - - // Create the site nodes - foreach ($project_node->project_objects['platform'] as $nid => $env){ - $node = new stdClass(); - $node->type = 'site'; - $node->status = 1; - $node->uid = $user->uid; - $node->title = $env .'.'. $project_node->base_url; + print_r($project_node); + print_r($form_state); - // Aegir properties - // @TODO: better client support - $node->client = HOSTING_DEFAULT_CLIENT; - $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); - - $node->platform = $nid; - $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $form_state['values']['install_profile'])); - - //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); - - // @TODO: Improve site language handling? - $node->site_language = !empty($user->language)? $user->language: 'en'; - - // Save the node - if ($node = node_submit($node)) { - node_save($node); - - //And save the association to the project - //INSERT INTO `tesla`.`hosting_devshop_project_object` (`project_nid`, `object_nid`, `object_type`, `env_type`) VALUES ('123', '434', 'ag', 'ag'); - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); - } - } +/* + $args = array( + 'update' => 0, + 'revert' => 0, + 'cache' => 1 + ); +*/ + + $args = array($form_state['values']['platform_name']); + +// hosting_add_task($project_nid, 'devshop-platform-create', $args); } diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 0d609cdfc..c46c2dee2 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -76,7 +76,7 @@ function devshop_projects_hosting_js_page(){ $args = func_get_args(); $nid = &$args[1]; $task = &$args[2]; - + // Get task type from $task argument... For whatver reason, they don't make it an argument list($aegir_context, $task_type) = explode('_', $task); diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 385712369..87f52c622 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -21,11 +21,16 @@ function devshop_projects_hosting_tasks() { $tasks['project']['devshop-platform-create'] = array( 'title' => t('Create Platform'), 'description' => t('Create a new platform to add to an existin project.'), + 'access callback' => _cp_access_callback, ); return $tasks; } +function _cp_access_callback() { + return TRUE; +} + /** * Implements hook_hosting_project_context_options() * @@ -99,12 +104,6 @@ function devshop_projects_hosting_task_table($node) { $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); - $tasklist['devshop-platform-create']['nid'] = $node->nid; - $tasklist['devshop-platform-create']['task_status'] = 1; - $tasklist['devshop-platform-create']['exists'] = 1; - - - // Override some unset($tasklist['devshop-create']['task_permitted']); unset($tasklist['devshop-create']['nid']); @@ -112,8 +111,7 @@ function devshop_projects_hosting_task_table($node) { foreach ($tasklist as $task => $info) { $row = array(); - if (!isset($info['nid']) && !$info['task_permitted'] && - $task != 'devshop-platform-create') { + if (!isset($info['nid']) && !$info['task_permitted']) { // just don't show those tasks, since we'll not be able to run them continue; } From 1241eba1f261b999140e27c94e56e8f5e4b0ecc4 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Wed, 19 Sep 2012 15:16:44 -0600 Subject: [PATCH 0087/3476] Platform create working --- devshop_projects/devshop_projects.form.inc | 19 +----- devshop_projects/devshop_projects.task.inc | 67 ++-------------------- 2 files changed, 6 insertions(+), 80 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index b1628bc1f..77e21eda1 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -314,7 +314,6 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { '#title' => t('Git URL'), '#required' => FALSE, '#description' => t(''), - '#required' => TRUE, '#size' => 40, '#default_value' => '', '#maxlength' => 255, @@ -322,9 +321,7 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { $form['branch'] = array( '#type' => 'textfield', '#title' => t('Branch'), - '#required' => TRUE, '#description' => t(''), - '#required' => TRUE, '#size' => 40, '#default_value' => 'master', '#maxlength' => 255, @@ -358,18 +355,6 @@ function devshop_projects_platform_create_form_validate(&$form, &$form_state){ function devshop_projects_platform_create_form_submit(&$form, &$form_state){ $project_node = node_load($form_state['values']['nid']); - print_r($project_node); - print_r($form_state); - -/* - $args = array( - 'update' => 0, - 'revert' => 0, - 'cache' => 1 - ); -*/ - - $args = array($form_state['values']['platform_name']); - -// hosting_add_task($project_nid, 'devshop-platform-create', $args); + $args = array('platform-name' => $form_state['values']['platform_name']); + hosting_add_task($project_node->nid, 'devshop-platform-create', $args); } diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 87f52c622..6d2194646 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -21,7 +21,7 @@ function devshop_projects_hosting_tasks() { $tasks['project']['devshop-platform-create'] = array( 'title' => t('Create Platform'), 'description' => t('Create a new platform to add to an existin project.'), - 'access callback' => _cp_access_callback, +// 'access callback' => _cp_access_callback, ); return $tasks; @@ -81,12 +81,10 @@ function devshop_projects_hosting_task_table($node) { ); $tasklist = hosting_task_fetch_tasks($node->nid); - //print_r($tasklist); if ($node->project_status != 'sites_ready'){ return; } - // Get tasklists for all sites $tasks = array(); foreach ($node->project_objects['site'] as $nid => $env){ @@ -107,6 +105,9 @@ function devshop_projects_hosting_task_table($node) { // Override some unset($tasklist['devshop-create']['task_permitted']); unset($tasklist['devshop-create']['nid']); + + unset($tasklist['devshop-platform-create']['task_permitted']); + unset($tasklist['devshop-platform-create']['nid']); foreach ($tasklist as $task => $info) { $row = array(); @@ -144,63 +145,3 @@ function devshop_projects_hosting_task_table($node) { return $output; } -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Commit" task. - */ -function hosting_task_devshop_platform_create_form($project_node) { - $form = array(); - - //Bail if no platforms yet. - if (!isset($project_node->project_objects['platform'])){ - $form['note'] = array( - '#type' => 'item', - '#title' => t('Clone & Verify'), - '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), - ); - return $form; - } - - $form['platform_name'] = array( - '#type' => 'textfield', - '#title' => t('Platform Name'), - '#required' => TRUE, - '#description' => t(''), - '#required' => TRUE, - '#size' => 40, - '#default_value' => '', - '#maxlength' => 255, - ); - $form['git_url'] = array( - '#type' => 'textfield', - '#title' => t('Git URL'), - '#required' => FALSE, - '#description' => t(''), - '#required' => TRUE, - '#size' => 40, - '#default_value' => '', - '#maxlength' => 255, - ); - $form['branch'] = array( - '#type' => 'textfield', - '#title' => t('Branch'), - - '#required' => TRUE, - '#description' => t(''), - '#required' => TRUE, - '#size' => 40, - '#default_value' => 'master', - '#maxlength' => 255, - ); - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Create platform'), - ); - return $form; -} - From 4b196dfc21ee5d1791437c4cd7234d5531848a5a Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Wed, 19 Sep 2012 15:51:47 -0600 Subject: [PATCH 0088/3476] Display platform count for each project in the main project view page --- devshop_projects/devshop_projects.node.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 478def3b7..6c6e475b9 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -285,7 +285,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { */ function devshop_projects_projects_view() { - $header = array('Projects', 'Sites'); + $header = array('Projects', 'Platforms', 'Sites'); $r = db_query("SELECT * FROM {hosting_devshop_project}"); $rows = array(); @@ -294,6 +294,7 @@ function devshop_projects_projects_view() { $node = node_load($proj->nid); $row = array(); $row[] = l($node->title, "node/$proj->nid"); + $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='platform'", $node->nid)); $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); $rows[] = $row; } From 5d8fd13763a1ec02e5d207d373c0e4b0d3863641 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 20 Sep 2012 13:50:18 -0600 Subject: [PATCH 0089/3476] Auto create site when a platform is added to a project. --- devshop_projects/devshop_projects.form.inc | 28 +----------- devshop_projects/devshop_projects.module | 37 ++++++++++++++++ devshop_projects/devshop_projects.task.inc | 51 ++++++++++++++++++++++ 3 files changed, 89 insertions(+), 27 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 77e21eda1..76dfc0934 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -250,33 +250,7 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ // Create the site nodes foreach ($project_node->project_objects['platform'] as $nid => $env){ - $node = new stdClass(); - $node->type = 'site'; - $node->status = 1; - $node->uid = $user->uid; - $node->title = $env .'.'. $project_node->base_url; - - // Aegir properties - // @TODO: better client support - $node->client = HOSTING_DEFAULT_CLIENT; - $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); - - $node->platform = $nid; - $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $form_state['values']['install_profile'])); - - //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); - - // @TODO: Improve site language handling? - $node->site_language = !empty($user->language)? $user->language: 'en'; - - // Save the node - if ($node = node_submit($node)) { - node_save($node); - - //And save the association to the project - //INSERT INTO `tesla`.`hosting_devshop_project_object` (`project_nid`, `object_nid`, `object_type`, `env_type`) VALUES ('123', '434', 'ag', 'ag'); - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); - } + devshop_projects_create_site($project_node, node_load($nid), $env); } } diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 2ef73755a..cc96132b9 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -130,3 +130,40 @@ function devshop_projects_hosting_drush_aliases_name($node) { return $node->project_name .".". $node->project_environment; } } + +/* + * Helper function to create a site in a project + */ + +function devshop_projects_create_site($project_node, $platform_node, $env) { + + global $user; + + // Create the site nodes + $node = new stdClass(); + $node->type = 'site'; + $node->status = 1; + $node->uid = $user->uid; + $node->title = $env .'.'. $project_node->base_url; + + // Aegir properties + // @TODO: better client support + $node->client = HOSTING_DEFAULT_CLIENT; + $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); + + $node->platform = $platform_node->nid; + $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); + + //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); + + // @TODO: Improve site language handling? + $node->site_language = !empty($user->language)? $user->language: 'en'; + + // Save the node + if ($node = node_submit($node)) { + node_save($node); + + //And save the association to the project + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); + } +} diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 6d2194646..967da7084 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -145,3 +145,54 @@ function devshop_projects_hosting_task_table($node) { return $output; } +/* + * Implementation of hook_post_hosting_TASK_TYPE_task + * + * If a new platform has been added to a project and the env + * is not dev, test, or live, auto-create a site. + */ + +function devshop_projects_post_hosting_verify_task($task, $data) { + + // We only case about platforms. + if ($task->ref->type != 'platform') { + return; + } + + // Get platform nid. + $nid = $task->ref->nid; + + // Get project object information about this platform + $p_obj = db_fetch_object(db_query("SELECT * " . + "FROM {hosting_devshop_project_object} " . + "WHERE object_nid = %d", $nid)); + + // Not in a project + if (!$p_obj) { + return; + } + + if (in_array($p_obj->env_type, array('dev', 'test', 'live'))) { + return; + } + + // Make sure we don't already have a site + $s_obj = db_fetch_object(db_query("SELECT * " . + "FROM {hosting_devshop_project_object} " . + "WHERE object_type = 'site' AND " . + " env_type = '%s'", + $p_obj->env_type)); + + // See if site exists + if ($s_obj) { + return; + } + + // So this is a platform which is in a project and it not dev, test, or + // live. Let's create a site based off of this platform. + + $platform = node_load($nid); + $project = node_load($p_obj->project_nid); + + devshop_projects_create_site($project, $platform, $p_obj->env_type); +} From eb445eff3076834e97b9a1645691b084b1b34969 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 20 Sep 2012 14:32:48 -0600 Subject: [PATCH 0090/3476] Remove git url and branch for create platform form --- devshop_projects/devshop_projects.form.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 76dfc0934..011484f21 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -258,7 +258,6 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ * Form for platform creation */ function devshop_projects_platform_create_form($form_state, $project_nid) { - //print_r(hosting_task_fetch_tasks($project_nid)); $project_node = node_load($project_nid); $form = array(); @@ -277,12 +276,14 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { '#type' => 'textfield', '#title' => t('Platform Name'), '#required' => TRUE, - '#description' => t(''), + '#description' => t('Enter the name of the platform to be added to project ' . $project_node->title . '. Once the platform is created and verified, a site will be automatically created and added to this project.'), '#required' => TRUE, '#size' => 40, '#default_value' => '', '#maxlength' => 255, ); +/* + Not needed for initial phase $form['git_url'] = array( '#type' => 'textfield', '#title' => t('Git URL'), @@ -300,6 +301,7 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { '#default_value' => 'master', '#maxlength' => 255, ); +*/ $form['nid'] = array( '#type' => 'value', '#value' => $project_node->nid, From bbe7605fecc47b939a9a7c482418e39444db1631 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 21 Sep 2012 14:38:24 -0600 Subject: [PATCH 0091/3476] Remove the Commit Log menu tab from the projects view page and put it back in the node and site page. Also added links from the project view Project Env tabke to the Commit Log page. --- devshop_log/devshop_log.module | 13 +++++-------- devshop_projects/devshop_projects.node.inc | 1 + 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index d60d96808..9cfde0a34 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -27,7 +27,7 @@ function _devsop_log_access_callback($nid) { } $node = node_load($nid); - if (!$node || $node->type != 'project') { + if (!$node || ($node->type != 'site' && $node->type != 'platform')) { return FALSE; } return TRUE; @@ -114,13 +114,10 @@ function devshop_git_log_settings() { */ function _devshop_log_load($nid) { - - $project_node = node_load($nid); - $project_platforms = array_flip($project_node->project_objects['platform']); - - // load the platform node - if (!($node = node_load($project_platforms['dev']))) { - return "Unable to load platform node!"; + + $node = node_load($nid); + if($node->type == 'site') { + $node = node_load($node->platform); } // Run "git log" and capture the output. We need to chdir to the repo diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 6c6e475b9..fcad60610 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -227,6 +227,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = l('Site', "node/$site->nid"); $row[] = l('Platform', "node/$site->platform"); $row[] = l('Download Database', "$url/admin/status/"); + $row[] = l('Commit Log', "node/$site->platform/gitlog"); $rows[] = $row; } $table = theme('table', $header, $rows); From d65a5cd7050684d1a73acd017c4d117bd1c65166 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 21 Sep 2012 15:14:45 -0600 Subject: [PATCH 0092/3476] If site does not have features enabled, don't do revert on pull --- devshop_tasks/devshop_tasks.module | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index b58da1642..e45931f14 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -97,6 +97,7 @@ function hosting_task_devshop_commit_form($node) { function hosting_task_devshop_pull_form($node) { // @TODO: Once there is some kind of site relationship system, // determine if the site has a parent site to sync from, then show this checkbox. + $has_features = _devshop_tasks_site_has_module($node, 'features'); /* $form['sync'] = array( '#title' => t('Sync content from live before code pull?'), @@ -112,8 +113,8 @@ function hosting_task_devshop_pull_form($node) { $form['revert'] = array( '#title' => t('Revert all features after code pull?'), '#type' => 'checkbox', - '#default_value' => 1, - '#access' => _devshop_tasks_site_has_module($node, 'features'), + '#default_value' => $has_features, + '#access' => $has_features, ); $form['cache'] = array( '#title' => t('Clear cache after code pull?'), From b975824efac2ce6484b0ac8ba0efdf2a95d080a3 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Tue, 25 Sep 2012 11:43:53 -0600 Subject: [PATCH 0093/3476] Don't value of 'revert' to FALSE is site does not have features enabled. --- devshop_tasks/devshop_tasks.module | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index e45931f14..521380ede 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -139,7 +139,8 @@ function hosting_task_devshop_sync_form($node) { */ // Get nid $nid = $node->nid; - + $has_features = _devshop_tasks_site_has_module($node, 'features'); + // See if this site is already in a project $query = db_query("SELECT project_nid " . "FROM {hosting_devshop_project_object} " . @@ -189,8 +190,8 @@ function hosting_task_devshop_sync_form($node) { $form['revert'] = array( '#title' => t('Revert all features after content sync?'), '#type' => 'checkbox', - '#default_value' => 1, - '#access' => _devshop_tasks_site_has_module($site_node, 'features'), + '#default_value' => $has_features, + '#access' => $has_features, ); $form['cache'] = array( '#title' => t('Clear cache after content sync?'), From e93bc64a5ccfe3127908124c6e1955611dc867ce Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 27 Sep 2012 18:14:02 -0600 Subject: [PATCH 0094/3476] Added the ability to edit and resubmit a project create that failed --- devshop_projects/devshop_projects.form.inc | 102 ++++++++++++++------- devshop_projects/devshop_projects.node.inc | 18 ++++ devshop_projects/devshop_projects.task.inc | 20 ++++ 3 files changed, 106 insertions(+), 34 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 011484f21..42e816912 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -15,6 +15,11 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ unset($form['revision_information']); unset($form['author']); unset($form['options']); + unset($form['buttons']['delete']); + unset($form['buttons']['preview']); + if ($form['retry']['#value']) { + $form['buttons']['submit']['#value'] = t('Save and Retry'); + } } } @@ -23,6 +28,16 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ */ function devshop_projects_form(&$node) { + $retry = devshop_project_project_create_failed($node->nid, $task); + if ($retry) { + $form['notice'] = array( + '#type' => 'item', + '#title' => t('NOTICE!'), + '#description' => t('Project Create failed! You can view the task log ' . l('here.', "node/$task->nid") . ' Please make any necessary corrections below and resubmit the form to try again.'), + '#weight' => -1, + ); + } + $form['git_url'] = array( '#type' => 'textfield', '#title' => t('Git URL'), @@ -63,15 +78,27 @@ function devshop_projects_form(&$node) { '#maxlength' => 255, '#weight' => 2, ); - + $form['retry'] = array( + '#type' => 'value', + '#value' => $retry, + ); + // Don't allow editing if ($node->nid) { - $form['code_path']['#type'] = $form['title']['#type'] = $form['git_url']['#type'] = 'item'; +// $form['code_path']['#type'] = $form['title']['#type'] = 'item'; $form['code_path']['#value'] = $form['code_path']['#default_value']; $form['title']['#value'] = $form['title']['#default_value']; - $form['git_url']['#value'] = $form['git_url']['#default_value']; + + $form['code_path']['#attributes'] = array('readonly' => 'readonly'); + $form['title']['#attributes'] = array('readonly' => 'readonly'); + + if (!$retry) { +// $form['git_url']['#type'] = 'item'; + $form['git_url']['#attributes'] = array('readonly' => 'readonly'); + $form['git_url']['#value'] = $form['git_url']['#default_value']; + } } - + return $form; } @@ -79,6 +106,7 @@ function devshop_projects_form(&$node) { * Implementation of hook_validate(). */ function devshop_projects_validate($node, &$form) { + // No validation if op == Delete if ($node->op == t('Delete')) { return; @@ -93,41 +121,25 @@ function devshop_projects_validate($node, &$form) { } // The project code name must not be in the hosting_context table - if ($result = db_fetch_object(db_query("SELECT name FROM {hosting_context} WHERE name = '%s'", $node->title))) { + if (!$node->retry && ($result = db_fetch_object(db_query("SELECT name FROM {hosting_context} WHERE name = '%s'", $node->title)))) { form_set_error('title', t('Project code name existing in hosting context table.')); } // The project code name must be unique - if ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid))) { + if (!$node->retry && ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid)))) { form_set_error('title', t('Project code name is already is use by another project')); } // Make sure the path is unique. $cp = hosting_path_normalize($node->code_path); - if ($add && $result = db_fetch_object(db_query("SELECT code_path FROM {hosting_devshop_project} WHERE code_path = '%s' AND nid <> %d", $cp, $node->nid))) { + if (!$node->retry && $add && $result = db_fetch_object(db_query("SELECT code_path FROM {hosting_devshop_project} WHERE code_path = '%s' AND nid <> %d", $cp, $node->nid))) { form_set_error('code_path', t('Code path is already in use by another project')); } // Directory must not exist - if ($add && file_exists($cp)) { + if (!$node->retry && $add && file_exists($cp)) { form_set_error('code_path', t('Code path directory already exists.')); } - - // Sites may not be used in more than one environement - if($node->dev_site != 0 && $node->dev_site == $node->test_site) { - form_set_error('dev_site', t('Site used more than once.')); - form_set_error('test_site', t('Site used more than once.')); - } - - if($node->dev_site != 0 && $node->dev_site == $node->live_site) { - form_set_error('dev_site', t('Site used more than once.')); - form_set_error('live_site', t('Site used more than once.')); - } - - if($node->test_site != 0 && $node->test_site == $node->live_site) { - form_set_error('test_site', t('Site used more than once.')); - form_set_error('live_site', t('Site used more than once.')); - } } /** @@ -166,11 +178,22 @@ function devshop_projects_install_sites_form($form_state, $project_node) { // OUTPUT if ($platforms_ready == FALSE){ - $form['note'] = array( - '#type' => 'item', - '#title' => t('Clone & Verify'), - '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), - ); + $retry = devshop_project_project_create_failed($project_node->nid, $task); + if ($retry) { + $form['note'] = array( + '#type' => 'item', + '#title' => t('NOTICE!'), + '#description' => t('Project Create failed! You can ' . l('view the task log here.', "node/$task->nid") . ' Please ' . l('edit the project settings', "node/$project_node->nid/edit") . ' and make any necessary corrections and resubmit the form to try again.'), + '#weight' => -1, + ); + } + else { + $form['note'] = array( + '#type' => 'item', + '#title' => t('Clone & Verify'), + '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + ); + } return $form; } elseif ($sites_installing) { $form['note'] = array( @@ -264,14 +287,25 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { //Bail if no platforms yet. if (!isset($project_node->project_objects['platform'])){ - $form['note'] = array( - '#type' => 'item', - '#title' => t('Clone & Verify'), - '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + $retry = devshop_project_project_create_failed($project_nid, $task); + if ($retry) { + $form['note'] = array( + '#type' => 'item', + '#title' => t('NOTICE!'), + '#description' => t('Project Create failed! You can ' . l('view the task log here.', "node/$task->nid") . ' Please ' . l('edit the project settings', "node/$project_nid/edit") . ' and make any necessary corrections and resubmit the form to try again.'), + '#weight' => -1, ); + } + else { + $form['note'] = array( + '#type' => 'item', + '#title' => t('Clone & Verify'), + '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), + ); + } return $form; } - // ZZZ + $form['platform_name'] = array( '#type' => 'textfield', '#title' => t('Platform Name'), diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index fcad60610..9514b75cf 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -103,6 +103,24 @@ function devshop_projects_insert($node) { hosting_add_task($node->nid, 'devshop-create'); } +/** + * Implementation of hook_update(). + */ +function devshop_projects_update($node) { + + db_query("UPDATE {hosting_devshop_project} " . + "SET git_url = '%s', code_path = '%s', base_url = '%s' " . + "WHERE nid = %d", + $node->git_url, hosting_path_normalize($node->code_path), + $node->base_url, $node->nid); + + // Pass retry flag + $args = array('retry' => $node->retry ? "1" : "0"); + + // Create hostmaster task + hosting_add_task($node->nid, 'devshop-create', $args); +} + /** * Implementation of hook_delete(). */ diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 967da7084..c4cdfcfd6 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -44,6 +44,7 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['base_url'] = $task->ref->base_url; $task->context_options['code_path'] = trim($task->ref->code_path, " "); $task->context_options['git_url'] = $task->ref->git_url; + $task->context_options['retry'] = $task->ref->retry; } /** @@ -196,3 +197,22 @@ function devshop_projects_post_hosting_verify_task($task, $data) { devshop_projects_create_site($project, $platform, $p_obj->env_type); } + +/* + * Returns TRUE is the project create task for the given node failed. + */ + +function devshop_project_project_create_failed($nid, &$task) { + + if ($nid) { + $task = hosting_get_most_recent_task($nid, 'devshop-create'); + if ($task && $task->task_status != 1) { + // Project Create failed. + return TRUE; + } + } + + // No task found OR it is successful + return FALSE; +} + From 0c106250737eca48634baaef351911881552fc5c Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 28 Sep 2012 11:47:43 -0600 Subject: [PATCH 0095/3476] Added delete project task --- devshop_projects/devshop_projects.task.inc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index c4cdfcfd6..2b95f8c89 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -14,6 +14,13 @@ function devshop_projects_hosting_tasks() { 'description' => t('Clones the repo, and creates the platforms.'), 'provision_save' => TRUE, ); + $tasks['project']['devshop-delete'] = array( + 'title' => t('Delete Project'), + 'description' => t('Delete a project and all associated sites and platforms.'), +// 'provision_save' => TRUE, + 'access callback' => _cp_access_callback, + + ); $tasks['project']['devshop-install'] = array( 'title' => t('Install Sites'), 'description' => t('Runs drupal installation on each of the three platforms.'), @@ -21,7 +28,6 @@ function devshop_projects_hosting_tasks() { $tasks['project']['devshop-platform-create'] = array( 'title' => t('Create Platform'), 'description' => t('Create a new platform to add to an existin project.'), -// 'access callback' => _cp_access_callback, ); return $tasks; From 7fff617ad668b64babad5b9dcec21508269832b2 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 28 Sep 2012 14:55:58 -0600 Subject: [PATCH 0096/3476] Use confirm for for devshop delete --- devshop_projects/devshop_projects.task.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 2b95f8c89..8f66b413e 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -18,7 +18,8 @@ function devshop_projects_hosting_tasks() { 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), // 'provision_save' => TRUE, - 'access callback' => _cp_access_callback, + 'dialog' => TRUE, + 'access callback' => _cp_access_callback, ); $tasks['project']['devshop-install'] = array( From e4bd28cb563fb242be36f373f34f4b093f202a2f Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 28 Sep 2012 18:45:03 -0600 Subject: [PATCH 0097/3476] Project delete check point check in, ergo: not working yet --- devshop_projects/devshop_projects.module | 9 ++++ devshop_projects/devshop_projects.node.inc | 3 +- devshop_projects/devshop_projects.task.inc | 54 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index cc96132b9..9a7c913d8 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -40,6 +40,15 @@ function devshop_projects_menu() { 'access arguments' => array('view projects'), ); + $items['hosting/projects/delete/%'] = array( + 'title' => t('Delete Project'), + 'description' => 'Delete a project and all of it\'s associated sites and platforms', + //'page callback' => 'devshop_projects_platform_create', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('devshop_projects_delete_form', 3), + 'access arguments' => array('delete projects'), + ); + return ($items); } diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 9514b75cf..05bb2485d 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -304,7 +304,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { */ function devshop_projects_projects_view() { - $header = array('Projects', 'Platforms', 'Sites'); + $header = array('Projects', 'Platforms', 'Sites', 'Delete?'); $r = db_query("SELECT * FROM {hosting_devshop_project}"); $rows = array(); @@ -315,6 +315,7 @@ function devshop_projects_projects_view() { $row[] = l($node->title, "node/$proj->nid"); $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='platform'", $node->nid)); $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); + $row[] = l("Delete Project", "hosting/project/delete/$proj->nid"); $rows[] = $row; } diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 8f66b413e..611af96ae 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -205,6 +205,60 @@ function devshop_projects_post_hosting_verify_task($task, $data) { devshop_projects_create_site($project, $platform, $p_obj->env_type); } +/* + * Implementation of hook_post_hosting_TASK_TYPE_task + * + * This hook is invoked when a project is deleted. We then: + * + * 1) Disable all sites + * 2) Delete all sites + * 3) Delete all platforms + * 4) Set project node status to 0 + * + */ + +function devshop_projects_post_hosting_devshop_delete_task($task, $data) { + + // We only case about projects. + if ($task->ref->type != 'project') { + watchdog('devshop', "Project delete post task hook called, but type is $task->ref->type"); + return; + } + + // Get project nid. + $nid = $task->ref->nid; + + // Make sure it's a real project + $p_info = db_fetch_object(db_query("SELECT * " . + "FROM {hosting_devshop_project} " . + "WHERE nid = %d", $nid)); + if (!$p_info) { + watchdog('devshop', "Project delete post task hook: project nid $nid not fund"); + return; + } + + // First disable and delete all of the sites + + $query = db_query("SELECT * " . + "FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d AND object_type = 'site'", $nid); + + while ($site = db_fetch_object($query)) { + hosting_add_task($site->nid, 'disable'); + hosting_add_task($site->nid, 'delete'); + } + + // Now delete all of the platforms + $query = db_query("SELECT * " . + "FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d AND object_type = 'platform'", + $nid); + + while ($site = db_fetch_object($query)) { + hosting_add_task($site->nid, 'delete'); + } +} + /* * Returns TRUE is the project create task for the given node failed. */ From 47b0eed16068f3a3600ae0154dbc7da5418c1ebe Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Tue, 2 Oct 2012 16:56:29 -0600 Subject: [PATCH 0098/3476] Project delete check point check in --- devshop_projects/devshop_projects.form.inc | 61 +++++++++++++++++++++- devshop_projects/devshop_projects.module | 7 ++- devshop_projects/devshop_projects.node.inc | 9 ++-- devshop_projects/devshop_projects.task.inc | 13 ++--- 4 files changed, 76 insertions(+), 14 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 42e816912..70edac060 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -267,7 +267,9 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ global $user; $project_node = node_load($form_state['values']['nid']); - + + $project_node->install_profile = $form_state['values']['install_profile']; + // Save installation profile to database db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); @@ -368,3 +370,60 @@ function devshop_projects_platform_create_form_submit(&$form, &$form_state){ $args = array('platform-name' => $form_state['values']['platform_name']); hosting_add_task($project_node->nid, 'devshop-platform-create', $args); } + +/** + * Form for project delete + */ +function devshop_projects_project_delete_form($form_state, $project_nid) { + $project_node = node_load($project_nid); + + $form = devshop_projects_view($project_node)->content; + + unset($form['sites']['add_platform']); + unset($form['tasks_view']); + unset($form['devshop']); + unset($form['info']['#prefix']); + unset($form['info']['#suffix']); + + $form['message'] = array( + '#type' => 'item', + '#value' => t('Are you sure you want to delete this project and all of its associated sites and platforms?'), + '#weight' => 31, + ); + + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_nid, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Delete Project'), + '#weight' => 32, + ); + + $form['cancel'] = array( + '#type' => 'submit', + '#value' => t('Cancel'), + '#weight' => 33, + ); + + return $form; +} + +/** + * Form for project delete submit handler + */ +function devshop_projects_project_delete_form_submit(&$form, &$form_state){ + + // Make sure the user really wants to delete the project + if ($form_state['clicked_button']['#value'] == t('Delete Project')) { + hosting_add_task($form['nid']['#value'], 'delete'); + } + $form_state['redirect'] = 'hosting/projects'; + drupal_goto('hosting/projects'); +} + + + + diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9a7c913d8..a9cf55551 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -16,6 +16,7 @@ include_once('devshop_projects.task.inc'); function devshop_projects_perm() { return array( 'view projects', + 'delete project' ); } @@ -34,7 +35,6 @@ function devshop_projects_menu() { $items['hosting/projects/platform/create/%'] = array( 'title' => 'Create and add a platform', 'description' => 'Create and add a platform to an existing project', - //'page callback' => 'devshop_projects_platform_create', 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_projects_platform_create_form', 4), 'access arguments' => array('view projects'), @@ -43,9 +43,8 @@ function devshop_projects_menu() { $items['hosting/projects/delete/%'] = array( 'title' => t('Delete Project'), 'description' => 'Delete a project and all of it\'s associated sites and platforms', - //'page callback' => 'devshop_projects_platform_create', 'page callback' => 'drupal_get_form', - 'page arguments' => array('devshop_projects_delete_form', 3), + 'page arguments' => array('devshop_projects_project_delete_form', 3), 'access arguments' => array('delete projects'), ); @@ -167,7 +166,7 @@ function devshop_projects_create_site($project_node, $platform_node, $env) { // @TODO: Improve site language handling? $node->site_language = !empty($user->language)? $user->language: 'en'; - + // Save the node if ($node = node_submit($node)) { node_save($node); diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 05bb2485d..1b189fa1e 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -92,7 +92,6 @@ function devshop_projects_insert($node) { db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url) ". "VALUES (%d, '%s', '%s', '%s')", $node->nid, $node->git_url, hosting_path_normalize($node->code_path), $node->base_url); -// _devshop_projects_insert_sites($node); // Save hosting context if ((!$node->old_vid)) { @@ -108,6 +107,8 @@ function devshop_projects_insert($node) { */ function devshop_projects_update($node) { +dpm("Update hook called"); +dpm($node); db_query("UPDATE {hosting_devshop_project} " . "SET git_url = '%s', code_path = '%s', base_url = '%s' " . "WHERE nid = %d", @@ -118,13 +119,15 @@ function devshop_projects_update($node) { $args = array('retry' => $node->retry ? "1" : "0"); // Create hostmaster task - hosting_add_task($node->nid, 'devshop-create', $args); + //hosting_add_task($node->nid, 'devshop-create', $args); } /** * Implementation of hook_delete(). */ function devshop_projects_delete($node) { +dpm("Delete hook called"); +dpm($node); db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); @@ -315,7 +318,7 @@ function devshop_projects_projects_view() { $row[] = l($node->title, "node/$proj->nid"); $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='platform'", $node->nid)); $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); - $row[] = l("Delete Project", "hosting/project/delete/$proj->nid"); + $row[] = l("Delete Project", "hosting/projects/delete/$proj->nid"); $rows[] = $row; } diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 611af96ae..8368e5c37 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -14,7 +14,7 @@ function devshop_projects_hosting_tasks() { 'description' => t('Clones the repo, and creates the platforms.'), 'provision_save' => TRUE, ); - $tasks['project']['devshop-delete'] = array( + $tasks['project']['delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), // 'provision_save' => TRUE, @@ -217,8 +217,9 @@ function devshop_projects_post_hosting_verify_task($task, $data) { * */ -function devshop_projects_post_hosting_devshop_delete_task($task, $data) { +function devshop_projects_post_hosting_delete_task($task, $data) { + watchdog('devshop', "Project delete post task hook called"); // We only case about projects. if ($task->ref->type != 'project') { watchdog('devshop', "Project delete post task hook called, but type is $task->ref->type"); @@ -244,8 +245,8 @@ function devshop_projects_post_hosting_devshop_delete_task($task, $data) { "WHERE project_nid = %d AND object_type = 'site'", $nid); while ($site = db_fetch_object($query)) { - hosting_add_task($site->nid, 'disable'); - hosting_add_task($site->nid, 'delete'); + hosting_add_task($site->object_nid, 'disable'); + hosting_add_task($site->object_nid, 'delete'); } // Now delete all of the platforms @@ -254,8 +255,8 @@ function devshop_projects_post_hosting_devshop_delete_task($task, $data) { "WHERE project_nid = %d AND object_type = 'platform'", $nid); - while ($site = db_fetch_object($query)) { - hosting_add_task($site->nid, 'delete'); + while ($platform = db_fetch_object($query)) { + hosting_add_task($platform->object_nid, 'delete'); } } From 860fce8ba5751b0f99c2bbf0c13cebf870c3d31d Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Thu, 4 Oct 2012 16:45:29 +0200 Subject: [PATCH 0099/3476] Jacinto. Add new option for hard reset --- devshop_pull/devshop_pull.inc | 29 +++++----- devshop_pull/devshop_pull.install | 25 ++++++-- devshop_pull/devshop_pull.module | 94 ++++++++++++++++++++++--------- 3 files changed, 99 insertions(+), 49 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 7936f4322..1ac83f562 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -66,24 +66,21 @@ function devshop_pull_callback($project, $hash) { return; } - //Search dev platform of this project + //Search site with pull enable of this project $platforms = $pnode->project_objects['platform']; - - $platform = array_search('dev', $platforms); - - // Iterate through all of the sites that use platform dev - $r = db_query("SELECT nid FROM {hosting_site} " . - "WHERE platform = %d AND status = 1", $platform); - + //Put timestamp - db_query('UPDATE {hosting_devshop_pull} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); - - while ($site = db_fetch_object($r)) { - $message = "Queuing a pull task for node id $site->nid"; - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); - print "$message
"; - devshop_pull_task($site->nid); - } + db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); + + foreach ($platforms as $site_nid => $platform) { + //If pull enabled then add to task + if (db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid))) { + $message = "Queuing a pull task for node id $site_nid"; + watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); + print "$message
"; + devshop_pull_task($site_nid); + } + } } /** diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index bd0546b33..3b4e28914 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -8,7 +8,7 @@ * Implementation of hook_schema(). */ function devshop_pull_schema() { - $schema['hosting_devshop_pull'] = array( + $schema['hosting_devshop_pull_projects'] = array( 'fields' => array( 'project_nid' => array( 'type' => 'int', @@ -20,23 +20,38 @@ function devshop_pull_schema() { 'not null' => TRUE, 'default' => 0, ), - 'pull_reset' => array( + 'last_pull' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), - 'last_pull' => array( + 'last_pull_status' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), - 'last_pull_status' => array( + ), + 'primary key' => array('project_nid'), + ); + $schema['hosting_devshop_pull_sites'] = array( + 'fields' => array( + 'site_nid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'pull_enabled' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'pull_reset' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), ), - 'primary key' => array('project_nid'), + 'primary key' => array('site_nid'), ); return $schema; diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 410d4f5be..dbd2e966f 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -84,12 +84,29 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id){ ), ); - $form['pull_reset'] = array( - '#title' => 'Hard Reset on Pull', - '#type' => 'checkbox', - '#description' => t('Reset any changes to project files. WARNING: Any uncommitted changes to the project files will be discarded.'), - '#default_value' => $node->pull_reset, - ); + } + if ($form_id == 'site_node_form') { + $site = $form['#node']; + if (isset($site->project_nid)) { + if ($site->nid) { + $default_value = $site->pull_enabled; + } + else { + $default_value = in_array($site->project_environment, array('live', 'test')) ? TRUE : FALSE; + } + $form['pull_enabled'] = array( + '#title' => t('Pull on Commit'), + '#description' => t('If enabled. When detecting new code changes will be updated automatically.'), + '#type' => 'checkbox', + '#default_value' => $default_value, + ); + $form['pull_reset'] = array( + '#title' => 'Hard Reset on Pull', + '#type' => 'checkbox', + '#description' => t('Reset any changes to project files. WARNING: Any uncommitted changes to the project files will be discarded.'), + '#default_value' => $site->pull_reset, + ); + } } } @@ -137,7 +154,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { '#type' => 'item', '#title' => t('Reset on Pull'), '#weight' => 31, - '#value' => $pnode->pull_reset? t('Enabled'): t('Disabled'), + '#value' => $node->pull_reset? t('Enabled'): t('Disabled'), ); $node->content['devshop_pull']['last_pull'] = array( '#type' => 'item', @@ -156,20 +173,30 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { break; case 'load': if ($node->type == 'project') { - $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull} WHERE project_nid = %d', $node->nid)); + $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); if (!empty($data->project_nid)){ $node->pull_method = $data->pull_method; - $node->pull_reset = $data->pull_reset; $node->last_pull = $data->last_pull; $node->last_pull_status = $data->last_pull_status; } } + elseif ($node->type == 'site') { + $data_project = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); + if (isset($data_project['project_nid'])) { + $data = db_fetch_array(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_sites} WHERE site_nid = %d", $node->nid)); + return $data; + } + } break; case 'insert': case 'update': if ($node->type == 'project') { - db_query('DELETE FROM {hosting_devshop_pull} WHERE project_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull} (project_nid, pull_method, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_method, $node->pull_reset); + db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); + } + elseif ($node->type == 'site') { + db_query('DELETE FROM {hosting_devshop_pull_sites} WHERE site_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_sites} (site_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_enabled, $node->pull_reset); } break; } @@ -187,7 +214,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { * @TODO Convert to check pull queue sites. */ function devshop_pull_get_sites($limit = 5) { - $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); + $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); return db_result($result); } @@ -198,7 +225,7 @@ function devshop_pull_get_sites($limit = 5) { function devshop_pull_hosting_devshop_pull_task_rollback($task, $data) { $project = node_load($task->ref->project_nid); watchdog('devshop', 'Pull FAILED on project: @alias', array('@alias' => $project->title)); - db_query('UPDATE {hosting_devshop_pull} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $task->ref->project_nid); + db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $task->ref->project_nid); } /** @@ -214,7 +241,7 @@ function devshop_pull_post_hosting_devshop_pull_task($task, $data) { */ function hosting_pull_queue($count) { - $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); + $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); while ($project = db_fetch_object($result)) { @@ -232,31 +259,42 @@ function devshop_pull_task($nid){ $node = node_load($nid); $nids = array(); + $args = array(); + if ($node->type == 'site'){ $nids[] = $node->nid; + + // When doing the pull task, don't do a db update or revert. + // Just clear all of the caches + $args[$node->nid] = array( + 'reset' => $node->pull_reset, + 'update' => 0, + 'rever' => 0, + 'cache' => 1 + ); } else { - //Search dev platform of this project + //Search site with pull enabled of this project $platforms = $node->project_objects['platform']; - $platform = array_search('dev', $platforms); - $r = db_query("SELECT nid FROM {hosting_site} " . - "WHERE platform = %d AND status = 1", $platform); - while ($site = db_fetch_object($r)) { - $nids[] = $site->nid; + foreach ($platforms as $site_nid => $platform) { + //If pull enabled then add to task + $data = db_fetch_object(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid)); + if ($data->pull_enabled) { + $nids[] = $site_nid; + $args[$node->nid] = array( + 'reset' => $data->pull_reset, + 'update' => 0, + 'rever' => 0, + 'cache' => 1 + ); + } } } - // When doing the pull task, don't do a db update or revert. - // Just clear all of the caches - $args = array( - 'update' => 0, - 'revert' => 0, - 'cache' => 1 - ); foreach ($nids as $nid){ - hosting_add_task($nid, 'devshop-pull', $args); + hosting_add_task($nid, 'devshop-pull', $args[$nid]); } } From 3dc12facce5cfe255054f592e1eec44544f024ba Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 4 Oct 2012 22:29:05 -0600 Subject: [PATCH 0100/3476] Project delete check point check-in. Coding complete, some debugging left to do, --- devshop_projects/devshop_projects.form.inc | 54 ++++++ devshop_projects/devshop_projects.module | 1 + devshop_projects/devshop_projects.node.inc | 10 +- devshop_projects/devshop_projects.task.inc | 212 +++++++++++++++++---- 4 files changed, 239 insertions(+), 38 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 70edac060..847791b50 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -418,8 +418,62 @@ function devshop_projects_project_delete_form_submit(&$form, &$form_state){ // Make sure the user really wants to delete the project if ($form_state['clicked_button']['#value'] == t('Delete Project')) { + $node = node_load($form['nid']['#value']); + if ($node->data) { + $data = $node->data; + } + else { + $data = array(); + } + + // First get a list of all of the objects for this project + $query = db_query("SELECT * " . + "FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d", $node->nid); + + $snid = array(); + $pnid = array(); + + while ($obj = db_fetch_object($query)) { + if ($obj->object_type == 'site') { + $snid[] = $obj->object_nid; + } + else if ($obj->object_type == 'platform') { + $pnid[] = $obj->object_nid; + } + } + + if (count($snid) > 0) { + $data['project_delete_site_disable'] = $snid; + $data['project_delete_site_delete'] = $snid; + } + else { + unset($data['project_delete_sites_disable']); + unset($data['project_delete_sites_delete']); + } + + if (count($pnid) > 0) { + $data['project_delete_platform_delete'] = $snid; + } + else { + unset($data['project_delete_platform_delete']); + } + + if ($data['project_delete_platform_delete'] || + $data['project_delete_site_disable'] || + $data['project_delete_site_delete'] ) { + $data['deleting_project'] = TRUE; + } + else { + unset($data['deleting_project']); + } + + $node->data = $data; + node_save($node); hosting_add_task($form['nid']['#value'], 'delete'); } + + // Go back to the main project list page $form_state['redirect'] = 'hosting/projects'; drupal_goto('hosting/projects'); } diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a9cf55551..6a986faa5 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -175,3 +175,4 @@ function devshop_projects_create_site($project_node, $platform_node, $env) { db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); } } + diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 1b189fa1e..8263b5519 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -115,11 +115,13 @@ dpm($node); $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, $node->nid); - // Pass retry flag - $args = array('retry' => $node->retry ? "1" : "0"); + // If this is a rerry, kick start another devshop-create + if ($node->retry) { + $args = array('retry' => 1); - // Create hostmaster task - //hosting_add_task($node->nid, 'devshop-create', $args); + // Create hostmaster task + hosting_add_task($node->nid, 'devshop-create', $args); + } } /** diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 8368e5c37..2f0e7ea47 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -206,58 +206,202 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } /* - * Implementation of hook_post_hosting_TASK_TYPE_task + * This function checks to see if there if there are any more objects + * to disable or delete during the 'project delete' task. The list of + * tasks that need to be done are kept in $project_node->data. See the + * following two function for more detail on those arrays: * - * This hook is invoked when a project is deleted. We then: + * devshop_projects_project_delete_form_submit + * devshop_projects_project_delete_continue * - * 1) Disable all sites - * 2) Delete all sites - * 3) Delete all platforms - * 4) Set project node status to 0 + * @param $pnode + * The project node that is to be deleted. * */ -function devshop_projects_post_hosting_delete_task($task, $data) { +function devshop_projects_project_delete_queue_next_task($pnode) { - watchdog('devshop', "Project delete post task hook called"); - // We only case about projects. - if ($task->ref->type != 'project') { - watchdog('devshop', "Project delete post task hook called, but type is $task->ref->type"); - return; + // Assume all done + $node_updated = FALSE; + $nid = FALSE; + + // Keys to tasks to be performed. Objects must be processed in this + // order due to task dependencies. + $keys = array('site' => 'disable', + 'site' => 'delete', + 'platform' => 'delete'); + + // Iterate through arrays of tasks => nids and queue one tasks that + // is left to do + + foreach ($keys as $type => $task) { + $key1 = 'project_delete_{$type}_{$task}'; + watchdog('devshop', "key1 = $key1"); //zzz + + if (isset($pnode->data[$key1])) { + if (count($pnode->data[$key1]) > 0) { + // Use the first nid in the list and stop looking for any more work + $nid = $pnode->data[$key1][0]; + break; + } + else { + watchdog('devshop', "key1 = $key1 COUNT IS ZERO!"); //zzz + } + + } + else { + watchdog('devshop', "key1 = $key1 is NOT SET!"); //zzz + } + watchdog('devshop', "unsetting key $key1 XXX"); //zzz + unset($pnode->data[$key1]); + $node_updated = TRUE; } - // Get project nid. - $nid = $task->ref->nid; + // If $nid != FALSE, then we need to act on that node. If it is FALSE, + // then we are all done. We need to set the project node status + // field to 0 and delete that row from the hosting_devshop_project + // table + + if ($nid) { + watchdog('devshop', "Project delete: scheduling taskk '%s' for nid $nid"); + hosting_add_task($nid, $task); + } + + // If we changed the project node in any way, we need to save the changes + if ($node_updated) { + watchdog('devshop', "updating node. status = {$node->status}"); //zzz + // watchdog('devshop', "updating node. status = ${node->status}"); //zzz + node_save($pnode); + } + + if (!$nid) { + watchdog('devshop', "Project delete: complete!"); + $pnode->status = 0; + + //$pnode->rid = 0; + $node_updated = TRUE; + + // delete this project + watchdog('devshop', "deleting project from hosting_devshop_project"); //zzz + db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", + $pnode->nid); + } + +} + +/* + * This function get called when we are in the process of deleting a + * project and all of its related sites & platforms. It is invoked + * by the one of the hook_post_hosting_TASK_TYPE_task() hook when + * a site disable or site/platform delete tasks complete. + * + * All of the sites that we need to disable and delete and all of the + * platforms we need to delete are kept in the project node's + * $node->data field. There are four elements in that array: + * + * + * $project_node-> data['deleting_project']: if TRUE, we are in the process + * of delete this project and all sites & platforms. + * $project_node->data['project_delete_site_disable']: array of site NID's + * that are to be disabled. + * $project_node->data['project_delete_site_delete']: an array of site NID's + * that are to be deleted once they are disabled. + * $project_node->data['project_delete_platform_delete']: an array of platform + * NID's that are to be deleted once the sites based on these platforms + * are disabled and deleted. + * + * We first disable all of the sites, then we delete them. Next we delete + * all of the platforms. As soon as a site or platform is deleted, we remove + * the NID from the corresponding array. When everything is deleted, we + * remove the rows from the object table. Hostmaster will remove them from + * the hosting context table during the delete task. + * + * @param $nid + * The NID of the object which the task just completed on. It can be + * the project node (when the actual project is being deleted). The + * site site node (when the site was disabled or deleted), or the + * platform node (when the platform was deleted). + * @param $op + * The operation (task type) that just complted. Will be one of: + * 'project delete', 'site disable', 'site delete', or + * 'platform delete'. + */ +function devshop_projects_project_delete_continue($nid, $op) { - // Make sure it's a real project - $p_info = db_fetch_object(db_query("SELECT * " . - "FROM {hosting_devshop_project} " . - "WHERE nid = %d", $nid)); - if (!$p_info) { - watchdog('devshop', "Project delete post task hook: project nid $nid not fund"); + if (!($node = node_load($nid))) { + watchdog('devshop', "Delete continue: nid $nid not found"); return; } - // First disable and delete all of the sites + if ($node->type == 'project') { + devshop_projects_project_delete_queue_next_task($node); + return; + } + else if (($node->type != 'site') && + ($node->type == 'platform')) { + // We only care about sites and platforms + return; + } - $query = db_query("SELECT * " . - "FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d AND object_type = 'site'", $nid); + // Does this object belong to a devshop project? If not, + // then just ignore it. + if (!($node->project_nid)) { + return; + } - while ($site = db_fetch_object($query)) { - hosting_add_task($site->object_nid, 'disable'); - hosting_add_task($site->object_nid, 'delete'); + // load project node + if (!($project_node = node_load($node->project_id))) { + watchdog('devshop', + "Delete continue: can't load project nid $node->project_nid"); + return; } - // Now delete all of the platforms - $query = db_query("SELECT * " . - "FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d AND object_type = 'platform'", - $nid); + if (!$project_node->data['deleting_project']) { + watchdog('devshop', + "Delete continue: delete_project flag not TRUE"); + return; + } + + // delete this object + db_query("DELETE FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d AND object_nid = %d", + $project_node->nid, $node->nid); + + // Remove this nid from our array of things to do + $key1 = 'project_delete_{$node->type}_{$op}'; + $key2 = array_search($node->nid, $project_node->data[$key1]); - while ($platform = db_fetch_object($query)) { - hosting_add_task($platform->object_nid, 'delete'); + if ($key2 != FALSE) { + unset($project_node->data[$key1][$key2]); + if (count($project_node->data[$key1][$key2]) == 0) { + unset($project_node->data[$key1]); + } + node_save($project_node); } + + // do the next delete task + devshop_projects_project_delete_queue_next_task($project_node); +} + +/* + * Implementation of hook_post_hosting_TASK_TYPE_task + * + */ + +function devshop_projects_post_hosting_delete_task($task, $data) { + watchdog('devshop', "Delete post task hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'delete'); + +} + +/* + * Implementation of hook_post_hosting_TASK_TYPE_task + * + */ + +function devshop_projects_post_hosting_disable_task($task, $data) { + watchdog('devshop', "Disablw post task hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'disable'); } /* From a420d9cdaee0f2ea49e2206ddf16f910ed8c60f8 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Sat, 6 Oct 2012 20:46:19 -0600 Subject: [PATCH 0101/3476] Project delete working! Some sanity checks and clean up still needs to be done. --- devshop_projects/devshop_projects.form.inc | 43 +++--- devshop_projects/devshop_projects.install | 7 + devshop_projects/devshop_projects.module | 29 ++++ devshop_projects/devshop_projects.node.inc | 5 +- devshop_projects/devshop_projects.task.inc | 149 +++++++++++++-------- 5 files changed, 159 insertions(+), 74 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 847791b50..3cbec3ca1 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -8,7 +8,7 @@ * Implements hook_form_alter(). */ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ - + // Removing unneccesary fieldgroups if ($form_id == 'project_node_form'){ unset($form['menu']); @@ -385,9 +385,17 @@ function devshop_projects_project_delete_form($form_state, $project_nid) { unset($form['info']['#prefix']); unset($form['info']['#suffix']); + $form['remove_dir'] = array( + '#type' => 'checkbox', + '#title' => t('Remove all project files and directories.'), + '#description' => t("Note that the platform & site directories are removed regardless of this option. By choosing this option, everything else in the directory '{$project_node->code_path}', including the directory itself, will be removed."), + '#default_value' => FALSE, + '#weight' => 30, + ); + $form['message'] = array( '#type' => 'item', - '#value' => t('Are you sure you want to delete this project and all of its associated sites and platforms?'), + '#value' => t("Are you sure you want to delete project '{$project_node->title}' and all of its associated sites and platforms?"), '#weight' => 31, ); @@ -418,18 +426,13 @@ function devshop_projects_project_delete_form_submit(&$form, &$form_state){ // Make sure the user really wants to delete the project if ($form_state['clicked_button']['#value'] == t('Delete Project')) { - $node = node_load($form['nid']['#value']); - if ($node->data) { - $data = $node->data; - } - else { - $data = array(); - } + $nid = $form['nid']['#value']; + $data = devshop_projects_project_data_get($nid); // First get a list of all of the objects for this project $query = db_query("SELECT * " . "FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d", $node->nid); + "WHERE project_nid = %d", $nid); $snid = array(); $pnid = array(); @@ -448,12 +451,12 @@ function devshop_projects_project_delete_form_submit(&$form, &$form_state){ $data['project_delete_site_delete'] = $snid; } else { - unset($data['project_delete_sites_disable']); - unset($data['project_delete_sites_delete']); + unset($data['project_delete_site_disable']); + unset($data['project_delete_site_delete']); } if (count($pnid) > 0) { - $data['project_delete_platform_delete'] = $snid; + $data['project_delete_platform_delete'] = $pnid; } else { unset($data['project_delete_platform_delete']); @@ -463,14 +466,20 @@ function devshop_projects_project_delete_form_submit(&$form, &$form_state){ $data['project_delete_site_disable'] || $data['project_delete_site_delete'] ) { $data['deleting_project'] = TRUE; + if($form_state['values']['remove_dir']) { + $data['deleting_project_remove_dir'] = TRUE; + } } else { unset($data['deleting_project']); } - - $node->data = $data; - node_save($node); - hosting_add_task($form['nid']['#value'], 'delete'); + + // Save the delete task and nid lists. We're going to need them + // when the hosting post task hook gets invoked + devshop_projects_project_data_set($nid, $data); + + // Kick the whole thing off by deleting the project first + hosting_add_task($nid, 'delete'); } // Go back to the main project list page diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 0988a55ea..fc6bf387e 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -92,3 +92,10 @@ function devshop_projects_uninstall() { // Delete tables. drupal_uninstall_schema('devshop_projects'); } + +function devshop_projects_update_1() { + $ret = array(); + $ret[] = update_sql("ALTER TABLE {hosting_devshop_project} " . + "ADD COLUMN data longtext NOT NULL default ''"); + return $ret; +} diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6a986faa5..2bbc2e65c 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -176,3 +176,32 @@ function devshop_projects_create_site($project_node, $platform_node, $env) { } } +/* + * Helper function which writes a serialize array in the project file + */ + +function devshop_projects_project_data_set($nid, $data) { + db_query("UPDATE {hosting_devshop_project} SET data = '%s' WHERE nid = %d", + serialize($data), $nid); +} + +/* + * Helper function which reads the serialize array in the project file + */ + +function devshop_projects_project_data_get($nid) { + + $sdata = db_result(db_query("SELECT data FROM {hosting_devshop_project} " . + "WHERE nid = %d", $nid)); + + if (!$sdata || strlen($sdata) < 1) { + $data = array(); + } + else { + $data = unserialize($sdata); + } + + return $data; +} + + diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 8263b5519..3c1028974 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -107,8 +107,6 @@ function devshop_projects_insert($node) { */ function devshop_projects_update($node) { -dpm("Update hook called"); -dpm($node); db_query("UPDATE {hosting_devshop_project} " . "SET git_url = '%s', code_path = '%s', base_url = '%s' " . "WHERE nid = %d", @@ -128,8 +126,7 @@ dpm($node); * Implementation of hook_delete(). */ function devshop_projects_delete($node) { -dpm("Delete hook called"); -dpm($node); + db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 2f0e7ea47..687cadd2f 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -205,10 +205,18 @@ function devshop_projects_post_hosting_verify_task($task, $data) { devshop_projects_create_site($project, $platform, $p_obj->env_type); } +function watchcat($m, $l) { + $f = fopen("/tmp/{$m}.catlog", "a+"); + $cr = ($l[strlen($l) - 1] == '\n') ? true : false; + fwrite($f, sprintf("%s %s%s", date('Y-m-d H:i:s'), $l, $cr ? "" : "\n")); + fclose($f); +} + + /* * This function checks to see if there if there are any more objects * to disable or delete during the 'project delete' task. The list of - * tasks that need to be done are kept in $project_node->data. See the + * tasks that need to be done are kept in the project data field. See the * following two function for more detail on those arrays: * * devshop_projects_project_delete_form_submit @@ -220,41 +228,39 @@ function devshop_projects_post_hosting_verify_task($task, $data) { */ function devshop_projects_project_delete_queue_next_task($pnode) { - // Assume all done - $node_updated = FALSE; + $updated = FALSE; $nid = FALSE; + $data = devshop_projects_project_data_get($pnode->nid); // Keys to tasks to be performed. Objects must be processed in this // order due to task dependencies. - $keys = array('site' => 'disable', - 'site' => 'delete', - 'platform' => 'delete'); + $keys = array(array('type' => 'site', + 'task' => 'disable'), + array('type' => 'site', + 'task' => 'delete'), + array('type' => 'platform', + 'task' => 'delete')); // Iterate through arrays of tasks => nids and queue one tasks that // is left to do - foreach ($keys as $type => $task) { - $key1 = 'project_delete_{$type}_{$task}'; - watchdog('devshop', "key1 = $key1"); //zzz + foreach ($keys as $oper) { + $key1 = "project_delete_{$oper['type']}_{$oper['task']}"; - if (isset($pnode->data[$key1])) { - if (count($pnode->data[$key1]) > 0) { + if (isset($data[$key1])) { + if (count($data[$key1]) > 0) { // Use the first nid in the list and stop looking for any more work - $nid = $pnode->data[$key1][0]; + $nid = $data[$key1][0]; break; } else { - watchdog('devshop', "key1 = $key1 COUNT IS ZERO!"); //zzz + // Array is empty, delete it + unset($data[$key1]); } - - } - else { - watchdog('devshop', "key1 = $key1 is NOT SET!"); //zzz + $updated = TRUE; } - watchdog('devshop', "unsetting key $key1 XXX"); //zzz - unset($pnode->data[$key1]); - $node_updated = TRUE; + } // If $nid != FALSE, then we need to act on that node. If it is FALSE, @@ -263,30 +269,31 @@ function devshop_projects_project_delete_queue_next_task($pnode) { // table if ($nid) { - watchdog('devshop', "Project delete: scheduling taskk '%s' for nid $nid"); - hosting_add_task($nid, $task); + hosting_add_task($nid, $oper['task']); } - // If we changed the project node in any way, we need to save the changes - if ($node_updated) { - watchdog('devshop', "updating node. status = {$node->status}"); //zzz - // watchdog('devshop', "updating node. status = ${node->status}"); //zzz - node_save($pnode); + // If we changed the project data array in any way, we need to save it + if ($updated) { + devshop_projects_project_data_set($pnode->nid, $data); } if (!$nid) { - watchdog('devshop', "Project delete: complete!"); $pnode->status = 0; //$pnode->rid = 0; - $node_updated = TRUE; + node_save($pnode); + + // Check to see if the user wanted the project directory removed. + // If so, make sure the directory path is valid and isn't "/"!! + if ($data['deleting_project_remove_dir'] && + strlen($pnode->code_path) > 1) { + @exec("rm -rf {$pnode->code_path}"); + } // delete this project - watchdog('devshop', "deleting project from hosting_devshop_project"); //zzz db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", $pnode->nid); } - } /* @@ -300,13 +307,13 @@ function devshop_projects_project_delete_queue_next_task($pnode) { * $node->data field. There are four elements in that array: * * - * $project_node-> data['deleting_project']: if TRUE, we are in the process + * project data['deleting_project']: if TRUE, we are in the process * of delete this project and all sites & platforms. - * $project_node->data['project_delete_site_disable']: array of site NID's + * project data['project_delete_site_disable']: array of site NID's * that are to be disabled. - * $project_node->data['project_delete_site_delete']: an array of site NID's + * project data['project_delete_site_delete']: an array of site NID's * that are to be deleted once they are disabled. - * $project_node->data['project_delete_platform_delete']: an array of platform + * project data['project_delete_platform_delete']: an array of platform * NID's that are to be deleted once the sites based on these platforms * are disabled and deleted. * @@ -325,8 +332,12 @@ function devshop_projects_project_delete_queue_next_task($pnode) { * The operation (task type) that just complted. Will be one of: * 'project delete', 'site disable', 'site delete', or * 'platform delete'. + * @param $failed + * If TRUE, this task did not complete succesfully. We know this + * the 'rollback' hook was invoked rather then the 'post task' + * hook. There isn't much we can do but log it and carry on. */ -function devshop_projects_project_delete_continue($nid, $op) { +function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { if (!($node = node_load($nid))) { watchdog('devshop', "Delete continue: nid $nid not found"); @@ -334,15 +345,19 @@ function devshop_projects_project_delete_continue($nid, $op) { } if ($node->type == 'project') { + // First step complete: project deleted. Now we need to + // disable all of the sites, delete them. And then delete + // all of the platforms. devshop_projects_project_delete_queue_next_task($node); return; } else if (($node->type != 'site') && - ($node->type == 'platform')) { + ($node->type != 'platform')) { // We only care about sites and platforms return; } + // Does this object belong to a devshop project? If not, // then just ignore it. if (!($node->project_nid)) { @@ -350,33 +365,38 @@ function devshop_projects_project_delete_continue($nid, $op) { } // load project node - if (!($project_node = node_load($node->project_id))) { + if (!($project_node = node_load($node->project_nid))) { watchdog('devshop', "Delete continue: can't load project nid $node->project_nid"); return; } - if (!$project_node->data['deleting_project']) { - watchdog('devshop', - "Delete continue: delete_project flag not TRUE"); + $data = devshop_projects_project_data_get($project_node->nid); + + if (!$data['deleting_project']) { return; } - // delete this object - db_query("DELETE FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d AND object_nid = %d", - $project_node->nid, $node->nid); + if ($op == 'delete') { + // delete this object if the task just complete was a delete + db_query("DELETE FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d AND object_nid = %d", + $project_node->nid, $node->nid); + } // Remove this nid from our array of things to do - $key1 = 'project_delete_{$node->type}_{$op}'; - $key2 = array_search($node->nid, $project_node->data[$key1]); + $key1 = "project_delete_{$node->type}_{$op}"; + $key2 = array_search($node->nid, $data[$key1]); - if ($key2 != FALSE) { - unset($project_node->data[$key1][$key2]); - if (count($project_node->data[$key1][$key2]) == 0) { - unset($project_node->data[$key1]); + if ($key2 === FALSE) { + watchdog('devshop', "search for nid {$node->nid} failed, key = $key2"); + } + else { + array_shift($data[$key1]); + if (count($data[$key1]) == 0) { + unset($data[$key1]); } - node_save($project_node); + devshop_projects_project_data_set($project_node->nid, $data); } // do the next delete task @@ -394,16 +414,39 @@ function devshop_projects_post_hosting_delete_task($task, $data) { } +/** + * Implementation of hook_hosting_TASK_TYPE_task_rollback(). + * + * This hook is invoked when the delete task fails. + * Carry on my wayward son. There will be peace when you are done. + * Lay your weary head to rest. Don't you cry no more. + */ +function devshop_projects_hosting_delete_task_rollback($task, $data) { + watchdog('devshop', "Delete task rollback hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); +} + /* * Implementation of hook_post_hosting_TASK_TYPE_task * */ function devshop_projects_post_hosting_disable_task($task, $data) { - watchdog('devshop', "Disablw post task hook called"); + watchdog('devshop', "Disable post task hook called"); devshop_projects_project_delete_continue($task->ref->nid, 'disable'); } +/** + * Implementation of hook_hosting_TASK_TYPE_task_rollback(). + * + * This hook is invoked when the disable task fails. But we must carry on! + */ +function devshop_projects_hosting_disable_task_rollback($task, $data) { + watchdog('devshop', "Disable task rollback hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); +} + + /* * Returns TRUE is the project create task for the given node failed. */ From 730f6ff6c921375a2152767fe32abf0111e0000b Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Tue, 9 Oct 2012 17:37:05 -0600 Subject: [PATCH 0102/3476] Skeleton devshop project platform module --- devshop_projects/devshop_projects.form.inc | 21 +------ .../devshop_projects_platform.info | 5 ++ .../devshop_projects_platform.install | 55 +++++++++++++++++++ .../devshop_projects_platform.module | 41 ++++++++++++++ 4 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 devshop_projects_platform/devshop_projects_platform.info create mode 100644 devshop_projects_platform/devshop_projects_platform.install create mode 100644 devshop_projects_platform/devshop_projects_platform.module diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 3cbec3ca1..8d71c44ae 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -318,26 +318,6 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { '#default_value' => '', '#maxlength' => 255, ); -/* - Not needed for initial phase - $form['git_url'] = array( - '#type' => 'textfield', - '#title' => t('Git URL'), - '#required' => FALSE, - '#description' => t(''), - '#size' => 40, - '#default_value' => '', - '#maxlength' => 255, - ); - $form['branch'] = array( - '#type' => 'textfield', - '#title' => t('Branch'), - '#description' => t(''), - '#size' => 40, - '#default_value' => 'master', - '#maxlength' => 255, - ); -*/ $form['nid'] = array( '#type' => 'value', '#value' => $project_node->nid, @@ -345,6 +325,7 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { $form['submit'] = array( '#type' => 'submit', '#value' => t('Create platform'), + '#weight' => 32, ); return $form; } diff --git a/devshop_projects_platform/devshop_projects_platform.info b/devshop_projects_platform/devshop_projects_platform.info new file mode 100644 index 000000000..230b5ff34 --- /dev/null +++ b/devshop_projects_platform/devshop_projects_platform.info @@ -0,0 +1,5 @@ +name = DevShop Projects Platform +description = A DevShop Projects module that allows the user to create platforms with different git repo and branch than the original project setting. +core = 6.x +package = DevShop + diff --git a/devshop_projects_platform/devshop_projects_platform.install b/devshop_projects_platform/devshop_projects_platform.install new file mode 100644 index 000000000..5ec87f8f5 --- /dev/null +++ b/devshop_projects_platform/devshop_projects_platform.install @@ -0,0 +1,55 @@ + array( + 'project_nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Project/Node ID.', + ), + 'object_nid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Site NID.', + ), + 'branch' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => 255, + 'default' => '', + 'description' => 'The git branch for this platform', + ), + ), + 'primary key' => array('object_nid'), + ); + + return $schema; +} + +/** + * Implementation of hook_install(). + */ +function devshop_projects_platform_install() { + // Create tables. + drupal_install_schema('devshop_projects_platform'); +} + +/** + * Implementation of hook_uninstall(). + */ +function devshop_projects_platform_uninstall() { + // Delete tables. + drupal_uninstall_schema('devshop_projects_platform'); +} + diff --git a/devshop_projects_platform/devshop_projects_platform.module b/devshop_projects_platform/devshop_projects_platform.module new file mode 100644 index 000000000..ea059a915 --- /dev/null +++ b/devshop_projects_platform/devshop_projects_platform.module @@ -0,0 +1,41 @@ + 'textfield', + '#title' => t('Git URL'), + '#required' => FALSE, + '#description' => t(''), + '#size' => 40, + '#default_value' => '', + '#maxlength' => 255, + ); + $form['branch'] = array( + '#type' => 'textfield', + '#title' => t('Branch'), + '#description' => t(''), + '#size' => 40, + '#default_value' => 'master', + '#maxlength' => 255, + ); + } +} From 6c5a2d923909b528b204365c18488d53b14d9397 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Wed, 10 Oct 2012 14:55:51 -0600 Subject: [PATCH 0103/3476] Can now create project platforms with different git url and/or branch than the git url in the project --- devshop_projects/devshop_projects.form.inc | 15 +++- .../devshop_projects_platform.install | 18 +++-- .../devshop_projects_platform.module | 77 ++++++++++++++++++- 3 files changed, 102 insertions(+), 8 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 8d71c44ae..f79b55d55 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -348,8 +348,21 @@ function devshop_projects_platform_create_form_validate(&$form, &$form_state){ function devshop_projects_platform_create_form_submit(&$form, &$form_state){ $project_node = node_load($form_state['values']['nid']); - $args = array('platform-name' => $form_state['values']['platform_name']); + $args = array(); + if ($form_state['values']['git_url']) { + $args['git-url'] = $form_state['values']['git-url']; + } + + if ($form_state['values']['branch']) { + $args['branch'] = $form_state['values']['branch']; + } + + $args['platform-name'] = $form_state['values']['platform_name']; + hosting_add_task($project_node->nid, 'devshop-platform-create', $args); + + $form_state['redirect'] = "node/{$project_node->nid}"; + drupal_goto("node/{$project_node->nid}"); } /** diff --git a/devshop_projects_platform/devshop_projects_platform.install b/devshop_projects_platform/devshop_projects_platform.install index 5ec87f8f5..b8c745d3c 100644 --- a/devshop_projects_platform/devshop_projects_platform.install +++ b/devshop_projects_platform/devshop_projects_platform.install @@ -17,11 +17,19 @@ function devshop_projects_platform_schema() { 'default' => 0, 'description' => 'Project/Node ID.', ), - 'object_nid' => array( - 'type' => 'int', + 'platform_name' => array( + 'type' => 'varchar', 'not null' => TRUE, - 'default' => 0, - 'description' => 'Site NID.', + 'length' => 255, + 'default' => '', + 'description' => 'The name this platform', + ), + 'git_url' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => 255, + 'default' => '', + 'description' => 'The git URL of this platform', ), 'branch' => array( 'type' => 'varchar', @@ -31,7 +39,7 @@ function devshop_projects_platform_schema() { 'description' => 'The git branch for this platform', ), ), - 'primary key' => array('object_nid'), + 'primary key' => array('platform_name'), ); return $schema; diff --git a/devshop_projects_platform/devshop_projects_platform.module b/devshop_projects_platform/devshop_projects_platform.module index ea059a915..5b58fb6e8 100644 --- a/devshop_projects_platform/devshop_projects_platform.module +++ b/devshop_projects_platform/devshop_projects_platform.module @@ -19,14 +19,16 @@ function devshop_projects_platform_perm() { */ function devshop_projects_platform_form_alter(&$form, &$form_state, $form_id){ - if ($form_id == 'devshop_projects_platform_create_form'){ + if ($form_id == 'devshop_projects_platform_create_form') { + $project_node = node_load($form['nid']['#value']); + $form['git_url'] = array( '#type' => 'textfield', '#title' => t('Git URL'), '#required' => FALSE, '#description' => t(''), '#size' => 40, - '#default_value' => '', + '#default_value' => $project_node->git_url, '#maxlength' => 255, ); $form['branch'] = array( @@ -38,4 +40,75 @@ function devshop_projects_platform_form_alter(&$form, &$form_state, $form_id){ '#maxlength' => 255, ); } + + // Setup a call back when this form is submitted + array_unshift($form['#submit'], + devshop_projects_platform_platform_create_save); +} + +/* + * This is a pseudo form sibmit function that gets invoked when the + * project platform create form is submitted. + */ + +function devshop_projects_platform_platform_create_save($form, &$form_state) { + + $project_node = node_load($form_state['values']['nid']); + $pname = $form_state['values']['platform_name']; + $pname = "{$project_node->title}_{$pname}"; + db_query("INSERT INTO {hosting_devshop_project_platform} " . + "(project_nid, platform_name, git_url, branch) " . + "VALUES (%d, '%s', '%s', '%s')", + $project_node->nid, + $pname, + $form_state['values']['git_url'], + $form_state['values']['branch']); +} + +/** + * Implements hook_nodeapi() + */ +function devshop_projects_platform_nodeapi(&$node, $op, $a3 = null) { + //Platforms and Sites + if ($node->type == 'platform' || $node->type == 'site'){ + + if ($node->type == 'platform') { + $pname = $node->title; + } + else { + $pname = node_load($node->platform)->title; + } + + // Load Project info + if ($op == 'load') { + $data = db_fetch_array(db_query("SELECT git_url, branch " . + "FROM {hosting_devshop_project_platform} " . + "WHERE platform_name = '%s'", + $pname)); + return $data; + } + + // Display Project info + if ($op == 'view') { + if($node->git_url) { + $node->content['info']['git_url'] = array( + '#type' => 'item', + '#title' => t('Git URL'), + '#value' => $node->git_url, + '#weight' => 0 + ); + } + + if($node->branch) { + $node->content['info']['branch'] = array( + '#type' => 'item', + '#title' => t('Git Branch'), + '#value' => $node->branch, + '#weight' => 0 + ); + } + } + } } + + From bcca8e1d35cc7553cd784ade6813744bea6c52ff Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Wed, 10 Oct 2012 15:18:22 -0600 Subject: [PATCH 0104/3476] Delete from hosting_devshop_projct_platform when the platform is deleted --- .../devshop_projects_platform.module | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects_platform/devshop_projects_platform.module b/devshop_projects_platform/devshop_projects_platform.module index 5b58fb6e8..648103540 100644 --- a/devshop_projects_platform/devshop_projects_platform.module +++ b/devshop_projects_platform/devshop_projects_platform.module @@ -47,7 +47,7 @@ function devshop_projects_platform_form_alter(&$form, &$form_state, $form_id){ } /* - * This is a pseudo form sibmit function that gets invoked when the + * This is a pseudo form submit function that gets invoked when the * project platform create form is submitted. */ @@ -109,6 +109,12 @@ function devshop_projects_platform_nodeapi(&$node, $op, $a3 = null) { } } } + + if ($node->type == 'platform' && $op == 'delete') { + db_query("DELETE FROM {hosting_devshop_project_platform} " . + "WHERE platform_name = '%s'", $pname); + return; + } } From 8b60b0a820c10637a800b7c5328cc287d2797140 Mon Sep 17 00:00:00 2001 From: Chris Oden Date: Mon, 15 Oct 2012 20:50:44 -0400 Subject: [PATCH 0105/3476] Fix error which checked platforms rather than sites for pull enabled -- this begs the question of why this is enabled per-site rather than per-platform --- devshop_pull/devshop_pull.inc | 4 ++-- devshop_pull/devshop_pull.module | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 1ac83f562..9aba65f4f 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -67,12 +67,12 @@ function devshop_pull_callback($project, $hash) { } //Search site with pull enable of this project - $platforms = $pnode->project_objects['platform']; + $sites = $pnode->project_objects['site']; //Put timestamp db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); - foreach ($platforms as $site_nid => $platform) { + foreach ($sites as $site_nid => $site) { //If pull enabled then add to task if (db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid))) { $message = "Queuing a pull task for node id $site_nid"; diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index dbd2e966f..e23b0c3fb 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -275,10 +275,10 @@ function devshop_pull_task($nid){ } else { //Search site with pull enabled of this project - $platforms = $node->project_objects['platform']; + $sites = $node->project_objects['site']; - foreach ($platforms as $site_nid => $platform) { + foreach ($sites as $site_nid => $site) { //If pull enabled then add to task $data = db_fetch_object(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid)); if ($data->pull_enabled) { From 27f6ca4e6629af52b93d26447fb2f9b6abac3d1c Mon Sep 17 00:00:00 2001 From: Chris Oden Date: Tue, 16 Oct 2012 08:25:30 -0400 Subject: [PATCH 0106/3476] Fix permissions typo for deleting projects --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 2bbc2e65c..d321f2562 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -45,7 +45,7 @@ function devshop_projects_menu() { 'description' => 'Delete a project and all of it\'s associated sites and platforms', 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_projects_project_delete_form', 3), - 'access arguments' => array('delete projects'), + 'access arguments' => array('delete project'), ); return ($items); From c1102fac2fa13cc6723631b37db0502ebf496546 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 18 Oct 2012 14:36:30 -0600 Subject: [PATCH 0107/3476] Don't add platform form to every pagevi devshop_projects_platform.module --- .../devshop_projects_platform.module | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_projects_platform/devshop_projects_platform.module b/devshop_projects_platform/devshop_projects_platform.module index 648103540..7d4bfd9d8 100644 --- a/devshop_projects_platform/devshop_projects_platform.module +++ b/devshop_projects_platform/devshop_projects_platform.module @@ -39,11 +39,11 @@ function devshop_projects_platform_form_alter(&$form, &$form_state, $form_id){ '#default_value' => 'master', '#maxlength' => 255, ); - } - // Setup a call back when this form is submitted - array_unshift($form['#submit'], - devshop_projects_platform_platform_create_save); + // Setup a call back when this form is submitted + array_unshift($form['#submit'], + devshop_projects_platform_platform_create_save); + } } /* From 65ec0c79b88a7abcc90f8c5ec68b56a387371cb4 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 18 Oct 2012 14:45:40 -0600 Subject: [PATCH 0108/3476] Fixed typo in devshop_projects.module for access argument in delete project menu item. --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 2bbc2e65c..d321f2562 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -45,7 +45,7 @@ function devshop_projects_menu() { 'description' => 'Delete a project and all of it\'s associated sites and platforms', 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_projects_project_delete_form', 3), - 'access arguments' => array('delete projects'), + 'access arguments' => array('delete project'), ); return ($items); From a903225ddc05579371ad7522dd195ab4900399ea Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 18 Oct 2012 14:59:39 -0600 Subject: [PATCH 0109/3476] Removed Delete Project list from Project List page --- devshop_projects/devshop_projects.node.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 3c1028974..d65b07c89 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -306,7 +306,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { */ function devshop_projects_projects_view() { - $header = array('Projects', 'Platforms', 'Sites', 'Delete?'); + $header = array('Projects', 'Platforms', 'Sites'); $r = db_query("SELECT * FROM {hosting_devshop_project}"); $rows = array(); @@ -317,7 +317,6 @@ function devshop_projects_projects_view() { $row[] = l($node->title, "node/$proj->nid"); $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='platform'", $node->nid)); $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); - $row[] = l("Delete Project", "hosting/projects/delete/$proj->nid"); $rows[] = $row; } From 807a53adf869f3ca0a197858d424c207585b3559 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 18 Oct 2012 16:08:24 -0600 Subject: [PATCH 0110/3476] Added Delete Project link to the bottom of the project node view page. --- devshop_projects/devshop_projects.node.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index d65b07c89..94e0b825b 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -270,6 +270,10 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } + $node->content['sites']['delete_project'] = array( + '#type' => 'item', + '#value' => l("Delete this project", "hosting/projects/delete/$node->nid") + ); //Tasks $node->content['tasks_view'] = array( From 42d31385214f958952f21da7ed4fe39323546eed Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 18 Oct 2012 17:20:04 -0600 Subject: [PATCH 0111/3476] Don't show delete sites or platforms on projet node view page. Removed the Project Delete Task button from the same page. Delete objects from the devshop_objects table when the actual node is deleted. Wrote update 2 to remove stale objects from the devshop_object table (for node that have been deleted). --- devshop_projects/devshop_projects.install | 22 +++++++++++++++ devshop_projects/devshop_projects.node.inc | 32 ++++++++++++++++++---- devshop_projects/devshop_projects.task.inc | 8 ------ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index fc6bf387e..f1cd7f990 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -99,3 +99,25 @@ function devshop_projects_update_1() { "ADD COLUMN data longtext NOT NULL default ''"); return $ret; } + +/* + * Update 2: Delete rows in the hosting_devshop_project_objects table + * that the node pointed to by object nid no longer exists. + */ + +function devshop_projects_update_2() { + $ret = array(); + $query = db_query("SELECT object_nid " . + "FROM {hosting_devshop_project_object}"); + + while($proj = db_fetch_object($query)) { + $count = db_result(db_query("SELECT COUNT(*) FROM {node} " . + "WHERE nid = %d", $proj->object_nid)); + if ($count != 1) { + $ret[] = update_sql('DELETE FROM {hosting_devshop_project_object} ' . + 'WHERE object_nid = %d', $proj->object_nid); + } + } + + return $ret; +} diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 94e0b825b..c3be4fa5e 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -70,8 +70,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { '#value' => $node->project_environment, '#weight' => -11 ); - - return $data; + } + + // The NODE is being deleted. Remove the nid from the objects table + if ($op == 'delete'){ + db_query('DELETE FROM {hosting_devshop_project_object} ' . + 'WHERE object_nid = %d', $node->nid); } } } @@ -241,12 +245,28 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if (isset($node->project_objects['site'])) { foreach($node->project_objects['site'] as $nid => $env) { $site = node_load($nid); + $platform = node_load($site->platform); + if ($site->site_status == -2 && $platform->platform_status == -2) { + continue; + } $row = array(); - $url = "http://$site->title"; - $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); - $row[] = l('Site', "node/$site->nid"); + if ($site->site_status != -2) { + $url = "http://$site->title"; + $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); + $row[] = l('Site', "node/$site->nid"); + } + else { + // Site if deleted, just show the platform name + $row[] = $platform->title; + $row[] = ''; + } $row[] = l('Platform', "node/$site->platform"); - $row[] = l('Download Database', "$url/admin/status/"); + if ($site->site_status != -2) { + $row[] = l('Download Database', "$url/admin/status/"); + } + else { + $row[] = ''; + } $row[] = l('Commit Log', "node/$site->platform/gitlog"); $rows[] = $row; } diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 687cadd2f..4b4c4eb65 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -17,10 +17,6 @@ function devshop_projects_hosting_tasks() { $tasks['project']['delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), -// 'provision_save' => TRUE, - 'dialog' => TRUE, - 'access callback' => _cp_access_callback, - ); $tasks['project']['devshop-install'] = array( 'title' => t('Install Sites'), @@ -34,10 +30,6 @@ function devshop_projects_hosting_tasks() { return $tasks; } -function _cp_access_callback() { - return TRUE; -} - /** * Implements hook_hosting_project_context_options() * From 932bb0f34b774fb2a3ea003fcf6fb95e755ba67c Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Fri, 19 Oct 2012 14:17:07 -0600 Subject: [PATCH 0112/3476] Skeletal testing module being checked in --- .../devshop_projects_testing.info | 6 +++ .../devshop_projects_testing.install | 49 +++++++++++++++++++ .../devshop_projects_testing.module | 30 ++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 devshop_projects_testing/devshop_projects_testing.info create mode 100644 devshop_projects_testing/devshop_projects_testing.install create mode 100644 devshop_projects_testing/devshop_projects_testing.module diff --git a/devshop_projects_testing/devshop_projects_testing.info b/devshop_projects_testing/devshop_projects_testing.info new file mode 100644 index 000000000..8cc0770c9 --- /dev/null +++ b/devshop_projects_testing/devshop_projects_testing.info @@ -0,0 +1,6 @@ +name = DevShop Projects Testing +description = A DevShop Projects module that allows the user to incorporate simple tests within a project and have the test run automatically. +core = 6.x +package = DevShop +dependencies[] = devshop_hosting +~ diff --git a/devshop_projects_testing/devshop_projects_testing.install b/devshop_projects_testing/devshop_projects_testing.install new file mode 100644 index 000000000..961adc9c5 --- /dev/null +++ b/devshop_projects_testing/devshop_projects_testing.install @@ -0,0 +1,49 @@ + array( + 'project_nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Project Node ID.', + ), + 'tests_to_run' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + 'default' => '', + 'description' => 'A list of tests to run', + ), + ), + 'primary key' => array('project_nid'), + ); + + return $schema; +} + +/** + * Implementation of hook_install(). + */ +function devshop_projects_testing_install() { + // Create tables. + drupal_install_schema('devshop_projects_testing'); +} + +/** + * Implementation of hook_uninstall(). + */ +function devshop_projects_testing_uninstall() { + // Delete tables. + drupal_uninstall_schema('devshop_projects_testing'); +} + diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module new file mode 100644 index 000000000..5309e5d19 --- /dev/null +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -0,0 +1,30 @@ + Date: Wed, 24 Oct 2012 11:26:37 -0600 Subject: [PATCH 0113/3476] Removing files that where accidentally checked into this repo (from devshop_provision) --- commit.devshop.provision.inc | 62 --------- create.devshop.provision.inc | 86 ------------ create.platform.devshop.provision.inc | 56 -------- delete.devshop.provision.inc | 18 --- devshop_provision.context.project.inc | 27 ---- devshop_provision.drush.inc | 188 -------------------------- devshop_provision.info | 5 - dl.devshop.provision.inc | 17 --- pull.devshop.provision.inc | 87 ------------ sync.devshop.provision.inc | 143 -------------------- 10 files changed, 689 deletions(-) delete mode 100644 commit.devshop.provision.inc delete mode 100644 create.devshop.provision.inc delete mode 100644 create.platform.devshop.provision.inc delete mode 100644 delete.devshop.provision.inc delete mode 100644 devshop_provision.context.project.inc delete mode 100644 devshop_provision.drush.inc delete mode 100644 devshop_provision.info delete mode 100644 dl.devshop.provision.inc delete mode 100644 pull.devshop.provision.inc delete mode 100644 sync.devshop.provision.inc diff --git a/commit.devshop.provision.inc b/commit.devshop.provision.inc deleted file mode 100644 index 587ccb497..000000000 --- a/commit.devshop.provision.inc +++ /dev/null @@ -1,62 +0,0 @@ -platform->root; - $target = d()->name; - - //Pause Hostmaster (Mainly for the git pull task) - // @TODO Better way? @see drush_hosting_pause() - exec('crontab -r'); - - // Update All Features - provision_backend_invoke($target, 'features-update-all'); - - //Execute git commit - //Generate commit message - $message = array(); - $message[] = "Exported from: $target"; - $message[] = str_repeat('-', strlen($message[0])); - if (drush_get_option('message')){ - $message[] = drush_get_option('message'); - } - $message = implode("\n", $message); - - $data = array( - 'message' => $message, - ); - - //Invoke provision-git-add to add any new files to the index - provision_backend_invoke($target, 'provision-git-add', - array("sites/all/modules/features"), $data); - - //Invoke provision-git-commit - provision_backend_invoke($target, 'provision-git-commit', array(), $data); - - //Push, if the user wants. - if (drush_get_option('push')){ - provision_backend_invoke($target, 'provision-git-push'); - } - - //Revert, if the user wants. - if (drush_get_option('revert')){ - provision_backend_invoke($target, 'provision-git-reset'); - } - - //Resume cron - // @TODO: how to get hostmaster site name? @see drush_provision_post_hostmaster_migrate() - // they use drush_get_option('site_name') - // @TODO Better way? @see drush_hosting_pause() - provision_backend_invoke('hostmaster', 'hosting-setup'); -} diff --git a/create.devshop.provision.inc b/create.devshop.provision.inc deleted file mode 100644 index 43e46d46a..000000000 --- a/create.devshop.provision.inc +++ /dev/null @@ -1,86 +0,0 @@ -git_url; - $path = d()->code_path; - $project = d()->project_name; - $retry = drush_get_option('retry'); - - // Validate - if (empty($git_url)) { - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Git URL not valid.')); - } - if (file_exists($path) && !$retry) { - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Path already exists!')); - } - - // Pause Hostmaster (Mainly for the git pull task) - // @TODO Better way? @see drush_hosting_pause() - exec('crontab -r'); - - // Change to $path and copy dev to test and live. - // @TODO: Is better way to do with provision? - if(drush_shell_exec("mkdir $path -p")) { - drush_log(dt('Created directory !path.', array('!path' => $path)), 'ok'); - } - else { - // Mksie failed. If this is a retry and the directory exist, then it's - // OK, otherwise PANIC!! - if (!$retry || !is_dir($path)) { - drush_log(implode("\n", drush_shell_exec_output()), 'error'); - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Unable to create directory !path', array('!path' => $path))); - } - } - - // @TODO: Would this be good in provision git? I feel like its just a wrapper, is it worth it? - // Maybe it would be better, because of provision_backend_invoke() - //provision_backend_invoke($target, 'provision-git-clone', array($git_url, $path)); - if (drush_shell_cd_and_exec($path, "git clone $git_url dev")) { - drush_log(dt('Git repository cloned.', array('!path' => $path)), 'ok'); - drush_log(implode("\n", drush_shell_exec_output()), 'ok'); - } - else { - drush_log(implode("\n", drush_shell_exec_output()), 'error'); - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Git clone failed!')); - } - - if(drush_shell_cd_and_exec($path, "cp dev test -rf")) { - drush_log(dt('Repo copied to test.', array()), 'ok'); - } - else { - drush_log(implode("\n", drush_shell_exec_output()), 'error'); - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Unable to copy repo to test!')); - } - - if(drush_shell_cd_and_exec($path, "cp dev live -rf")) { - drush_log(dt('Repo copied to live.', array()), 'ok'); - } - else { - drush_log(implode("\n", drush_shell_exec_output()), 'error'); - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Unable to copy repo to live!')); - } - - // @TODO: Create Platforms and Sites, and import into Aegir! - foreach (array('dev', 'test', 'live') as $env){ - - // Save aegir context - $platform_hosting_name = "@platform_{$project}_{$env}"; - $options = array(); - $options['project'] = d()->name; - $options['context_type'] = "platform"; - $options['root'] = "{$path}/{$env}"; - $output .= drush_backend_invoke_args('provision-save', array($platform_hosting_name), $options); - - // Import platform - provision_backend_invoke('@hostmaster', 'hosting-import', array($platform_hosting_name)); - } - - //Resume cron - // @TODO Better way? @see drush_hosting_pause() - provision_backend_invoke('hostmaster', 'hosting-setup'); -} - diff --git a/create.platform.devshop.provision.inc b/create.platform.devshop.provision.inc deleted file mode 100644 index 29b964cfe..000000000 --- a/create.platform.devshop.provision.inc +++ /dev/null @@ -1,56 +0,0 @@ -git_url; - $branch = d()->branch; - $project = d()->project_name; - $path = d()->code_path; - - $args = drush_get_arguments(); - // Validate - if (empty($git_url)){ - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Git URL not valid.')); - } - - if (!file_exists($path)){ - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Path does not exist!')); - } - - $platform = drush_get_option('platform-name'); - - if (!$platform) { - // Try to get it from the command line - if (count($args) < 2) { - return drush_set_error('DRUSH_FRAMEWORK_ERROR', dt('Missing platform name!')); - } - else { - $platform = $args[1]; - } - } - - // Pause Hostmaster (Mainly for the git pull task) - // @TODO Better way? @see drush_hosting_pause() - exec('crontab -r'); - - // Clone repo - drush_shell_cd_and_exec($path, "git clone $git_url $platform"); - - $platform_hosting_name = "@platform_{$project}_{$platform}"; - $options = array(); - $options['project'] = d()->name; - $options['context_type'] = "platform"; - $options['root'] = "{$path}/{$platform}"; - $output .= drush_backend_invoke_args('provision-save', array($platform_hosting_name), $options); - - // Import platform - provision_backend_invoke('@hostmaster', 'hosting-import', array($platform_hosting_name)); - - //Resume cron - // @TODO Better way? @see drush_hosting_pause() - provision_backend_invoke('hostmaster', 'hosting-setup'); -} - diff --git a/delete.devshop.provision.inc b/delete.devshop.provision.inc deleted file mode 100644 index 8689588eb..000000000 --- a/delete.devshop.provision.inc +++ /dev/null @@ -1,18 +0,0 @@ - 'Project: The codename for this project.', - '--code_path' => 'Project: The path to the project codebases. (NOT the Drupal root)', - '--git_url' => 'Project: The Git URL for this project.', - '--base_url' => 'Project: the base URL that the dev/test/live subdomains will be attached to.', - '--server' => 'Project: The server hosting this project. (Default is @server_master)', - '--install_profile' => 'Project: The desired installation profile for all sites.', - ); - } - - function init_project() { - $this->setProperty('project_name'); - $this->setProperty('code_path'); - $this->setProperty('install_profile'); - $this->setProperty('git_url'); - $this->setProperty('base_url'); - } -} \ No newline at end of file diff --git a/devshop_provision.drush.inc b/devshop_provision.drush.inc deleted file mode 100644 index 7633855f7..000000000 --- a/devshop_provision.drush.inc +++ /dev/null @@ -1,188 +0,0 @@ - 'Create a DevShop Project with three platforms and three sites for dev, test, and live.', - 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, - 'arguments' => array( - 'git-url' => 'The URL of a git repo to use to create the platforms.', - 'path' => 'Desired project path.', - 'branch' => 'Branch (not yet implemented!)', - 'retry' => 'Retry the create.', - ), - 'aliases' => array('create-project'), - ); - $items['provision-devshop-delete'] = array( - 'description' => 'Delete a DevShop Project and all associated sites and platforms.', - 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, - 'arguments' => array( - ), - 'aliases' => array('delete-project'), - ); - $items['provision-devshop-platform-create'] = array( - 'description' => 'Create a new platform and add it to an existing DevShop Project.', - 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, - 'arguments' => array( - 'git-url' => 'The URL of a git repo to use to create the platforms.', - 'branch' => 'Branch (not yet implemented!)', - ), - 'aliases' => array('create-platform'), - ); - $items['provision-devshop-pull'] = array( - 'description' => 'Pull & verify site code and (optionally) run update.php, clear cache, and revert features.', - 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, - 'options' => array( - //'sync' => 'Sync content from default source site before code pull.', - 'no-reset' => 'Run git reset --hard before code pull', - 'no-update' => 'Run update.php after code pull.', - 'no-revert' => 'Revert all features after code pull.', - 'no-cache' => 'Clear all caches after code pull.', - ), - 'aliases' => array('pdp'), - ); - $items['provision-devshop-sync'] = array( - 'description' => 'Sync database (and files, coming soon) from a chosen source site.', - 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, - 'options' => array( - 'update' => 'Run update.php after content sync.', - 'revert' => 'Revert all features after content sync.', - 'cache' => 'Clear all caches after content sync.', - 'files' => 'Sync site files.', - ), - 'arguments' => array( - 'from' => 'Site alias to sync from.', - 'to' => 'Site alias to sync to.', - ), - 'aliases' => array('pds'), - ); - $items['provision-devshop-commit'] = array( - 'description' => 'Export the site\'s Features and commit the result.', - 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, - 'options' => array( - 'revert' => ' Force revert all features after exporting and committing.', - ), - 'aliases' => array('pdc'), - 'arguments' => array( - 'from' => 'Site alias to sync from.', - ), - ); - $items['provision-devshop-dl'] = array( - 'description' => 'Download modules. This is just a wrapper for dl, it is here for hostmaster.', - 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, - 'options' => array( - 'modules' => 'The list of modules to DL.', - ), - 'aliases' => array('pdl'), - ); - return $items; -} - -function drush_devshop_provision_pre_hosting_task() { - $task =& drush_get_context('HOSTING_TASK'); - // Pull - if ($task->ref->type == 'site' && $task->task_type == 'devshop-pull') { - $task->options['no-update'] = !$task->task_args['update']; - $task->options['no-revert'] = !$task->task_args['revert']; - $task->options['no-cache'] = !$task->task_args['cache']; - } - - // Commit - if ($task->ref->type == 'site' && $task->task_type == 'devshop-commit') { - $task->options['message'] = $task->task_args['message']; - $task->options['push'] = $task->task_args['push']; - $task->options['revert'] = $task->task_args['revert']; - } - - // Sync - if ($task->ref->type == 'site' && $task->task_type == 'devshop-sync') { - $task->options['source'] = $task->task_args['source']; - $task->options['pull'] = $task->task_args['pull']; - $task->options['no-update'] = !$task->task_args['update']; - $task->options['no-revert'] = !$task->task_args['revert']; - $task->options['no-cache'] = !$task->task_args['cache']; - } - - // Projectt Create - if ($task->ref->type == 'project' && $task->task_type == 'devshop-create') { - $task->options['retry'] = $task->task_args['retry']; - } - - // Platform Create - if ($task->ref->type == 'project' && $task->task_type == 'devshop-platform-create') { - $task->options['platform-name'] = $task->task_args['platform-name']; - } - - // Download - if ($task->ref->type == 'site' && $task->task_type == 'devshop-dl') { - $task->options['modules'] = $task->task_args['modules']; - } -} - -/** - * Implements hook_drush_pm_post_download() - * - * Offers to git add and push the recently downloaded project. - */ -function devshop_provision_drush_pm_post_download($project, $release) { - - // Only act if in a provision controlled repo. - if (!provision_git_is_repo(NULL, TRUE)){ - return; - } else { - if (drush_confirm(dt('Add to git repo and push?'))){ - - // TODO: $target == 'self' when using over a remote drush alias. - // This causes fail. It would be pretty damn impressive to be - // be able to drush dl module via remote alias to a devshop box. - $target = d()->name; - - //Generate commit message - $message = array(); - $message[] = "Project Added: " . $release['name']; - $message[] = str_repeat('-', strlen($message[0])); - $message[] = "This commit was generated by drush dl " . $project['name']; - $message[] = str_repeat('-', strlen($message[0])); - if (drush_get_option('message')){ - $message[] = drush_get_option('message'); - } - $message = implode("\n", $message); - - $data = array( - 'message' => $message, - ); - - //Invoke provision-git-add to add any new files to the index - provision_backend_invoke($target, 'provision-git-add', - array($project['full_project_path']), $data); - - //Invoke provision-git-commit - provision_backend_invoke($target, 'provision-git-commit', array(), $data); - - // Push! - provision_backend_invoke($target, 'provision-git-push'); - } - } - -} diff --git a/devshop_provision.info b/devshop_provision.info deleted file mode 100644 index c8158c60a..000000000 --- a/devshop_provision.info +++ /dev/null @@ -1,5 +0,0 @@ -name = "DevShop Provision" -description = "Provides the backend tools needed by DevShop." -version = 7.x-1.x -dependencies[] = provision_git -dependencies[] = provision_git_features diff --git a/dl.devshop.provision.inc b/dl.devshop.provision.inc deleted file mode 100644 index 0d51778aa..000000000 --- a/dl.devshop.provision.inc +++ /dev/null @@ -1,17 +0,0 @@ -name; - provision_backend_invoke($target, "dl $modules"); -} \ No newline at end of file diff --git a/pull.devshop.provision.inc b/pull.devshop.provision.inc deleted file mode 100644 index f93667d65..000000000 --- a/pull.devshop.provision.inc +++ /dev/null @@ -1,87 +0,0 @@ -name; - $site_path = d()->platform->root? d()->platform->root: d()->root; - - //Get options - $update = drush_get_option('no-update') == FALSE; - $revert = drush_get_option('no-revert') == FALSE; - $cache = drush_get_option('no-cache') == FALSE; - $reset = drush_get_option('no-reset') == FALSE; - - //If reset the first task is git hard reset - //Execute git reset --hard - if ($reset) { - if (drush_shell_cd_and_exec($site_path, 'git reset --hard')) { - drush_log(dt('Git repository reseted.', array('!path' => $site_path)), 'ok'); - $output = drush_shell_exec_output(); - drush_log(implode("\n", drush_shell_exec_output()), 'ok'); - } - else { - return drush_set_error('DRUSH_PROVISION_GIT_PULL_FAILED', dt("Git pull failed in !path.\nThe specific errors are below:\n!errors", array('!path' => $site_path, '!errors' => implode("\n", drush_shell_exec_output())))); - } - } - - // Pull latest version of site - //Execute git pull --rebase - if (drush_shell_cd_and_exec($site_path, 'git pull --rebase')) { - drush_log(dt('Git repository pulled.', array('!path' => $site_path)), 'ok'); - $output = drush_shell_exec_output(); - drush_log(implode("\n", drush_shell_exec_output()), 'ok'); - } - else { - return drush_set_error('DRUSH_PROVISION_GIT_PULL_FAILED', dt("Git pull failed in !path.\nThe specific errors are below:\n!errors", array('!path' => $site_path, '!errors' => implode("\n", drush_shell_exec_output())))); - } - - if(count($output) == 1) { - drush_log('Nothing else needs to be done', 'ok'); - return; - } - // Verify the site. - // @TODO: provision-git-pull should actually handle this. If the files change, - // it should automatically verify. - provision_backend_invoke($target, 'provision-verify'); - - // update db, unless option is false. - if ($update){ - provision_backend_invoke($target, 'updb'); - } - - // Revert All Features, unless option is false - if ($revert){ - provision_backend_invoke($target, 'features-revert-all'); - } - - // Clear the whole cache, unless option is false - if ($cache){ - provision_backend_invoke($target, 'cc all'); - } - - //Resume cron - // @TODO: how to get hostmaster site name? @see drush_provision_post_hostmaster_migrate() - // they use drush_get_option('site_name') - // @TODO Better way? @see drush_hosting_pause() - provision_backend_invoke('hostmaster', 'hosting-setup'); -} - diff --git a/sync.devshop.provision.inc b/sync.devshop.provision.inc deleted file mode 100644 index 991292552..000000000 --- a/sync.devshop.provision.inc +++ /dev/null @@ -1,143 +0,0 @@ -name; - $destination_name = substr($destination, 1); //Without @ - } - - //Fail if no destination - if (!$destination_name){ - return drush_log(dt('Destination not found. You must enter a destination alias to sync from'), 'error'); - } - - // Pause Hostmaster - // @TODO Better way? @see drush_hosting_pause() - exec('crontab -r'); - - //Drop the database - drush_log(dt('DevShop: Dropping tables for destination'), 'ok'); - provision_backend_invoke($destination, 'sql-drop'); - - // Run provision-devshop-pull if requested - if (drush_get_option('pull')) { - if (!provision_backend_invoke($destination, 'provision-git-pull')){ - return; - } - } - - // Sync the databases - $timestamp = date("Ymd-His"); - //@TODO I know there is a better way... Don't remember where to find it. - $file_path = drush_server_home() . "/backups/devshop-sync/"; - $file_name = "$source_name-$timestamp.sql"; - $file = $file_path . $file_name; - - //Create backup folder if does not exist - //@TODO: DO THIS ON REMOTE AS WELL - if (!file_exists($file_path)){ - provision_file()->mkdir($file_path) - ->succeed('Created devshop-sync backups folder: ' . $file_path) - ->fail('Could not create the folder: ' . $file_path) - ->status(); - } - - $options = array( - 'source-dump' => $file, - 'target-dump' => $file, - 'result-file' => $file, - //@TODO: get this working. Have to figure out proper code to extract gz into SQL Import - //'gzip' => TRUE, - ); - - //Source and target dump options aren't working here... @TODO: figure out why! - //provision_backend_invoke('@hostmaster', 'sql-sync', array($source, $destination), $options); - //drush_invoke('sql-sync', $source, $destination); - - // SQL Dump - drush_log(dt('DevShop: Creating SQL dump from !source', array('!source' => $source)), 'ok'); - provision_backend_invoke($source, 'sql-dump', array(), $options); - - // COPY SQL file if it doesn't exist (which means a remote source) - // @TODO: Determine if source is remote instead of just checking if file is there. - if (!file_exists($file)){ - // Rsync to this machine. - drush_log('Copying backup from remote server', 'ok'); - - if (provision_backend_invoke('server_master', 'rsync', array($source . ':' . $file_path, $file_path))) { - drush_log('SQL transfered', 'ok'); - } else { - return drush_log(dt('Failed to copy the backup from the remote server'), 'error'); - } - } - - // SQL Import - // provision_backend_invoke() does not work here. Doesn't like piping - drush_log(dt('DevShop: Importing SQL into !destination', array('!destination' => $destination)), 'ok'); - exec("drush $destination sql-cli < $file"); - - // Sync files via rsync - if (drush_get_option('files')) { - drush_log(dt('DevShop: Syncing file contents to !destination', array('!destination' => $destination)), 'ok'); - provision_backend_invoke('server_master', 'rsync', array("$source:%files", "$destination:%files")); - } - - //Get options - $update = drush_get_option('no-update') == FALSE; - $revert = drush_get_option('no-revert') == FALSE; - $cache = drush_get_option('no-cache') == FALSE; - - // update db, unless option is false. - if ($update){ - provision_backend_invoke($destination, 'updb'); - } - - // Revert All Features, unless option is false - if ($revert){ - provision_backend_invoke($destination, 'features-revert-all'); - } - - // Clear the whole cache, unless option is false - if ($cache){ - provision_backend_invoke($destination, 'cc all'); - } - - // Verify the site. - provision_backend_invoke($destination, 'provision-verify'); - - //Resume cron - // @TODO: how to get hostmaster site name? @see drush_provision_post_hostmaster_migrate() - // they use drush_get_option('site_name') - // @TODO Better way? @see drush_hosting_pause() - provision_backend_invoke('hostmaster', 'hosting-setup'); -} - -/** - * drush_sql_sync_init() does what we want! - */ -function drush_provision_devshop_sync_init($source = NULL, $destination = NULL) { - return drush_sql_sync_init($source, $destination); -} From d1b61caced31cd0b26b8d92af58050111f3eee48 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 25 Oct 2012 11:19:36 -0600 Subject: [PATCH 0114/3476] Use the user specified git URL rather than the project one. This was a bug in project platform create. --- devshop_projects/devshop_projects.form.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index f79b55d55..d01319af6 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -350,7 +350,7 @@ function devshop_projects_platform_create_form_submit(&$form, &$form_state){ $project_node = node_load($form_state['values']['nid']); $args = array(); if ($form_state['values']['git_url']) { - $args['git-url'] = $form_state['values']['git-url']; + $args['git-url'] = $form_state['values']['git_url']; } if ($form_state['values']['branch']) { From bd295f7296539ffd933011c2017aafacf181964b Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Thu, 25 Oct 2012 21:21:25 +0200 Subject: [PATCH 0115/3476] Fixed problem with git pull sites --- devshop_pull/devshop_pull.inc | 6 +++--- devshop_pull/devshop_pull.module | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 1ac83f562..4c1d26693 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -67,12 +67,12 @@ function devshop_pull_callback($project, $hash) { } //Search site with pull enable of this project - $platforms = $pnode->project_objects['platform']; - + $sites = $pnode->project_objects['site']; + //Put timestamp db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); - foreach ($platforms as $site_nid => $platform) { + foreach ($sites as $site_nid => $site) { //If pull enabled then add to task if (db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid))) { $message = "Queuing a pull task for node id $site_nid"; diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index dbd2e966f..864b4cbfe 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -121,9 +121,12 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if($node->type == 'project') { $pnode = $node; } - else { + elseif (isset($node->project_nid)) { $pnode = node_load($node->project_nid); } + else { + return; + } // @TODO: PULL QUEUE must be enabled first! Show a notice to the user if Pull Queue // is not enabled! @@ -275,10 +278,10 @@ function devshop_pull_task($nid){ } else { //Search site with pull enabled of this project - $platforms = $node->project_objects['platform']; + $sites = $node->project_objects['site']; - foreach ($platforms as $site_nid => $platform) { + foreach ($sites as $site_nid => $site) { //If pull enabled then add to task $data = db_fetch_object(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid)); if ($data->pull_enabled) { From cd579ce6adf7956ff5a80410e2affef3b45a8d8c Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 30 Oct 2012 21:27:16 +0100 Subject: [PATCH 0116/3476] Jacinto. Move pull to platform instead of sites node --- devshop_pull/devshop_pull.inc | 12 ++--- devshop_pull/devshop_pull.install | 13 +++-- devshop_pull/devshop_pull.module | 83 +++++++++++++++++------------- devshop_tasks/devshop_tasks.module | 29 +++++++++-- 4 files changed, 88 insertions(+), 49 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 4c1d26693..b0a19e515 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -66,19 +66,19 @@ function devshop_pull_callback($project, $hash) { return; } - //Search site with pull enable of this project - $sites = $pnode->project_objects['site']; + //Search platforms with pull enable of this project + $platforms = $pnode->project_objects['platform']; //Put timestamp db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); - foreach ($sites as $site_nid => $site) { + foreach ($platforms as $platform_nid => $site) { //If pull enabled then add to task - if (db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid))) { - $message = "Queuing a pull task for node id $site_nid"; + if (db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $site_nid))) { + $message = "Queuing a pull task for node id $platform_nid"; watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); print "$message
"; - devshop_pull_task($site_nid); + devshop_pull_task($platform_nid); } } } diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index 3b4e28914..dd85f04ac 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -33,9 +33,9 @@ function devshop_pull_schema() { ), 'primary key' => array('project_nid'), ); - $schema['hosting_devshop_pull_sites'] = array( + $schema['hosting_devshop_pull_platforms'] = array( 'fields' => array( - 'site_nid' => array( + 'platform_nid' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, @@ -51,7 +51,7 @@ function devshop_pull_schema() { 'default' => 0, ), ), - 'primary key' => array('site_nid'), + 'primary key' => array('platform_nid'), ); return $schema; @@ -63,6 +63,13 @@ function devshop_pull_schema() { function devshop_pull_install() { // Create tables. drupal_install_schema('devshop_pull'); + + //Enable pull for dev platform + $result = db_query("SELECT DISTINCT object_nid FROM {hosting_devshop_project_object} WHERE env_type='dev' AND object_type='platform'"); + + while ($item = db_fetch_object($result)) { + db_query("INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, pull_enabled, pull_reset) VALUES(%d, 1, 0)", $result->object_nid); + } } /** diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 864b4cbfe..eb305f0a1 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -48,7 +48,7 @@ function devshop_pull_hosting_queues() { 'type' => 'batch', 'name' => t('Pull queue'), 'description' => t('Run git pull on projects configured to do so.'), - 'total_items' => devshop_pull_get_sites(10), + 'total_items' => devshop_pull_get_platforms(10), 'frequency' => strtotime("1 minute", 0), 'singular' => t('project'), 'plural' => t('projects'), @@ -85,15 +85,11 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id){ ), ); } - if ($form_id == 'site_node_form') { - $site = $form['#node']; - if (isset($site->project_nid)) { - if ($site->nid) { - $default_value = $site->pull_enabled; - } - else { - $default_value = in_array($site->project_environment, array('live', 'test')) ? TRUE : FALSE; - } + if ($form_id == 'platform_node_form') { + $platform = $form['#node']; + if (isset($platform->project_nid)) { + $default_value = $platform->nid ? $platform->pull_enabled : FALSE; + $form['pull_enabled'] = array( '#title' => t('Pull on Commit'), '#description' => t('If enabled. When detecting new code changes will be updated automatically.'), @@ -104,7 +100,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id){ '#title' => 'Hard Reset on Pull', '#type' => 'checkbox', '#description' => t('Reset any changes to project files. WARNING: Any uncommitted changes to the project files will be discarded.'), - '#default_value' => $site->pull_reset, + '#default_value' => $platform->pull_reset, ); } } @@ -114,7 +110,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id){ * Implements hook_nodeapi() */ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { - if ($node->type == 'project' || $node->type == 'site') { + if ($node->type == 'project' || $node->type == 'platform') { switch ($op) { case 'view': if (!$a3) { //!teaser @@ -183,10 +179,10 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { $node->last_pull_status = $data->last_pull_status; } } - elseif ($node->type == 'site') { + elseif ($node->type == 'platform') { $data_project = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); if (isset($data_project['project_nid'])) { - $data = db_fetch_array(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_sites} WHERE site_nid = %d", $node->nid)); + $data = db_fetch_array(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); return $data; } } @@ -194,12 +190,12 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { case 'insert': case 'update': if ($node->type == 'project') { - db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); + db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); } - elseif ($node->type == 'site') { - db_query('DELETE FROM {hosting_devshop_pull_sites} WHERE site_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_sites} (site_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_enabled, $node->pull_reset); + elseif ($node->type == 'platform') { + db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_enabled, $node->pull_reset); } break; } @@ -207,17 +203,17 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { } /** - * Get a number of sites that have their pull queue enabled. + * Get a number of platforms that have their pull queue enabled. * * @param $limit - * Limit to a maximum of this number of sites. + * Limit to a maximum of this number of platforms. * @return * An array of site nodes that have a pull queue enabled. * * @TODO Convert to check pull queue sites. */ -function devshop_pull_get_sites($limit = 5) { - $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); +function devshop_pull_get_platforms($limit = 5) { + $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); return db_result($result); } @@ -244,7 +240,7 @@ function devshop_pull_post_hosting_devshop_pull_task($task, $data) { */ function hosting_pull_queue($count) { - $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.env_type = 'dev' AND dpo.object_type='site' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); + $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); while ($project = db_fetch_object($result)) { @@ -254,6 +250,21 @@ function hosting_pull_queue($count) { } +/** + * Helper for get all sites of one platform + */ +function devshop_pull_get_all_sites($nid_platform) { + $names = array(); + + $result = db_query("SELECT distinct nid FROM {hosting_site} WHERE platform=%d", $nid_platform); + while ($nid = db_fetch_object($result)) { + $name = db_result(db_query("SELECT title FROM {node} WHERE nid=%d", $nid->nid)); + $names[$name] = $name; + } + + return $names; +} + /** * Helper for creating the pull code tasks */ @@ -264,7 +275,7 @@ function devshop_pull_task($nid){ $nids = array(); $args = array(); - if ($node->type == 'site'){ + if ($node->type == 'platform'){ $nids[] = $node->nid; // When doing the pull task, don't do a db update or revert. @@ -272,25 +283,27 @@ function devshop_pull_task($nid){ $args[$node->nid] = array( 'reset' => $node->pull_reset, 'update' => 0, - 'rever' => 0, - 'cache' => 1 + 'revert' => 0, + 'cache' => 1, + 'sites' => devshop_pull_get_all_sites($node->nid), ); } else { - //Search site with pull enabled of this project - $sites = $node->project_objects['site']; + //Search platform with pull enabled of this project + $platforms = $node->project_objects['platform']; - foreach ($sites as $site_nid => $site) { + foreach ($platforms as $platform_nid => $platform) { //If pull enabled then add to task - $data = db_fetch_object(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_sites} WHERE site_nid=%d", $site_nid)); + $data = db_fetch_object(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); if ($data->pull_enabled) { - $nids[] = $site_nid; - $args[$node->nid] = array( + $nids[] = $platform_nid; + $args[$platform_nid] = array( 'reset' => $data->pull_reset, 'update' => 0, - 'rever' => 0, - 'cache' => 1 + 'revert' => 0, + 'cache' => 1, + 'sites' => devshop_pull_get_all_sites($platform_nid), ); } } diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index 521380ede..c32928d9e 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -16,12 +16,14 @@ function devshop_tasks_hosting_tasks() { 'description' => t('Recreates all Features and commits the result.'), 'dialog' => TRUE, ); - $pull = false; - $tasks['site']['devshop-pull'] = array( + $tasks['platform']['devshop-pull'] = array( 'title' => t('Pull Code'), - 'description' => t('Pull & verify site code and (optionally) run update.php, clear cache, and revert features.'), + 'description' => t('Pull & verify platform code and (optionally) run update.php, clear cache, and revert features.'), 'dialog' => TRUE, + 'task_permitted' => TRUE, + 'access callback' => 'user_access', + 'access arguments' => array('create devshop-pull task'), ); $tasks['site']['devshop-sync'] = array( @@ -89,6 +91,18 @@ function hosting_task_devshop_commit_form($node) { // @TODO: Provide a DIFF display to give the user an idea of what has changed. return $form; } + +function hosting_task_devshop_pull_form_validate($form, &$form_state) { + $sites = array(); + + foreach ($form_state['values']['parameters']['sites'] as $key => $value) { + if ($value) { + $sites[] = $value; + } + } + $form_state['values']['parameters']['sites'] = implode('|', $sites); +} + /** * Implementation of hook_hosting_task_TASK_TYPE_form(). * @@ -113,14 +127,19 @@ function hosting_task_devshop_pull_form($node) { $form['revert'] = array( '#title' => t('Revert all features after code pull?'), '#type' => 'checkbox', - '#default_value' => $has_features, - '#access' => $has_features, + '#default_value' => 1, ); $form['cache'] = array( '#title' => t('Clear cache after code pull?'), '#type' => 'checkbox', '#default_value' => 1, ); + $form['sites'] = array( + '#title' => t('Sites to update'), + '#type' => 'checkboxes', + '#options' => devshop_pull_get_all_sites($node->nid), + ); + return $form; } From bdbae0dcc3d4e51c948247a9631fa8f702689bed Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Thu, 1 Nov 2012 17:58:54 -0600 Subject: [PATCH 0117/3476] Basic testing host side working. Need to code and hook up banckend. --- devshop_projects/devshop_projects.form.inc | 12 +- .../devshop_projects_platform.module | 20 ++++ .../devshop_projects_testing.info | 1 - .../devshop_projects_testing.module | 109 +++++++++++++++++- 4 files changed, 137 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index d01319af6..aaf061756 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -107,8 +107,16 @@ function devshop_projects_form(&$node) { */ function devshop_projects_validate($node, &$form) { - // No validation if op == Delete - if ($node->op == t('Delete')) { + // It may look find of strange that we are doing a node load when the + // exact node we want is passed in here. But if this is an edit of an + // existing node, not all of the node fields (like project_status) have + // been initialized yet. + + $pnode = node_load($node->nid); + + // No validation if op == Delete or project_status == site_ready. The + // latter case happens when the user is editing the tests to run fied. + if ($node->op == t('Delete') || $pnode->project_status == 'sites_ready') { return; } diff --git a/devshop_projects_platform/devshop_projects_platform.module b/devshop_projects_platform/devshop_projects_platform.module index 7d4bfd9d8..9b0838f1f 100644 --- a/devshop_projects_platform/devshop_projects_platform.module +++ b/devshop_projects_platform/devshop_projects_platform.module @@ -118,3 +118,23 @@ function devshop_projects_platform_nodeapi(&$node, $op, $a3 = null) { } +/* + * Implementation of hook_post_hosting_TASK_TYPE_task + * + */ + +function devshop_projects_platform_post_hosting_create_task($task, $data) { + watchdog('devshop', "Create post task hook called"); + watchdog('devshop', print_r($task, 1)); + + +} + +/** + * Implementation of hook_hosting_TASK_TYPE_task_rollback(). + * + */ +function devshop_projects_platform_hosting_create_task_rollback($task, $data) { + watchdog('devshop', "Create task rollback hook called"); + watchdog('devshop', print_r($task, 1)); +} diff --git a/devshop_projects_testing/devshop_projects_testing.info b/devshop_projects_testing/devshop_projects_testing.info index 8cc0770c9..eb55d4c15 100644 --- a/devshop_projects_testing/devshop_projects_testing.info +++ b/devshop_projects_testing/devshop_projects_testing.info @@ -2,5 +2,4 @@ name = DevShop Projects Testing description = A DevShop Projects module that allows the user to incorporate simple tests within a project and have the test run automatically. core = 6.x package = DevShop -dependencies[] = devshop_hosting ~ diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index 5309e5d19..8e262f404 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -11,20 +11,125 @@ */ function devshop_projects_testing_perm() { return array( + 'run project tests', ); } +/** + * Implementation of hook_menu() + */ +function devshop_projects_testing_menu() { + + $items['hosting/projects/testing/%'] = array( + 'title' => t('Run Tests'), + 'description' => 'Run a sequence of tests configured for this project', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('devshop_projects_testing_run_tests_form', 3), + 'access arguments' => array('run project tests'), + ); + + return ($items); +} + /** * Implements hook_form_alter(). */ -function devshop_projects_testing_form_alter(&$form, &$form_state, $form_id){ +function devshop_projects_testing_form_alter(&$form, &$form_state, $form_id) { + + if ($form_id == 'project_node_form') { + $form['tests_to_run'] = array( + '#type' => 'textarea', + '#title' => t('Tests To Run'), + '#default_value' => $form['#node']->tests_to_run, + '#rows' => 6, + '#description' => t('Enter the names of the simpletests to run when this project receives a commit, one per line.'), + '#weight' => 20, + ); + } + else if ($form_id == 'devshop_projects_install_sites_form') { + $node = $form['#parameters'][2]; + $form['tests_to_run'] = array( + '#type' => 'item', + '#value' => l(t('Run Tests'), + "hosting/projects/testing/$node->nid"), + ); + + } } /** * Implements hook_nodeapi() */ -function devshop_projects_platform_nodeapi(&$node, $op, $a3 = null) { +function devshop_projects_testing_nodeapi(&$node, $op, $a3 = null) { + + if ($node->type != 'project' || $a3 != null) { + return; + } + + switch ($op) { + case 'load': + $data = db_fetch_array(db_query("SELECT tests_to_run " . + "FROM {hosting_devshop_project_testing} ". + "WHERE project_nid = %d", $node->nid)); + return $data; + + case 'view': + + break; + + + case 'insert': + case 'update': + db_query("DELETE FROM {hosting_devshop_project_testing} " . + "WHERE project_nid = %d", $node->nid); + + db_query("INSERT INTO {hosting_devshop_project_testing} " . + "(project_nid, tests_to_run) " . + "VALUES(%d, '%s')", $node->nid, $node->tests_to_run); + break; + + case 'delete': + db_query("DELETE FROM {hosting_devshop_project_testing} " . + "WHERE project_nid = %d", $node->nid); + break; + + default: + break; + } } +function devshop_projects_testing_run_tests_form($form_state, $project_nid) { + $project_node = node_load($project_nid); + + $form['tests_to_run'] = array( + '#type' => 'item', + '#title' => t('Tests To Run'), + '#value' => str_replace("\n", "
", $project_node->tests_to_run), + ); + + $form['sync'] = array( + '#type' => 'checkbox', + '#title' => t("Sync from Live before testing?"), + '#default_value' => FALSE, + ); + + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_nid, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Run Tests'), + '#weight' => 32, + ); + $form['cancel'] = array( + '#type' => 'submit', + '#value' => t('Cancel'), + '#weight' => 33, + ); + + return $form; +} From 8286666cd3981c3246cbbd262b0457f1176614dd Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Mon, 5 Nov 2012 18:00:28 -0600 Subject: [PATCH 0118/3476] Issue #1831060: Fix check for existing site when creating a new platform --- devshop_projects/devshop_projects.task.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 4b4c4eb65..b203889ae 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -172,16 +172,19 @@ function devshop_projects_post_hosting_verify_task($task, $data) { return; } +/* if (in_array($p_obj->env_type, array('dev', 'test', 'live'))) { return; } +*/ // Make sure we don't already have a site $s_obj = db_fetch_object(db_query("SELECT * " . "FROM {hosting_devshop_project_object} " . "WHERE object_type = 'site' AND " . - " env_type = '%s'", - $p_obj->env_type)); + " env_type = '%s' AND " . + " project_nid = %d", + $p_obj->env_type, $p_obj->project_nid)); // See if site exists if ($s_obj) { From be7df9a4276007dcc664b1e7d355269082b41fef Mon Sep 17 00:00:00 2001 From: wodenx Date: Tue, 6 Nov 2012 10:39:00 -0700 Subject: [PATCH 0119/3476] Fixed bug which causes platform create to fail when a platform of the same code name exists in anotjer project. --- devshop_projects/devshop_projects.task.inc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 4b4c4eb65..b2a46cfb7 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -172,16 +172,13 @@ function devshop_projects_post_hosting_verify_task($task, $data) { return; } - if (in_array($p_obj->env_type, array('dev', 'test', 'live'))) { - return; - } - // Make sure we don't already have a site $s_obj = db_fetch_object(db_query("SELECT * " . "FROM {hosting_devshop_project_object} " . "WHERE object_type = 'site' AND " . - " env_type = '%s'", - $p_obj->env_type)); + " env_type = '%s' AND " . + " project_nid = %d", + $p_obj->env_type. $p_obj->project_nid)); // See if site exists if ($s_obj) { From 1cb3af70861c7cba7ead533e87be1e80b723170c Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Tue, 6 Nov 2012 17:56:11 -0700 Subject: [PATCH 0120/3476] Refactored code to use task button and pop-up confirm dialog --- .../devshop_projects_testing.module | 88 +++++++++---------- 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index 8e262f404..928aac55a 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -15,22 +15,6 @@ function devshop_projects_testing_perm() { ); } -/** - * Implementation of hook_menu() - */ -function devshop_projects_testing_menu() { - - $items['hosting/projects/testing/%'] = array( - 'title' => t('Run Tests'), - 'description' => 'Run a sequence of tests configured for this project', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('devshop_projects_testing_run_tests_form', 3), - 'access arguments' => array('run project tests'), - ); - - return ($items); -} - /** * Implements hook_form_alter(). */ @@ -46,15 +30,6 @@ function devshop_projects_testing_form_alter(&$form, &$form_state, $form_id) { '#weight' => 20, ); } - else if ($form_id == 'devshop_projects_install_sites_form') { - $node = $form['#parameters'][2]; - $form['tests_to_run'] = array( - '#type' => 'item', - '#value' => l(t('Run Tests'), - "hosting/projects/testing/$node->nid"), - ); - - } } /** @@ -99,13 +74,47 @@ function devshop_projects_testing_nodeapi(&$node, $op, $a3 = null) { } -function devshop_projects_testing_run_tests_form($form_state, $project_nid) { - $project_node = node_load($project_nid); +/** + * Implementation of hook_hosting_tasks() + * + */ - $form['tests_to_run'] = array( - '#type' => 'item', - '#title' => t('Tests To Run'), - '#value' => str_replace("\n", "
", $project_node->tests_to_run), +function devshop_projects_testing_hosting_tasks() { + $tasks = array(); + + $tasks['site']['devshop-run-tests'] = array( + 'title' => t('Run Tests'), + 'description' => t('Run a group of previousely defined SimpleTest tests.'), + 'dialog' => TRUE, + 'task_permitted' => TRUE, + ); + + return $tasks; +} + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form(). + * + * For "Commit" task. + */ + +function hosting_task_devshop_run_tests_form($node) { + + if ($node->project_nid) { + $project_node = node_load($node->project_nid); + $tests = $project_node->tests_to_run; + } + else { + $tests = ""; + } + + $form['tests_to_run'] = array( + '#type' => 'textarea', + '#title' => t('Tests To Run'), + '#value' => $tests, + '#default_value' => $tests, + '#rows' => 6, + '#description' => t('Enter the names of the simpletests to run when this project receives a commit, one per line.'), ); $form['sync'] = array( @@ -114,22 +123,5 @@ function devshop_projects_testing_run_tests_form($form_state, $project_nid) { '#default_value' => FALSE, ); - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_nid, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Run Tests'), - '#weight' => 32, - ); - - $form['cancel'] = array( - '#type' => 'submit', - '#value' => t('Cancel'), - '#weight' => 33, - ); - return $form; } From aca19a26ed2001297402ec6da71d28e5da02211c Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Wed, 7 Nov 2012 15:22:21 -0700 Subject: [PATCH 0121/3476] Front end work for run tests complete --- .../devshop_projects_testing.module | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index 928aac55a..6e2321357 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -87,11 +87,42 @@ function devshop_projects_testing_hosting_tasks() { 'description' => t('Run a group of previousely defined SimpleTest tests.'), 'dialog' => TRUE, 'task_permitted' => TRUE, + 'access callback' => devshop_projects_testing_access, ); return $tasks; } +/* + * Task access callback hook + * + * Returne TRUE is this is a site node and qualifies to run tests. + * To qualify, it much be part of a devshop project group and must + * be either the 'test' site or one of the branch sites. + */ + +function devshop_projects_testing_access($node, $type) { + + if ($node->type != 'site' || !$node->project_nid) { + return FALSE; + } + + $query = db_query("SELECT * FROM {hosting_devshop_project_object} " . + "WHERE object_nid = %d", $node->nid); + + if (!($obj = db_fetch_object($query))) { + // This should not happen, but let's check for it anyway + return FALSE; + } + + if ($obj->env_type == 'live' || $obj->env_type == 'dev') { + return FALSE; + } + + // Everything else is OK + return TRUE; +} + /** * Implementation of hook_hosting_task_TASK_TYPE_form(). * From 624bc9648691d633a2c7b1e6f5e725374f948b55 Mon Sep 17 00:00:00 2001 From: Kory Hamzeh Date: Tue, 13 Nov 2012 15:05:34 -0700 Subject: [PATCH 0122/3476] Added form validation and changed test name to devshop-test --- .../devshop_projects_testing.module | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index 6e2321357..b8a255f18 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -82,7 +82,7 @@ function devshop_projects_testing_nodeapi(&$node, $op, $a3 = null) { function devshop_projects_testing_hosting_tasks() { $tasks = array(); - $tasks['site']['devshop-run-tests'] = array( + $tasks['site']['devshop-test'] = array( 'title' => t('Run Tests'), 'description' => t('Run a group of previousely defined SimpleTest tests.'), 'dialog' => TRUE, @@ -126,10 +126,9 @@ function devshop_projects_testing_access($node, $type) { /** * Implementation of hook_hosting_task_TASK_TYPE_form(). * - * For "Commit" task. */ -function hosting_task_devshop_run_tests_form($node) { +function hosting_task_devshop_test_form($node) { if ($node->project_nid) { $project_node = node_load($node->project_nid); @@ -142,7 +141,6 @@ function hosting_task_devshop_run_tests_form($node) { $form['tests_to_run'] = array( '#type' => 'textarea', '#title' => t('Tests To Run'), - '#value' => $tests, '#default_value' => $tests, '#rows' => 6, '#description' => t('Enter the names of the simpletests to run when this project receives a commit, one per line.'), @@ -156,3 +154,21 @@ function hosting_task_devshop_run_tests_form($node) { return $form; } + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form_validate(). + * + */ + +function hosting_task_devshop_test_form_validate($form, &$form_state) { + + $tests = $form_state['values']['parameters']['tests_to_run']; + $t = trim(str_replace("\n", "", $tests)); + + if (empty($tests) || empty($t) || $tests == "" || $t == ""){ + form_set_error('tests_to_run', t('You must include at least one test.')); + } + + +} + From 0d4da657a74235e8884dfac401191d5e56bdef96 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 27 Nov 2012 11:13:37 -0600 Subject: [PATCH 0123/3476] a bit of a refactor: sites were being created everytime you verified a platform --- devshop_projects/devshop_projects.task.inc | 47 ++++++++++------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index b2a46cfb7..290c4dae5 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -149,9 +149,13 @@ function devshop_projects_hosting_task_table($node) { * Implementation of hook_post_hosting_TASK_TYPE_task * * If a new platform has been added to a project and the env - * is not dev, test, or live, auto-create a site. + * is not dev, test, or live, auto-create a site.... + * + * + * @TODO: This is used only for creating the new site when a "branch platform" + * is created... we should save something in the task or platform node so we + * don't have to use this logic. */ - function devshop_projects_post_hosting_verify_task($task, $data) { // We only case about platforms. @@ -159,39 +163,32 @@ function devshop_projects_post_hosting_verify_task($task, $data) { return; } - // Get platform nid. + // Get objects $nid = $task->ref->nid; + $platform = node_load($nid); - // Get project object information about this platform - $p_obj = db_fetch_object(db_query("SELECT * " . - "FROM {hosting_devshop_project_object} " . - "WHERE object_nid = %d", $nid)); - - // Not in a project - if (!$p_obj) { + // If this platform isn't in a project, bail. + if (empty($platform->project_nid)){ + return; + } + + // Get the project + $project = node_load($platform->project_nid); + + // If the project doesn't have an install profile chosen yet, bail. + if (empty($project->install_profile)){ return; } - // Make sure we don't already have a site - $s_obj = db_fetch_object(db_query("SELECT * " . - "FROM {hosting_devshop_project_object} " . - "WHERE object_type = 'site' AND " . - " env_type = '%s' AND " . - " project_nid = %d", - $p_obj->env_type. $p_obj->project_nid)); - - // See if site exists - if ($s_obj) { + // If the project has a site already + $sites = array_flip($project->project_objects['site']); + if (isset($sites[$platform->project_environment])){ return; } // So this is a platform which is in a project and it not dev, test, or // live. Let's create a site based off of this platform. - - $platform = node_load($nid); - $project = node_load($p_obj->project_nid); - - devshop_projects_create_site($project, $platform, $p_obj->env_type); + devshop_projects_create_site($project, $platform, $platform->project_environment); } function watchcat($m, $l) { From 3669264e60fd09af6e8e5d83128869abe602b874 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 27 Nov 2012 11:23:47 -0600 Subject: [PATCH 0124/3476] comments cleanup --- devshop_projects/devshop_projects.task.inc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 290c4dae5..2c617e977 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -212,7 +212,6 @@ function watchcat($m, $l) { * The project node that is to be deleted. * */ - function devshop_projects_project_delete_queue_next_task($pnode) { // Assume all done $updated = FALSE; @@ -393,7 +392,6 @@ function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { * Implementation of hook_post_hosting_TASK_TYPE_task * */ - function devshop_projects_post_hosting_delete_task($task, $data) { watchdog('devshop', "Delete post task hook called"); devshop_projects_project_delete_continue($task->ref->nid, 'delete'); @@ -412,11 +410,10 @@ function devshop_projects_hosting_delete_task_rollback($task, $data) { devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); } -/* +/** * Implementation of hook_post_hosting_TASK_TYPE_task * */ - function devshop_projects_post_hosting_disable_task($task, $data) { watchdog('devshop', "Disable post task hook called"); devshop_projects_project_delete_continue($task->ref->nid, 'disable'); @@ -433,10 +430,9 @@ function devshop_projects_hosting_disable_task_rollback($task, $data) { } -/* +/** * Returns TRUE is the project create task for the given node failed. */ - function devshop_project_project_create_failed($nid, &$task) { if ($nid) { From f08b14f3c3eabbc35738c14724eb980879943677 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 10:35:58 -0500 Subject: [PATCH 0125/3476] removing hosting.feature.pull.inc... its really not needed. --- devshop_pull/hosting.feature.pull.inc | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 devshop_pull/hosting.feature.pull.inc diff --git a/devshop_pull/hosting.feature.pull.inc b/devshop_pull/hosting.feature.pull.inc deleted file mode 100644 index ddb63f83c..000000000 --- a/devshop_pull/hosting.feature.pull.inc +++ /dev/null @@ -1,14 +0,0 @@ - t('DevShop Pull Queue'), - 'description' => t('Provides a queue for pulling a Git powered project.'), - 'status' => HOSTING_FEATURE_ENABLED, - ); - return $features; -} From a1ec0a9f9db9fc5292957cb8e90504b91ca85bf0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 13:48:16 -0500 Subject: [PATCH 0126/3476] changing "readonly" to disabled. --- devshop_projects/devshop_projects.form.inc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index aaf061756..520b87be3 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -85,17 +85,11 @@ function devshop_projects_form(&$node) { // Don't allow editing if ($node->nid) { -// $form['code_path']['#type'] = $form['title']['#type'] = 'item'; - $form['code_path']['#value'] = $form['code_path']['#default_value']; - $form['title']['#value'] = $form['title']['#default_value']; - - $form['code_path']['#attributes'] = array('readonly' => 'readonly'); - $form['title']['#attributes'] = array('readonly' => 'readonly'); + $form['code_path']['#disabled'] = TRUE; + $form['title']['#disabled'] = TRUE; if (!$retry) { -// $form['git_url']['#type'] = 'item'; - $form['git_url']['#attributes'] = array('readonly' => 'readonly'); - $form['git_url']['#value'] = $form['git_url']['#default_value']; + $form['git_url']['#disabled'] = TRUE; } } From b34aa3994196fec5fe71649a4bb64e6c3614d2f9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 13:50:23 -0500 Subject: [PATCH 0127/3476] oops, still need values! --- devshop_projects/devshop_projects.form.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 520b87be3..a7eee314d 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -85,10 +85,14 @@ function devshop_projects_form(&$node) { // Don't allow editing if ($node->nid) { + $form['code_path']['#value'] = $form['code_path']['#default_value']; $form['code_path']['#disabled'] = TRUE; + + $form['title']['#value'] = $form['title']['#default_value']; $form['title']['#disabled'] = TRUE; if (!$retry) { + $form['git_url']['#value'] = $form['git_url']['#default_value']; $form['git_url']['#disabled'] = TRUE; } } From 532289b9e1c897a533cdbb396d73056cc272dbf6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 14:24:15 -0500 Subject: [PATCH 0128/3476] adding to settings form: a tool for enabling pull on all projects --- devshop_pull/devshop_pull.module | 3 +- devshop_pull/devshop_pull.settings.inc | 73 +++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index eb305f0a1..5426cb893 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -26,8 +26,7 @@ function devshop_pull_menu() { $items['admin/hosting/devshop_pull'] = array( 'title' => 'DevShop Pull Settings', 'description' => 'Configure Pull Code URL callback ', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('devshop_pull_settings'), + 'page callback' => 'devshop_pull_settings_page', 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer hosting settings'), 'file' => 'devshop_pull.settings.inc' diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index 7bfdab174..643f8c35e 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -1,9 +1,18 @@ 'textarea', '#title' => t('Control Access by IP'), @@ -14,3 +23,65 @@ function devshop_pull_settings() { ); return system_settings_form($form); } + +/** + * Tool to enable pull on all projects. + */ +function devshop_pull_reset_form() { + $form['note'] = array( + '#value' => '

' . t('Reset all Projects') . '

' . '

' . t('If you have a lot of projects and need to turn them all on, you can do so here.') . '

', + ); + $form['pull_method'] = array( + '#title' => 'Automatic Git Pull Method', + '#type' => 'radios', + '#default_value' => DEVSHOP_PULL_DISABLED, + '#options' => array( +DEVSHOP_PULL_DISABLED => t('Pull disabled.'), +DEVSHOP_PULL_QUEUE => t('Pull on queue (every minute).'), +DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), + + ), + ); + + $form['enable_dev_sites'] = array( + '#title' => t('On all dev sites, Enable Pull on Commit and Reset Hard.'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Enable Pull for all projects'), + ); + return $form; +} + + +/** + * Submit function for devshop_pull_reset_form() + */ +function devshop_pull_reset_form_submit($form, &$form_state) { + /* + // Clear out tables + db_query('TRUNCATE {hosting_devshop_pull_platforms}'); + db_query('TRUNCATE {hosting_devshop_pull_projects}'); + + // Find all projects + $query = db_query('SELECT nid FROM node WHERE type="project" AND status=1;'); + while($node = db_fetch_object($query)){ + $pull_data = array(); + $pull_data['project_nid'] = $node->nid; + $pull_data['pull_method'] = $form_state['values']['pull_method']; + drupal_write_record('hosting_devshop_pull_projects', $pull_data); + } + + // Loop through and insert data into tables + + // Find all dev sites + + // Loop through and insert data into tables + + drupal_set_message(t('Set all projects to !method', array('!method' => $form_state['values']['pull_method']))); + */ + drupal_set_message('Not yet implemented!'); +} \ No newline at end of file From 37099a37e33fa7cc3d07aa56a42141df06d6608c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 14:37:18 -0500 Subject: [PATCH 0129/3476] attempted fix --- devshop_pull/devshop_pull.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index b0a19e515..aed88fbbd 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -74,7 +74,8 @@ function devshop_pull_callback($project, $hash) { foreach ($platforms as $platform_nid => $site) { //If pull enabled then add to task - if (db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $site_nid))) { + $enabled = db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); + if ($enabled) { $message = "Queuing a pull task for node id $platform_nid"; watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); print "$message
"; From e14c21615440f1773d922026bd3c4dea875a1bac Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 14:46:09 -0500 Subject: [PATCH 0130/3476] moving pull back to site node --- devshop_tasks/devshop_tasks.module | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module index c32928d9e..213aea816 100644 --- a/devshop_tasks/devshop_tasks.module +++ b/devshop_tasks/devshop_tasks.module @@ -17,7 +17,7 @@ function devshop_tasks_hosting_tasks() { 'dialog' => TRUE, ); - $tasks['platform']['devshop-pull'] = array( + $tasks['site']['devshop-pull'] = array( 'title' => t('Pull Code'), 'description' => t('Pull & verify platform code and (optionally) run update.php, clear cache, and revert features.'), 'dialog' => TRUE, @@ -92,17 +92,6 @@ function hosting_task_devshop_commit_form($node) { return $form; } -function hosting_task_devshop_pull_form_validate($form, &$form_state) { - $sites = array(); - - foreach ($form_state['values']['parameters']['sites'] as $key => $value) { - if ($value) { - $sites[] = $value; - } - } - $form_state['values']['parameters']['sites'] = implode('|', $sites); -} - /** * Implementation of hook_hosting_task_TASK_TYPE_form(). * @@ -134,12 +123,6 @@ function hosting_task_devshop_pull_form($node) { '#type' => 'checkbox', '#default_value' => 1, ); - $form['sites'] = array( - '#title' => t('Sites to update'), - '#type' => 'checkboxes', - '#options' => devshop_pull_get_all_sites($node->nid), - ); - return $form; } From 626f4c4016f3573ec6904dac3d1222fca83ce30e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 15:09:49 -0500 Subject: [PATCH 0131/3476] refactoring --- devshop_pull/devshop_pull.inc | 2 +- devshop_pull/devshop_pull.module | 33 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index aed88fbbd..2299b369c 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -76,7 +76,7 @@ function devshop_pull_callback($project, $hash) { //If pull enabled then add to task $enabled = db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); if ($enabled) { - $message = "Queuing a pull task for node id $platform_nid"; + $message = "devshop_pull_task($platform_nid)"; watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); print "$message
"; devshop_pull_task($platform_nid); diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 5426cb893..ad6a0066c 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -254,13 +254,10 @@ function hosting_pull_queue($count) { */ function devshop_pull_get_all_sites($nid_platform) { $names = array(); - - $result = db_query("SELECT distinct nid FROM {hosting_site} WHERE platform=%d", $nid_platform); - while ($nid = db_fetch_object($result)) { - $name = db_result(db_query("SELECT title FROM {node} WHERE nid=%d", $nid->nid)); - $names[$name] = $name; + $result = db_query("SELECT title, hs.nid FROM {hosting_site} hs LEFT JOIN {node} n ON hs.nid = n.nid WHERE platform=%d", $nid_platform); + while ($node = db_fetch_object($result)) { + $names[$node->nid] = $node->title; } - return $names; } @@ -273,19 +270,21 @@ function devshop_pull_task($nid){ $nids = array(); $args = array(); - if ($node->type == 'platform'){ - $nids[] = $node->nid; + $sites = devshop_pull_get_all_sites($node->nid); +print_r($sites); + foreach ($sites as $nid => $site){ + $nids[] = $nid; - // When doing the pull task, don't do a db update or revert. - // Just clear all of the caches - $args[$node->nid] = array( - 'reset' => $node->pull_reset, - 'update' => 0, - 'revert' => 0, - 'cache' => 1, - 'sites' => devshop_pull_get_all_sites($node->nid), - ); + // When doing the pull task, don't do a db update or revert. + // Just clear all of the caches + $args[$nid] = array( + 'reset' => $node->pull_reset, + 'update' => 0, + 'revert' => 0, + 'cache' => 1, + ); + } } else { //Search platform with pull enabled of this project From 6127f81f432ee55ae4335135da07f8c1c51cdcaa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 28 Nov 2012 17:15:42 -0600 Subject: [PATCH 0132/3476] dont show project link if there is not project for this node --- devshop_projects/devshop_projects.node.inc | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index c3be4fa5e..16f8fb817 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -58,18 +58,20 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Display Project info if ($op == 'view'){ $project = node_load($node->project_nid); - $node->content['info']['project'] = array( - '#type' => 'item', - '#title' => t('Project'), - '#value' => l($project->title, "node/$project->nid"), - '#weight' => -12 - ); - $node->content['info']['env'] = array( - '#type' => 'item', - '#title' => t('Environment Type'), - '#value' => $node->project_environment, - '#weight' => -11 - ); + if (!empty($project)){ + $node->content['info']['project'] = array( + '#type' => 'item', + '#title' => t('Project'), + '#value' => l($project->title, "node/$project->nid"), + '#weight' => -12 + ); + $node->content['info']['env'] = array( + '#type' => 'item', + '#title' => t('Environment Type'), + '#value' => $node->project_environment, + '#weight' => -11 + ); + } } // The NODE is being deleted. Remove the nid from the objects table From 23d12a5963f6dc40c023845de089442344ed4e03 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 29 Nov 2012 12:28:51 -0500 Subject: [PATCH 0133/3476] Moving Projects menu item to primary links. Cleaning up comments --- devshop_projects/devshop_projects.module | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index d321f2562..0e73cb055 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -30,6 +30,8 @@ function devshop_projects_menu() { 'description' => 'Display a list of all projects', 'page callback' => 'devshop_projects_projects_view', 'access arguments' => array('view projects'), + 'menu_name' => 'primary-links', + 'weight' => 1, ); $items['hosting/projects/platform/create/%'] = array( @@ -113,6 +115,8 @@ function devshop_projects_hosting_js_page(){ /** * Status display + * + * @TODO: All of it! */ function devshop_projects_project_status(){ @@ -142,7 +146,6 @@ function devshop_projects_hosting_drush_aliases_name($node) { /* * Helper function to create a site in a project */ - function devshop_projects_create_site($project_node, $platform_node, $env) { global $user; @@ -176,19 +179,17 @@ function devshop_projects_create_site($project_node, $platform_node, $env) { } } -/* +/** * Helper function which writes a serialize array in the project file */ - function devshop_projects_project_data_set($nid, $data) { db_query("UPDATE {hosting_devshop_project} SET data = '%s' WHERE nid = %d", serialize($data), $nid); } -/* +/** * Helper function which reads the serialize array in the project file */ - function devshop_projects_project_data_get($nid) { $sdata = db_result(db_query("SELECT data FROM {hosting_devshop_project} " . From cde6ce40829d36c52a45a2a5684569a3ad64c6da Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 29 Nov 2012 13:49:36 -0500 Subject: [PATCH 0134/3476] better form description --- devshop_projects_testing/devshop_projects_testing.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index b8a255f18..ab4493dc2 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -26,7 +26,7 @@ function devshop_projects_testing_form_alter(&$form, &$form_state, $form_id) { '#title' => t('Tests To Run'), '#default_value' => $form['#node']->tests_to_run, '#rows' => 6, - '#description' => t('Enter the names of the simpletests to run when this project receives a commit, one per line.'), + '#description' => t('Enter the names of the simpletests to trigger when "Run Tests" task is started, one per line. See the !link for examples.', array('!link' => l(t('Drupal API'), 'http://api.drupal.org/api/drupal/modules%21simpletest%21drupal_web_test_case.php/class/DrupalWebTestCase/7'))), '#weight' => 20, ); } From 8bff0ed1f1114a2e51d712e4dc94f957e1e7ec0b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 29 Nov 2012 17:19:46 -0500 Subject: [PATCH 0135/3476] menu callback for pull link --- devshop_pull/devshop_pull.module | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index ad6a0066c..586d6ff52 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -29,12 +29,13 @@ function devshop_pull_menu() { 'page callback' => 'devshop_pull_settings_page', 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer hosting settings'), - 'file' => 'devshop_pull.settings.inc' + 'file' => 'devshop_pull.settings.inc', ); $items[DEVSHOP_PULL_CALLBACK_URL] = array( 'page callback' => 'devshop_pull_callback', 'access arguments' => array('access devshop pull callback'), - 'file' => 'devshop_pull.inc' + 'file' => 'devshop_pull.inc', + 'type' => MENU_CALLBACK, ); return $items; } From 1fecf60908051cd09db2f98b5f0c6b02b57f2911 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 29 Nov 2012 17:21:46 -0500 Subject: [PATCH 0136/3476] getting rid of pnode, slight refactor. --- devshop_projects/devshop_projects.form.inc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index a7eee314d..92ef156e1 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -110,14 +110,15 @@ function devshop_projects_validate($node, &$form) { // existing node, not all of the node fields (like project_status) have // been initialized yet. - $pnode = node_load($node->nid); - + $node = node_load($node->nid); + // No validation if op == Delete or project_status == site_ready. The // latter case happens when the user is editing the tests to run fied. - if ($node->op == t('Delete') || $pnode->project_status == 'sites_ready') { + if ($node->op == t('Delete') || $node->project_status == 'sites_ready') { return; } + // Full validation on when a creating a new node $add = (arg(1) == 'add' ? TRUE : FALSE); @@ -127,8 +128,11 @@ function devshop_projects_validate($node, &$form) { } // The project code name must not be in the hosting_context table - if (!$node->retry && ($result = db_fetch_object(db_query("SELECT name FROM {hosting_context} WHERE name = '%s'", $node->title)))) { - form_set_error('title', t('Project code name existing in hosting context table.')); + if (!$node->retry){ + $result = db_fetch_object(db_query("SELECT name, nid FROM {hosting_context} WHERE name = '%s'", $node->title)); + if ($node->nid != $result->nid){ + form_set_error('title', t('Project code name is unavailable. Please choose another.')); + } } // The project code name must be unique From 523dbb31fe42dda1363ef18d617b7dd2a82b1c57 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 30 Nov 2012 16:46:10 -0500 Subject: [PATCH 0137/3476] Fixing bad access callback string. --- devshop_projects_testing/devshop_projects_testing.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index ab4493dc2..9afed0c3c 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -87,7 +87,7 @@ function devshop_projects_testing_hosting_tasks() { 'description' => t('Run a group of previousely defined SimpleTest tests.'), 'dialog' => TRUE, 'task_permitted' => TRUE, - 'access callback' => devshop_projects_testing_access, + 'access callback' => 'devshop_projects_testing_access', ); return $tasks; From 11eb02666154b286effccb5d9d1a6f654e094953 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 30 Nov 2012 16:47:00 -0500 Subject: [PATCH 0138/3476] comment whitespace fix --- devshop_projects_testing/devshop_projects_testing.module | 4 ---- 1 file changed, 4 deletions(-) diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index 9afed0c3c..d098bab8e 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -78,7 +78,6 @@ function devshop_projects_testing_nodeapi(&$node, $op, $a3 = null) { * Implementation of hook_hosting_tasks() * */ - function devshop_projects_testing_hosting_tasks() { $tasks = array(); @@ -100,7 +99,6 @@ function devshop_projects_testing_hosting_tasks() { * To qualify, it much be part of a devshop project group and must * be either the 'test' site or one of the branch sites. */ - function devshop_projects_testing_access($node, $type) { if ($node->type != 'site' || !$node->project_nid) { @@ -127,7 +125,6 @@ function devshop_projects_testing_access($node, $type) { * Implementation of hook_hosting_task_TASK_TYPE_form(). * */ - function hosting_task_devshop_test_form($node) { if ($node->project_nid) { @@ -159,7 +156,6 @@ function hosting_task_devshop_test_form($node) { * Implementation of hook_hosting_task_TASK_TYPE_form_validate(). * */ - function hosting_task_devshop_test_form_validate($form, &$form_state) { $tests = $form_state['values']['parameters']['tests_to_run']; From 0d6ebc98a5d833d226ba23a37e33637c449ba3e5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 30 Nov 2012 16:48:16 -0500 Subject: [PATCH 0139/3476] fix missing header array --- devshop_projects/devshop_projects.node.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 16f8fb817..736446560 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -272,6 +272,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = l('Commit Log', "node/$site->platform/gitlog"); $rows[] = $row; } + $header = array(); $table = theme('table', $header, $rows); $node->content['sites'] = array( From bc25aaaabbc5012944753a06477bf8bd6288d44a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 30 Nov 2012 16:49:01 -0500 Subject: [PATCH 0140/3476] fixing whitespace indentation --- devshop_projects/devshop_projects.node.inc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 736446560..d70afca39 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -253,21 +253,21 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $row = array(); if ($site->site_status != -2) { - $url = "http://$site->title"; - $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); - $row[] = l('Site', "node/$site->nid"); + $url = "http://$site->title"; + $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); + $row[] = l('Site', "node/$site->nid"); } else { - // Site if deleted, just show the platform name - $row[] = $platform->title; - $row[] = ''; + // Site if deleted, just show the platform name + $row[] = $platform->title; + $row[] = ''; } $row[] = l('Platform', "node/$site->platform"); if ($site->site_status != -2) { - $row[] = l('Download Database', "$url/admin/status/"); + $row[] = l('Download Database', "$url/admin/status/"); } else { - $row[] = ''; + $row[] = ''; } $row[] = l('Commit Log', "node/$site->platform/gitlog"); $rows[] = $row; From 792c82c33789ea14624fbf2403523c07e697add4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 30 Nov 2012 17:14:33 -0500 Subject: [PATCH 0141/3476] fixing misspelled access callback --- devshop_log/devshop_log.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 9cfde0a34..ea1d43349 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -20,7 +20,7 @@ function devshop_log_perm() { * viewing is a platform node. */ -function _devsop_log_access_callback($nid) { +function _devshop_log_access_callback($nid) { if (!user_access('view git commit logs')) { return FALSE; @@ -53,7 +53,7 @@ function devshop_log_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_log_view_form', 1), 'type' => MENU_LOCAL_TASK, - 'access callback' => _devsop_log_access_callback, + 'access callback' => '_devshop_log_access_callback', 'access arguments' => array(1), 'weight' => 4, ); From a18ec204f0f169b8454b4ef198fd07174e174684 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 16:34:56 -0500 Subject: [PATCH 0142/3476] first commit of the wizard file --- devshop_projects/devshop_projects.wizard.inc | 293 +++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100755 devshop_projects/devshop_projects.wizard.inc diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc new file mode 100755 index 000000000..33731f408 --- /dev/null +++ b/devshop_projects/devshop_projects.wizard.inc @@ -0,0 +1,293 @@ + 'devshop_project_create', + 'path' => "node/add/project/%step", + 'show trail' => TRUE, + 'show back' => TRUE, + 'show cancel' => TRUE, + 'show return' => FALSE, + 'next text' => 'Next', + 'next callback' => 'devshop_projects_create_wizard_next', + 'finish callback' => 'wombat_basic_add_subtask_finish', + 'return callback' => 'wombat_basic_add_subtask_finish', + 'cancel callback' => 'wombat_basic_add_subtask_cancel', + 'order' => array( + 'git_url' => t('Step 1: Get Code'), + 'environments' => t('Step 2: Choose Branches'), + 'install' => t('Step 2: Install Drupal'), + ), + 'forms' => array( + 'git_url' => array( + 'form id' => 'devshop_project_create_step_1_git' + ), + 'environments' => array( + 'form id' => 'devshop_project_create_step_2_environments' + ), + 'install' => array( + 'form id' => 'devshop_project_create_step_3_install' + ), + ), + ); + + // *** SETTING THE FORM UP FOR MULTISTEP *** // + $form_state = array( + 'cache name' => NULL, + ); + $project = devshop_project_create_get_cache(NULL); + if ($step == NULL){ + $step = 'git_url'; + } + if (empty($project)){ + // set form to first step -- we have no data + $step = current(array_keys($form_info['order'])); + $project = new stdClass(); + + // ** set the storage object so its ready for whatever comes next + ctools_object_cache_set('project', $form_state['cache name'], $project); + } + + //THIS IS WHERE WILL STORE ALL FORM DATA + $form_state['project'] = $project; + + // and this is the witchcraft that makes it work + $output = ctools_wizard_multistep_form($form_info, $step, $form_state); + return $output; +} + + +/** + * STEP 1: Form + * + * Just get the Git URL. If we can't access the code, all else should fail. + */ +function devshop_project_create_step_1_git(&$form, &$form_state) { + $project = &$form_state['project']; + $form['git_url'] = array( + '#type' => 'textfield', + '#required' => 1, + '#title' => t('Git URL'), + '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), + '#default_value' => $project->git_url, + ); +} + +/** + * STEP 1: Validator. + * + * This is where we try and connect to the remote branches. + * + * As long as the SSH for www-data is setup right, this will work for private repos. + * @TODO: Figure out any security implications of giving www-data an ssh private key and telling users that + * (if they want web-based validation) they have to add www-data's SSH key to the repo as well. + * + * This is also where we get all the tags and branches. + */ +function devshop_project_create_step_1_git_validate(&$from, &$form_state) { + $project = &$form_state['project']; + $project->git_url = $form_state['values']['git_url']; + + $command = "git ls-remote {$project->git_url}"; + $remote_list = explode("\n", shell_exec($command)); + + // If remote list is empty, we have either a typo or a connection error. + if (count($remote_list) == 1 && empty($remote_list[0])){ + form_set_error('git_url', t('This server was not able to connect to that Git URL. Please check the URL, and make sure www-data and aegir user has SSH keys setup to connect to your repos.')); + return; + } + + // Build tag and branch list + $project->branches = array(); + $project->tags= array(); + + foreach ($remote_list AS $line_string){ + // @TODO: Would love some regex love here + // Example remote line: + // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 + $line = explode(" ", $line_string); + $ref = explode("/", $line[1]); + $branch = array_pop($ref); + + if ($ref[1] == 'heads') { + $project->branches[] = $branch; + } else if ($ref[1] == 'tags') { + $project->tags[] = $branch; + } + } +} + +/** + * KEY CONCEPT: generally, you will never save data here -- you will simply add values to the + * yet to be saved ctools_cache object. + * + * Saving happens at the end, within the $form_info['finish callback']; + */ +function devshop_project_create_step_1_git_submit(&$from, &$form_state) { + $project = &$form_state['project']; + $project->git_url = $form_state['values']['git_url']; +} + +/** + * STEP 2: Choose environments + */ +function devshop_project_create_step_2_environments(&$form, &$form_state) { + $project = &$form_state['project']; + dsm($project); + $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); + + $branch_options = array(t('Choose a branch...')); + $branch_options += array_combine($project->branches, $project->branches); + + $form['default_platforms'] = array( + '#type' => 'fieldset', + '#title' => t('Default Platforms'), + '#theme' => 'devshop_project_create_platforms_table', + '#tree' => TRUE, + ); + + foreach ($default_platforms as $env) { + $form['default_platforms'][$env] = array( + '#tree' => TRUE, + ); + $form['default_platforms'][$env]['enabled'] = array( + '#type' => 'checkbox', + '#title' => $env, + '#default_value' => isset($project->default_platforms[$env]), + ); + $form['default_platforms'][$env]['branch'] = array( + '#type' => 'select', + '#default_value' => $project->default_platforms[$env], + '#options' => $branch_options + ); + } + + $form['branch_platforms'] = array( + '#type' => 'checkboxes', + '#multiple' => 'true', + '#title' => t('Branch Platforms'), + '#description' => t('Check the branches you wish to create platforms for.'), + '#default_value' => $project->branch_platforms, + '#options' => $branch_options + ); + $form_state['no buttons'] = TRUE; +} + + +/** + * STEP 2: SUBMIT + */ +function devshop_project_create_step_2_environments_validate(&$form, &$form_state) { + $project = &$form_state['project']; + $values = $form_state['values']; + + // At least one platform is required + $none_chosen = TRUE; + foreach ($values['default_platforms'] as $env => $data) { + if ($data['enabled']){ + $none_chosen = FALSE; + if ($data['branch']){ + // Save the value + form_set_value($form['default_platforms'][$env], $data['branch'], $form_state); + } else { + // If you chose a default platform, you must choose a branch + form_set_error("default_platforms][$env][branch", t('You must choose a branch to create a platform.')); + } + } + } + $branch_platforms = array_filter($values['branch_platforms']); + if (!empty($branch_platforms)){ + $none_chosen = FALSE; + } + + if ($none_chosen){ + form_set_error('form', t('You must choose to build at least one platform.')); + } + + // Filter out branch platforms forms_api so we can keep the submit clean. + form_set_value($form['branch_platforms'], array_filter($values['branch_platforms']), $form_state); + +} +/** + * STEP 2: SUBMIT + */ +function devshop_project_create_step_2_environments_submit(&$form, &$form_state) { + // Get project and reset properties.. + $project = &$form_state['project']; + $values = $form_state['values']; + + // Set Values + $project->default_platforms = $values['default_platforms']; + $project->branch_platforms = $values['branch_platforms']; +} + + + +/** + * NEXT callback + * Saves anything in $form_state['project'] to ctools cache. + * + * The form submit callback is responsible for putting data into $form_state['project']. + */ +function devshop_projects_create_wizard_next(&$form_state) { + $project = &$form_state['project']; + $cache = ctools_object_cache_set('project', $form_state['cache name'], $project); +} + + +/*----PART 3 CTOOLS CALLBACKS -- these usually don't have to be very unique ---------------------- */ + +/** + * Callback generated when the add page process is finished. + * this is where you'd normally save. + */ +function wombat_basic_add_subtask_finish(&$form_state) { + $wombat = &$form_state['wombat_obj']; + drupal_set_message('Wombat '.$wombat->name.' successfully deployed. Wombat will be assigned the following duties: '.implode(",", $wombat->duties)).'.'; + // Clear the cache + ctools_object_cache_clear('wombat_basic', $form_state['cache name']); + $form_state['redirect'] = 'wombat'; +} + + +/** + * Callback generated when the 'cancel' button is clicked. + * + * All we do here is clear the cache. + */ +function wombat_basic_add_subtask_cancel(&$form_state) { + // Update the cache with changes. + ctools_object_cache_clear('wombat_basic', $form_state['cache name']); + $form_state['redirect'] = 'wombat'; + drupal_set_message('Coward.'); +} + +/*----PART 4 CTOOLS FORM STORAGE HANDLERS -- these usually don't have to be very unique ---------------------- */ + +/** + * Remove an item from the object cache. + */ +function devshop_project_create_clear_cache($name) { + ctools_object_cache_clear('project', $name); +} + +/** + * Get the cached changes to a given task handler. + */ +function devshop_project_create_get_cache($name) { + $cache = ctools_object_cache_get('project', $name); + return $cache; +} + + + + +function d($in) { + print '
';
+  print_r($in);
+  print '
'; +} \ No newline at end of file From 920d629924e6f576061a31687161a389105f9765 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 16:39:42 -0500 Subject: [PATCH 0143/3476] taking over node/add/project --- devshop_projects/devshop_projects.module | 6 ++++++ 1 file changed, 6 insertions(+) mode change 100644 => 100755 devshop_projects/devshop_projects.module diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module old mode 100644 new mode 100755 index 0e73cb055..237520966 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -59,6 +59,12 @@ function devshop_projects_menu() { function devshop_projects_menu_alter(&$items) { $items['hosting/tasks/%node/list']['page callback'] = 'devshop_projects_hosting_task_ajax_list'; $items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; + + + $items['node/add/project']['page callback'] = 'devshop_projects_create_wizard'; + $items['node/add/project']['page arguments'] = array(3); + $items['node/add/project']['file'] = 'devshop_projects.wizard.inc'; + $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects'); } /** From d075708fc7c8a121996d2220c1a217170b22e9a4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 16:40:10 -0500 Subject: [PATCH 0144/3476] adding dependency to ctools --- devshop_projects/devshop_projects.info | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 devshop_projects/devshop_projects.info diff --git a/devshop_projects/devshop_projects.info b/devshop_projects/devshop_projects.info old mode 100644 new mode 100755 index f0b330219..005571436 --- a/devshop_projects/devshop_projects.info +++ b/devshop_projects/devshop_projects.info @@ -3,3 +3,5 @@ description = A DevShop module to sites/platforms together into projects. core = 6.x package = DevShop +dependencies[] = hosting +dependencies[] = ctools From 02a9147ab880e8ed2c0ce8f347b02c39cf223777 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 18:20:52 -0500 Subject: [PATCH 0145/3476] removed dsm --- devshop_projects/devshop_projects.wizard.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 33731f408..9cec4f0ce 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -137,7 +137,6 @@ function devshop_project_create_step_1_git_submit(&$from, &$form_state) { */ function devshop_project_create_step_2_environments(&$form, &$form_state) { $project = &$form_state['project']; - dsm($project); $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); $branch_options = array(t('Choose a branch...')); From ff70f12fc79c1391d0264062ce0a0ed78aea4232 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 19:06:52 -0500 Subject: [PATCH 0146/3476] project settings step --- devshop_projects/devshop_projects.wizard.inc | 158 ++++++++++++++----- 1 file changed, 115 insertions(+), 43 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 9cec4f0ce..8b5986430 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -1,5 +1,8 @@ FALSE, 'next text' => 'Next', 'next callback' => 'devshop_projects_create_wizard_next', - 'finish callback' => 'wombat_basic_add_subtask_finish', - 'return callback' => 'wombat_basic_add_subtask_finish', - 'cancel callback' => 'wombat_basic_add_subtask_cancel', + 'finish callback' => 'devshop_projects_create_wizard_finish', + 'return callback' => 'devshop_projects_create_wizard_return', + 'cancel callback' => 'devshop_projects_create_wizard_cancel', 'order' => array( - 'git_url' => t('Step 1: Get Code'), - 'environments' => t('Step 2: Choose Branches'), - 'install' => t('Step 2: Install Drupal'), + 'git_url' => t('Step 1: Git URL'), + 'settings' => t('Step 2: Project Settings'), + 'environments' => t('Step 3: Configure Platforms'), + 'install' => t('Step 4: Install Drupal'), ), 'forms' => array( 'git_url' => array( - 'form id' => 'devshop_project_create_step_1_git' + 'form id' => 'devshop_project_create_step_git' + ), + 'settings' => array( + 'form id' => 'devshop_project_create_step_settings' ), 'environments' => array( - 'form id' => 'devshop_project_create_step_2_environments' + 'form id' => 'devshop_project_create_step_environments' ), 'install' => array( - 'form id' => 'devshop_project_create_step_3_install' + 'form id' => 'devshop_project_create_step_install' ), ), ); - // *** SETTING THE FORM UP FOR MULTISTEP *** // $form_state = array( 'cache name' => NULL, ); @@ -60,13 +66,18 @@ function devshop_projects_create_wizard($step = NULL){ return $output; } +/********** + * STEP 1 + * Git URL + **********/ + /** * STEP 1: Form - * + * * Just get the Git URL. If we can't access the code, all else should fail. */ -function devshop_project_create_step_1_git(&$form, &$form_state) { +function devshop_project_create_step_git(&$form, &$form_state) { $project = &$form_state['project']; $form['git_url'] = array( '#type' => 'textfield', @@ -78,8 +89,8 @@ function devshop_project_create_step_1_git(&$form, &$form_state) { } /** - * STEP 1: Validator. - * + * STEP 1: Validate + * * This is where we try and connect to the remote branches. * * As long as the SSH for www-data is setup right, this will work for private repos. @@ -88,7 +99,7 @@ function devshop_project_create_step_1_git(&$form, &$form_state) { * * This is also where we get all the tags and branches. */ -function devshop_project_create_step_1_git_validate(&$from, &$form_state) { +function devshop_project_create_step_git_validate(&$from, &$form_state) { $project = &$form_state['project']; $project->git_url = $form_state['values']['git_url']; @@ -122,20 +133,93 @@ function devshop_project_create_step_1_git_validate(&$from, &$form_state) { } /** - * KEY CONCEPT: generally, you will never save data here -- you will simply add values to the - * yet to be saved ctools_cache object. - * - * Saving happens at the end, within the $form_info['finish callback']; + * STEP 1: Submit */ -function devshop_project_create_step_1_git_submit(&$from, &$form_state) { +function devshop_project_create_step_git_submit(&$from, &$form_state) { $project = &$form_state['project']; $project->git_url = $form_state['values']['git_url']; + + // Extract the project name from the git url + // @TODO: Regex? + $project_name = array_pop(explode("/", $project->git_url)); + $project_name = str_replace('.git', '', $project_name); + + $project->title = $project_name; + $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); + $project->base_url = strtr(variable_get('devshop_project_default_base_url', "@name.localhost"), array('@name' => $project_name)); +} + +/********** + * STEP 2 + * Project Settings + *********/ + +/** + * STEP 2: Form + */ +function devshop_project_create_step_settings(&$form, &$form_state) { + $project = &$form_state['project']; + + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project.'), + '#size' => 40, + '#default_value' => $project->title, + '#maxlength' => 255, + ); + $form['code_path'] = array( + '#type' => 'textfield', + '#title' => t('Code path'), + '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $project->code_path, + '#maxlength' => 255, + '#weight' => 2, + ); + $form['base_url'] = array( + '#type' => 'textfield', + '#title' => t('Primary Domain'), + '#description' => t('The domain name all sites will be built under.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $project->base_url, + '#maxlength' => 255, + '#weight' => 2, + ); + } /** - * STEP 2: Choose environments + * STEP 2: Validate */ -function devshop_project_create_step_2_environments(&$form, &$form_state) { +function devshop_project_create_step_settings_validate(&$from, &$form_state) { + $project = &$form_state['project']; +} + +/** + * STEP 2: Submit + */ +function devshop_project_create_step_settings_submit(&$from, &$form_state) { + $project = &$form_state['project']; + + $project->title = $form_state['values']['title']; + $project->code_path = $form_state['values']['code_path']; + $project->base_url = $form_state['values']['base_url']; +} + + +/********** + * STEP 3 + * Project Environments + *********/ + +/** + * STEP 3: Form + */ +function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); @@ -180,7 +264,7 @@ function devshop_project_create_step_2_environments(&$form, &$form_state) { /** * STEP 2: SUBMIT */ -function devshop_project_create_step_2_environments_validate(&$form, &$form_state) { +function devshop_project_create_step_environments_validate(&$form, &$form_state) { $project = &$form_state['project']; $values = $form_state['values']; @@ -214,7 +298,7 @@ function devshop_project_create_step_2_environments_validate(&$form, &$form_stat /** * STEP 2: SUBMIT */ -function devshop_project_create_step_2_environments_submit(&$form, &$form_state) { +function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Get project and reset properties.. $project = &$form_state['project']; $values = $form_state['values']; @@ -244,12 +328,10 @@ function devshop_projects_create_wizard_next(&$form_state) { * Callback generated when the add page process is finished. * this is where you'd normally save. */ -function wombat_basic_add_subtask_finish(&$form_state) { - $wombat = &$form_state['wombat_obj']; - drupal_set_message('Wombat '.$wombat->name.' successfully deployed. Wombat will be assigned the following duties: '.implode(",", $wombat->duties)).'.'; - // Clear the cache - ctools_object_cache_clear('wombat_basic', $form_state['cache name']); - $form_state['redirect'] = 'wombat'; +function devshop_projects_create_wizard_finish(&$form_state) { + $project = &$form_state['project']; + ctools_object_cache_clear('project', $form_state['cache name']); + $form_state['redirect'] = 'hosting/projects'; } @@ -258,11 +340,10 @@ function wombat_basic_add_subtask_finish(&$form_state) { * * All we do here is clear the cache. */ -function wombat_basic_add_subtask_cancel(&$form_state) { +function devshop_projects_create_wizard_cancel(&$form_state) { // Update the cache with changes. - ctools_object_cache_clear('wombat_basic', $form_state['cache name']); - $form_state['redirect'] = 'wombat'; - drupal_set_message('Coward.'); + ctools_object_cache_clear('project', $form_state['cache name']); + $form_state['redirect'] = 'hosting/projects'; } /*----PART 4 CTOOLS FORM STORAGE HANDLERS -- these usually don't have to be very unique ---------------------- */ @@ -280,13 +361,4 @@ function devshop_project_create_clear_cache($name) { function devshop_project_create_get_cache($name) { $cache = ctools_object_cache_get('project', $name); return $cache; -} - - - - -function d($in) { - print '
';
-  print_r($in);
-  print '
'; } \ No newline at end of file From 72f3e44da1a2446a0916300300e0a733bf6e1858 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 19:08:43 -0500 Subject: [PATCH 0147/3476] fixing a warning --- devshop_projects/devshop_projects.wizard.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 8b5986430..ef0df3328 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -141,7 +141,8 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { // Extract the project name from the git url // @TODO: Regex? - $project_name = array_pop(explode("/", $project->git_url)); + $u = explode("/", $project->git_url); + $project_name = array_pop($u); $project_name = str_replace('.git', '', $project_name); $project->title = $project_name; From 9bd4920c42bb654a648a6ee8c5413a83ace8c598 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 20:14:46 -0500 Subject: [PATCH 0148/3476] Creating the project node after step 2 --- devshop_projects/devshop_projects.node.inc | 18 ++--- devshop_projects/devshop_projects.task.inc | 5 ++ devshop_projects/devshop_projects.wizard.inc | 76 ++++++++++++-------- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 16f8fb817..df6d2ce6c 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -105,7 +105,7 @@ function devshop_projects_insert($node) { } // Create hostmaster task - hosting_add_task($node->nid, 'devshop-create'); + hosting_add_task($node->nid, 'verify'); } /** @@ -118,14 +118,14 @@ function devshop_projects_update($node) { "WHERE nid = %d", $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, $node->nid); - - // If this is a rerry, kick start another devshop-create - if ($node->retry) { - $args = array('retry' => 1); - - // Create hostmaster task - hosting_add_task($node->nid, 'devshop-create', $args); - } + // + //// If this is a rerry, kick start another devshop-create + //if (isset($node->retry)) { + // $args = array('retry' => 1); + // + // // Create hostmaster task + // hosting_add_task($node->nid, 'devshop-create', $args); + //} } /** diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 2c617e977..c6b128235 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -9,6 +9,11 @@ */ function devshop_projects_hosting_tasks() { $tasks = array(); + $tasks['project']['verify'] = array( + 'title' => t('Verify Project'), + 'description' => t('Verifies access to the git repository, downloads branch and tag information.'), + 'provision_save' => TRUE, + ); $tasks['project']['devshop-create'] = array( 'title' => t('Create Project'), 'description' => t('Clones the repo, and creates the platforms.'), diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index ef0df3328..3ad2fbc26 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -103,33 +103,6 @@ function devshop_project_create_step_git_validate(&$from, &$form_state) { $project = &$form_state['project']; $project->git_url = $form_state['values']['git_url']; - $command = "git ls-remote {$project->git_url}"; - $remote_list = explode("\n", shell_exec($command)); - - // If remote list is empty, we have either a typo or a connection error. - if (count($remote_list) == 1 && empty($remote_list[0])){ - form_set_error('git_url', t('This server was not able to connect to that Git URL. Please check the URL, and make sure www-data and aegir user has SSH keys setup to connect to your repos.')); - return; - } - - // Build tag and branch list - $project->branches = array(); - $project->tags= array(); - - foreach ($remote_list AS $line_string){ - // @TODO: Would love some regex love here - // Example remote line: - // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 - $line = explode(" ", $line_string); - $ref = explode("/", $line[1]); - $branch = array_pop($ref); - - if ($ref[1] == 'heads') { - $project->branches[] = $branch; - } else if ($ref[1] == 'tags') { - $project->tags[] = $branch; - } - } } /** @@ -198,17 +171,64 @@ function devshop_project_create_step_settings(&$form, &$form_state) { */ function devshop_project_create_step_settings_validate(&$from, &$form_state) { $project = &$form_state['project']; + + $command = "git ls-remote {$project->git_url}"; + $remote_list = explode("\n", shell_exec($command)); + + // If remote list is empty, we have either a typo or a connection error. + if (count($remote_list) == 1 && empty($remote_list[0])){ + form_set_error('git_url', t('This server was not able to connect to that Git URL. Please check the URL, and make sure www-data and aegir user has SSH keys setup to connect to your repos.')); + return; + } + + // Build tag and branch list + $project->branches = array(); + $project->tags= array(); + + foreach ($remote_list AS $line_string){ + // @TODO: Would love some regex love here + // Example remote line: + // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 + $line = explode(" ", $line_string); + $ref = explode("/", $line[1]); + + $branch = array_pop($ref); + + if ($ref[1] == 'heads') { + $project->branches[] = $branch; + } else if ($ref[1] == 'tags') { + $project->tags[] = $branch; + } + } } /** * STEP 2: Submit */ function devshop_project_create_step_settings_submit(&$from, &$form_state) { + global $user; $project = &$form_state['project']; $project->title = $form_state['values']['title']; $project->code_path = $form_state['values']['code_path']; $project->base_url = $form_state['values']['base_url']; + + $node = $project; + + // If the node hasn't been saved yet... + if (empty($project->nid)){ + $node = $project; + $node->type = 'project'; + $node->status = 1; + $node->uid = $user->uid; + if ($node = node_submit($node)) { + node_save($node); + } + + if ($node->nid){ + $project->nid; + } + } } @@ -223,7 +243,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); - +dsm($project); $branch_options = array(t('Choose a branch...')); $branch_options += array_combine($project->branches, $project->branches); From f4f85589e4b93c4ebfea491670a3cda72b2c0ec7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 20:22:33 -0500 Subject: [PATCH 0149/3476] removing menu alter hacks --- devshop_projects/devshop_projects.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 237520966..51ae8d8d6 100755 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -57,8 +57,8 @@ function devshop_projects_menu() { * Implementation of hook_menu_alter() */ function devshop_projects_menu_alter(&$items) { - $items['hosting/tasks/%node/list']['page callback'] = 'devshop_projects_hosting_task_ajax_list'; - $items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; + //$items['hosting/tasks/%node/list']['page callback'] = 'devshop_projects_hosting_task_ajax_list'; + //$items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; $items['node/add/project']['page callback'] = 'devshop_projects_create_wizard'; From 065c398f2a207c630622772a12ac5116e275a639 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 20:23:39 -0500 Subject: [PATCH 0150/3476] removing hosting tasks table hack --- devshop_projects/devshop_projects.node.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index df6d2ce6c..bc818a337 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -300,7 +300,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { //Tasks $node->content['tasks_view'] = array( '#type' => 'item', - '#value' => devshop_projects_hosting_task_table($node), + //'#value' => devshop_projects_hosting_task_table($node), + '#value' => hosting_task_table($node), '#prefix' => '
', '#suffix' => '
', '#weight' => 10 From 1468664328495b3319e15ce70dfb68b5eea0fc25 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 20:44:45 -0500 Subject: [PATCH 0151/3476] getting rid of branch listing directly in wizard --- devshop_projects/devshop_projects.wizard.inc | 32 ++------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 3ad2fbc26..e6729dcef 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -63,6 +63,7 @@ function devshop_projects_create_wizard($step = NULL){ // and this is the witchcraft that makes it work $output = ctools_wizard_multistep_form($form_info, $step, $form_state); + return $output; } @@ -171,35 +172,6 @@ function devshop_project_create_step_settings(&$form, &$form_state) { */ function devshop_project_create_step_settings_validate(&$from, &$form_state) { $project = &$form_state['project']; - - $command = "git ls-remote {$project->git_url}"; - $remote_list = explode("\n", shell_exec($command)); - - // If remote list is empty, we have either a typo or a connection error. - if (count($remote_list) == 1 && empty($remote_list[0])){ - form_set_error('git_url', t('This server was not able to connect to that Git URL. Please check the URL, and make sure www-data and aegir user has SSH keys setup to connect to your repos.')); - return; - } - - // Build tag and branch list - $project->branches = array(); - $project->tags= array(); - - foreach ($remote_list AS $line_string){ - // @TODO: Would love some regex love here - // Example remote line: - // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 - $line = explode(" ", $line_string); - $ref = explode("/", $line[1]); - - $branch = array_pop($ref); - - if ($ref[1] == 'heads') { - $project->branches[] = $branch; - } else if ($ref[1] == 'tags') { - $project->tags[] = $branch; - } - } } /** @@ -243,7 +215,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); -dsm($project); + $branch_options = array(t('Choose a branch...')); $branch_options += array_combine($project->branches, $project->branches); From a6776ee84594bee94547724903ae2ec06f523491 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 21:12:20 -0500 Subject: [PATCH 0152/3476] saving branches to the hosting context! --- devshop_projects/devshop_projects.task.inc | 54 +++++++++++++++++++- devshop_projects/devshop_projects.wizard.inc | 5 ++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index c6b128235..1bc9e3839 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -42,13 +42,65 @@ function devshop_projects_hosting_tasks() { * For project entities. */ function devshop_projects_hosting_project_context_options(&$task) { + + drush_log('[DEVSHOP] devshop_projects_hosting_project_context_options()', 'ok'); $task->context_options['server'] = '@server_master'; $task->context_options['project_name'] = $task->ref->title; $task->context_options['install_profile'] = $task->ref->install_profile; $task->context_options['base_url'] = $task->ref->base_url; $task->context_options['code_path'] = trim($task->ref->code_path, " "); $task->context_options['git_url'] = $task->ref->git_url; - $task->context_options['retry'] = $task->ref->retry; + + $branches = getBranchesAndTags($task->ref->git_url); + + $task->context_options['git_branches'] = $branches['branches']; + $task->context_options['git_tags'] = $branches['tags']; +} + +function getBranchesAndTags($git_url = NULL){ + if (is_null($git_url)){ + $git_url = drush_get_option('git_url'); + } + $command = "git ls-remote {$git_url}"; + drush_shell_exec($command); + $exec_output = drush_shell_exec_output(); + + drush_log('[DEVSHOP] running '.$command, 'ok'); + drush_log('[DEVSHOP] ' . implode("\n", $exec_output), 'ok'); + + // Check for Permission Denied + // @TODO: Provide link to the Public key for the server. + if ('Permission denied' == substr($exec_output[0], 0, 17)){ + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error:'). implode("\n", $exec_output), 'error'); + } + + // If remote list is empty, something else went wrong. + if (count($exec_output) == 1 && empty($exec_output[0])){ + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('Something went wrong. Check the git URL and try again.'), 'error'); + return; + } + + // Build tag and branch list + $branches = array(); + $tags = array(); + + foreach ($exec_output AS $line_string){ + // @TODO: Would love some regex love here + // Example remote line: + // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 + $line = explode(" ", $line_string); + $ref = explode("/", $line[1]); + + $branch = array_pop($ref); + + if ($ref[1] == 'heads') { + $branches[] = $branch; + } else if ($ref[1] == 'tags') { + $tags[] = $branch; + } + } + drush_log(dt('[DEVSHOP] Found !b branches and !t tags.', array('!b' => count($branches), '!t' => count($tags), )), 'ok'); + return array('branches' => $branches, 'tags' => $tags); } /** diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index e6729dcef..ed1d94059 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -216,6 +216,11 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); + if (empty($project->branches)){ + drupal_set_message('Please wait while we connect to your repo.'); + return; + } + $branch_options = array(t('Choose a branch...')); $branch_options += array_combine($project->branches, $project->branches); From 200986058abff55539565eefe9d8f640002c759e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 21:24:50 -0500 Subject: [PATCH 0153/3476] moving code around --- devshop_projects/devshop_projects.wizard.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index ed1d94059..cc31020be 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -186,7 +186,6 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { $project->base_url = $form_state['values']['base_url']; $node = $project; - // If the node hasn't been saved yet... if (empty($project->nid)){ $node = $project; @@ -215,9 +214,10 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); - - if (empty($project->branches)){ - drupal_set_message('Please wait while we connect to your repo.'); + + $project_node = node_load($project->nid); + if (empty($project_node->git_branches)){ + drupal_set_message('Please wait while we connect to your repo and find out what branches you have.'); return; } From b7f3b17f140b25b761a56ad786968f3432e53b2b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 21:30:12 -0500 Subject: [PATCH 0154/3476] adding required data column --- devshop_projects/devshop_projects.install | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index f1cd7f990..aa87580ec 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -37,6 +37,18 @@ function devshop_projects_schema() { 'size' => 'big', 'not null' => FALSE, ), + 'git_branches' => array( + 'type' => 'longtext', + 'not null' => TRUE, + 'default' => '', + 'description' => 'Serialized data.', + ), + 'git_tags' => array( + 'type' => 'longtext', + 'not null' => TRUE, + 'default' => '', + 'description' => 'Serialized data.', + ), ), 'primary key' => array('nid'), ); @@ -70,6 +82,12 @@ function devshop_projects_schema() { 'default' => '', 'description' => 'Environment type: dev, test, live, or sandbox.', ), + 'data' => array( + 'type' => 'longtext', + 'not null' => TRUE, + 'default' => '', + 'description' => 'Serialized data.', + ), ), 'primary key' => array('object_nid'), ); From 6c9e114fdfd62fcb9d953b239ae0298a06d4bca7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 21:30:36 -0500 Subject: [PATCH 0155/3476] calling node save when we get the branches and tags --- devshop_projects/devshop_projects.task.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 1bc9e3839..f28ecbb93 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -53,6 +53,10 @@ function devshop_projects_hosting_project_context_options(&$task) { $branches = getBranchesAndTags($task->ref->git_url); + $task->ref->git_branches = $branches['branches']; + $task->ref->git_tags = $branches['tags']; + node_save($task->ref); + $task->context_options['git_branches'] = $branches['branches']; $task->context_options['git_tags'] = $branches['tags']; } From 86f0120f9c769a0a06ba0860d8e63e42f0cfd288 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 21:45:59 -0500 Subject: [PATCH 0156/3476] proper schema... and... it saves! --- devshop_projects/devshop_projects.install | 22 +++++----------------- devshop_projects/devshop_projects.node.inc | 13 ++++++++++--- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index aa87580ec..3a325a35f 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -37,17 +37,11 @@ function devshop_projects_schema() { 'size' => 'big', 'not null' => FALSE, ), - 'git_branches' => array( - 'type' => 'longtext', - 'not null' => TRUE, - 'default' => '', - 'description' => 'Serialized data.', - ), - 'git_tags' => array( - 'type' => 'longtext', - 'not null' => TRUE, - 'default' => '', - 'description' => 'Serialized data.', + 'data' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + 'description' => 'A serialized array of name value pairs for this project.', ), ), 'primary key' => array('nid'), @@ -82,12 +76,6 @@ function devshop_projects_schema() { 'default' => '', 'description' => 'Environment type: dev, test, live, or sandbox.', ), - 'data' => array( - 'type' => 'longtext', - 'not null' => TRUE, - 'default' => '', - 'description' => 'Serialized data.', - ), ), 'primary key' => array('object_nid'), ); diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index bc818a337..94703112f 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -113,11 +113,15 @@ function devshop_projects_insert($node) { */ function devshop_projects_update($node) { + $data = array(); + $data['git_branches'] = $node->git_branches; + $data['git_tags'] = $node->git_tags; + db_query("UPDATE {hosting_devshop_project} " . - "SET git_url = '%s', code_path = '%s', base_url = '%s' " . + "SET git_url = '%s', code_path = '%s', base_url = '%s', data='%s' " . "WHERE nid = %d", $node->git_url, hosting_path_normalize($node->code_path), - $node->base_url, $node->nid); + $node->base_url, serialize($data), $node->nid); // //// If this is a rerry, kick start another devshop-create //if (isset($node->retry)) { @@ -146,7 +150,7 @@ function devshop_projects_delete($node) { * Node object */ function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile ' . + $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile, data ' . 'FROM {hosting_devshop_project} ' . 'WHERE nid = %d', $node->nid)); $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); @@ -154,6 +158,9 @@ function devshop_projects_load($node) { $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; $additions += $hosting_name; } + $data = unserialize($additions['data']); + $additions['git_branches'] = $data['git_branches']; + $additions['git_tags'] = $data['git_tags']; $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); From 95f898ed7748c0f61a7f40ed1d3f5d050c470596 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 2 Dec 2012 21:47:58 -0500 Subject: [PATCH 0157/3476] getting branches from node! --- devshop_projects/devshop_projects.wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index cc31020be..c4369d070 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -222,7 +222,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { } $branch_options = array(t('Choose a branch...')); - $branch_options += array_combine($project->branches, $project->branches); + $branch_options += array_combine($project_node->git_branches, $project_node->git_branches); $form['default_platforms'] = array( '#type' => 'fieldset', From 6b1fe5da7c9d097c3e8165c112df447766bebf7c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 14:02:16 -0500 Subject: [PATCH 0158/3476] step names, default git url --- devshop_projects/devshop_projects.wizard.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index c4369d070..509ec08b4 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -21,9 +21,9 @@ function devshop_projects_create_wizard($step = NULL){ 'return callback' => 'devshop_projects_create_wizard_return', 'cancel callback' => 'devshop_projects_create_wizard_cancel', 'order' => array( - 'git_url' => t('Step 1: Git URL'), - 'settings' => t('Step 2: Project Settings'), - 'environments' => t('Step 3: Configure Platforms'), + 'git_url' => t('Step 1: Git'), + 'settings' => t('Step 2: Settings'), + 'environments' => t('Step 3: Platforms'), 'install' => t('Step 4: Install Drupal'), ), 'forms' => array( @@ -53,6 +53,7 @@ function devshop_projects_create_wizard($step = NULL){ // set form to first step -- we have no data $step = current(array_keys($form_info['order'])); $project = new stdClass(); + $project->git_url = ''; // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); From 6d90b19ff1ab6d8c4b640819b92506ae9ec63194 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 14:19:19 -0500 Subject: [PATCH 0159/3476] Code cleanup --- devshop_projects/devshop_projects.ajax.inc | 65 +++++++ devshop_projects/devshop_projects.wizard.inc | 184 +++++++++++-------- 2 files changed, 168 insertions(+), 81 deletions(-) create mode 100644 devshop_projects/devshop_projects.ajax.inc diff --git a/devshop_projects/devshop_projects.ajax.inc b/devshop_projects/devshop_projects.ajax.inc new file mode 100644 index 000000000..9b69370da --- /dev/null +++ b/devshop_projects/devshop_projects.ajax.inc @@ -0,0 +1,65 @@ + NULL, + 'submitted' => FALSE, + ); + $form_build_id = $_POST['form_build_id']; + + // Get the form from the cache. + $form = form_get_cache($form_build_id, $form_state); + $args = $form['#parameters']; + $form_id = array_shift($args); + + // We will run some of the submit handlers so we need to disable redirecting. + $form['#redirect'] = FALSE; + + // We need to process the form, prepare for that by setting a few internals + // variables. + $form['#post'] = $_POST; + $form['#programmed'] = FALSE; + $form_state['post'] = $_POST; + + // Build, validate and if possible, submit the form. + // drupal_process_form($form_id, $form, $form_state); + + // This call recreates the form relying solely on the form_state that the + // drupal_process_form set up. + $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id); + + //// Render the new output. + //$choice_form = $form['choice_wrapper']['choice']; + //unset($choice_form['#prefix'], $choice_form['#suffix']); // Prevent duplicate wrappers. + //$output = theme('status_messages') . drupal_render($choice_form); + + + // Get refs + $git_url = $form_state['post']['git_url']; + $command = "git-ls-remote $git_url"; + $exec_output = shell_exec($command); + + $lines = explode("\n", $exec_output); + $output .= print_r($lines, 1); + $branches = array(); + foreach ($lines as $line){ + list($commit, $ref_string) = explode(" ", $line); + list($i, $type, $name) = explode('/', $ref_string); + + if ($type == 'heads'){ + $branches[$name] = $name; + } + } + + $form['branch']['#options'] = $branches; + + //$output .= $command.$exec_output; + $output .= theme('status_messages'); + $output .= drupal_render($form['branch']); +// $output .= print_r($form_state, 1); + + drupal_json(array('status' => TRUE, 'data' => $output)); +} \ No newline at end of file diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 509ec08b4..12f3d3aef 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -1,14 +1,60 @@ NULL, + ); + + // Setup project + $project = ctools_object_cache_get('project', NULL); + + // Setup Step. + if ($step == NULL){ + + // We are on the first step. If there is a last_step in the session other + // than here, redirect to it. + if (isset($project->last_step) && $project->last_step != 'git_url'){ + drupal_goto('node/add/project/' . $project->last_step); + return; + } else { + $step = 'git_url'; + } + } + + + if (empty($project)){ + // set form to first step -- we have no data + $step = current(array_keys($form_info['order'])); + $project = new stdClass(); + $project->git_url = ''; + + // ** set the storage object so its ready for whatever comes next + ctools_object_cache_set('project', $form_state['cache name'], $project); + } + + //THIS IS WHERE WILL STORE ALL FORM DATA + $form_state['project'] = $project; + + // and this is the witchcraft that makes it work + $output = ctools_wizard_multistep_form($form_info, $step, $form_state); + + return $output; +} + +/** + * The form_info for the ctools wizard + */ +function devshop_projects_create_wizard_info(){ + return array( 'id' => 'devshop_project_create', 'path' => "node/add/project/%step", 'show trail' => TRUE, @@ -18,7 +64,6 @@ function devshop_projects_create_wizard($step = NULL){ 'next text' => 'Next', 'next callback' => 'devshop_projects_create_wizard_next', 'finish callback' => 'devshop_projects_create_wizard_finish', - 'return callback' => 'devshop_projects_create_wizard_return', 'cancel callback' => 'devshop_projects_create_wizard_cancel', 'order' => array( 'git_url' => t('Step 1: Git'), @@ -41,33 +86,53 @@ function devshop_projects_create_wizard($step = NULL){ ), ), ); - - $form_state = array( - 'cache name' => NULL, - ); - $project = devshop_project_create_get_cache(NULL); - if ($step == NULL){ - $step = 'git_url'; - } - if (empty($project)){ - // set form to first step -- we have no data - $step = current(array_keys($form_info['order'])); - $project = new stdClass(); - $project->git_url = ''; +} - // ** set the storage object so its ready for whatever comes next - ctools_object_cache_set('project', $form_state['cache name'], $project); - } - - //THIS IS WHERE WILL STORE ALL FORM DATA - $form_state['project'] = $project; - - // and this is the witchcraft that makes it work - $output = ctools_wizard_multistep_form($form_info, $step, $form_state); - - return $output; +/** + * WIZARD TOOLS + */ + +/** + * NEXT callback + * Saves anything in $form_state['project'] to ctools cache. + * + * The form submit callbacks are responsible for putting data into + * $form_state['project']. + */ +function devshop_projects_create_wizard_next(&$form_state) { + $project = &$form_state['project']; + $cache = ctools_object_cache_set('project', $form_state['cache name'], $project); +} + + +/** + * FINISH callback + * Callback generated when the add page process is finished. + * this is where you'd normally save. + */ +function devshop_projects_create_wizard_finish(&$form_state) { + $project = &$form_state['project']; + ctools_object_cache_clear('project', $form_state['cache name']); + $form_state['redirect'] = 'hosting/projects'; +} + + +/** + * CANCEL callback + * Callback generated when the 'cancel' button is clicked. + * Remove the project data cache and send back to projects page. + */ +function devshop_projects_create_wizard_cancel(&$form_state) { + // Update the cache with changes. + ctools_object_cache_clear('project', $form_state['cache name']); + $form_state['redirect'] = 'hosting/projects'; } +/** + * WIZARD STEPS + */ + + /********** * STEP 1 * Git URL @@ -123,6 +188,10 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project->title = $project_name; $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); $project->base_url = strtr(variable_get('devshop_project_default_base_url', "@name.localhost"), array('@name' => $project_name)); + + + // Save next step (for session use) + $project->next_step = 'settings'; } /********** @@ -201,6 +270,9 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { $project->nid; } } + + // Save next step (for session use) + $project->next_step = 'environments'; } @@ -305,59 +377,9 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Set Values $project->default_platforms = $values['default_platforms']; $project->branch_platforms = $values['branch_platforms']; + + + // Save next step (for session use) + $project->next_step = 'install'; } - - -/** - * NEXT callback - * Saves anything in $form_state['project'] to ctools cache. - * - * The form submit callback is responsible for putting data into $form_state['project']. - */ -function devshop_projects_create_wizard_next(&$form_state) { - $project = &$form_state['project']; - $cache = ctools_object_cache_set('project', $form_state['cache name'], $project); -} - - -/*----PART 3 CTOOLS CALLBACKS -- these usually don't have to be very unique ---------------------- */ - -/** - * Callback generated when the add page process is finished. - * this is where you'd normally save. - */ -function devshop_projects_create_wizard_finish(&$form_state) { - $project = &$form_state['project']; - ctools_object_cache_clear('project', $form_state['cache name']); - $form_state['redirect'] = 'hosting/projects'; -} - - -/** - * Callback generated when the 'cancel' button is clicked. - * - * All we do here is clear the cache. - */ -function devshop_projects_create_wizard_cancel(&$form_state) { - // Update the cache with changes. - ctools_object_cache_clear('project', $form_state['cache name']); - $form_state['redirect'] = 'hosting/projects'; -} - -/*----PART 4 CTOOLS FORM STORAGE HANDLERS -- these usually don't have to be very unique ---------------------- */ - -/** - * Remove an item from the object cache. - */ -function devshop_project_create_clear_cache($name) { - ctools_object_cache_clear('project', $name); -} - -/** - * Get the cached changes to a given task handler. - */ -function devshop_project_create_get_cache($name) { - $cache = ctools_object_cache_get('project', $name); - return $cache; -} \ No newline at end of file From 2d8250addf782e434f98290890826cb7e0018bfd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 14:40:05 -0500 Subject: [PATCH 0160/3476] moving forms and submits around. now creates the project node after step 1 to get branches as early as possible --- devshop_projects/devshop_projects.wizard.inc | 87 +++++++++++--------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 12f3d3aef..12c6460fe 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -22,20 +22,24 @@ function devshop_projects_create_wizard($step = NULL){ // We are on the first step. If there is a last_step in the session other // than here, redirect to it. - if (isset($project->last_step) && $project->last_step != 'git_url'){ - drupal_goto('node/add/project/' . $project->last_step); - return; + $first_step = current(array_keys($form_info['order'])); + if (isset($project->next_step) && $project->next_step != $first_step){ + // @TODO: This fails when you hit the back button, of course... + // drupal_goto('node/add/project/' . $project->next_step); + //return; + $step = $first_step; } else { - $step = 'git_url'; + $step = $first_step; } } - + // Create default project object if (empty($project)){ // set form to first step -- we have no data $step = current(array_keys($form_info['order'])); $project = new stdClass(); $project->git_url = ''; + $project->title = ''; // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); @@ -126,6 +130,10 @@ function devshop_projects_create_wizard_cancel(&$form_state) { // Update the cache with changes. ctools_object_cache_clear('project', $form_state['cache name']); $form_state['redirect'] = 'hosting/projects'; + + // @TODO: If step 1 has been completed, we should run Delete on Project node. + // to get rid of drush alias context, files, everything that this form + // may have created. } /** @@ -153,6 +161,15 @@ function devshop_project_create_step_git(&$form, &$form_state) { '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), '#default_value' => $project->git_url, ); + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository.'), + '#size' => 40, + '#default_value' => $project->title, + '#maxlength' => 255, + ); } /** @@ -170,26 +187,41 @@ function devshop_project_create_step_git_validate(&$from, &$form_state) { $project = &$form_state['project']; $project->git_url = $form_state['values']['git_url']; + // @TODO: Check for duplicate project name here. + } /** * STEP 1: Submit */ function devshop_project_create_step_git_submit(&$from, &$form_state) { + global $user; + $project = &$form_state['project']; $project->git_url = $form_state['values']['git_url']; + $project->title = $project_name = $form_state['values']['title']; + + if (empty($project->nid)){ + // Create the project node now. We will update it with the chosen path. + // This is so we can check for branches and save the hosting_context as soon + // as possible. + $node = $project; + $node->type = 'project'; + $node->status = 1; + $node->uid = $user->uid; + $node->name = $user->name; + if ($node = node_submit($node)) { + node_save($node); + } + if ($node->nid){ + $project->nid; + } + } - // Extract the project name from the git url - // @TODO: Regex? - $u = explode("/", $project->git_url); - $project_name = array_pop($u); - $project_name = str_replace('.git', '', $project_name); - - $project->title = $project_name; + // Saving the project node now, with these defaults based on the name. $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); $project->base_url = strtr(variable_get('devshop_project_default_base_url', "@name.localhost"), array('@name' => $project_name)); - // Save next step (for session use) $project->next_step = 'settings'; } @@ -205,15 +237,6 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { function devshop_project_create_step_settings(&$form, &$form_state) { $project = &$form_state['project']; - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project.'), - '#size' => 40, - '#default_value' => $project->title, - '#maxlength' => 255, - ); $form['code_path'] = array( '#type' => 'textfield', '#title' => t('Code path'), @@ -235,6 +258,7 @@ function devshop_project_create_step_settings(&$form, &$form_state) { '#weight' => 2, ); + // @TODO: Put Pull Code settings here } /** @@ -251,26 +275,11 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { global $user; $project = &$form_state['project']; - $project->title = $form_state['values']['title']; + // For this step, we just save code path and base url for later saving and + // verification. $project->code_path = $form_state['values']['code_path']; $project->base_url = $form_state['values']['base_url']; - $node = $project; - // If the node hasn't been saved yet... - if (empty($project->nid)){ - $node = $project; - $node->type = 'project'; - $node->status = 1; - $node->uid = $user->uid; - if ($node = node_submit($node)) { - node_save($node); - } - - if ($node->nid){ - $project->nid; - } - } - // Save next step (for session use) $project->next_step = 'environments'; } From e5a8107ff2fb86c849de8c224299dda446f6f437 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 14:45:09 -0500 Subject: [PATCH 0161/3476] removing the step stuff --- devshop_projects/devshop_projects.wizard.inc | 23 +++----------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 12c6460fe..20e7ae279 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -19,18 +19,7 @@ function devshop_projects_create_wizard($step = NULL){ // Setup Step. if ($step == NULL){ - - // We are on the first step. If there is a last_step in the session other - // than here, redirect to it. - $first_step = current(array_keys($form_info['order'])); - if (isset($project->next_step) && $project->next_step != $first_step){ - // @TODO: This fails when you hit the back button, of course... - // drupal_goto('node/add/project/' . $project->next_step); - //return; - $step = $first_step; - } else { - $step = $first_step; - } + $step = current(array_keys($form_info['order'])); } // Create default project object @@ -40,6 +29,7 @@ function devshop_projects_create_wizard($step = NULL){ $project = new stdClass(); $project->git_url = ''; $project->title = ''; + $project->default_platforms = array(); // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); @@ -222,8 +212,6 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); $project->base_url = strtr(variable_get('devshop_project_default_base_url', "@name.localhost"), array('@name' => $project_name)); - // Save next step (for session use) - $project->next_step = 'settings'; } /********** @@ -280,8 +268,6 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { $project->code_path = $form_state['values']['code_path']; $project->base_url = $form_state['values']['base_url']; - // Save next step (for session use) - $project->next_step = 'environments'; } @@ -324,7 +310,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); $form['default_platforms'][$env]['branch'] = array( '#type' => 'select', - '#default_value' => $project->default_platforms[$env], + '#default_value' => $project->default_platforms[$env]? $project->default_platforms[$env]: NULL, '#options' => $branch_options ); } @@ -387,8 +373,5 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project->default_platforms = $values['default_platforms']; $project->branch_platforms = $values['branch_platforms']; - - // Save next step (for session use) - $project->next_step = 'install'; } From 6f4680e31ccecc61de6e559a27f56270ab0e188d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 15:03:24 -0500 Subject: [PATCH 0162/3476] env step --- devshop_projects/devshop_projects.wizard.inc | 34 ++++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 20e7ae279..e40d111f5 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -35,12 +35,19 @@ function devshop_projects_create_wizard($step = NULL){ ctools_object_cache_set('project', $form_state['cache name'], $project); } - //THIS IS WHERE WILL STORE ALL FORM DATA + // All forms can access $form_state['project']; $form_state['project'] = $project; - // and this is the witchcraft that makes it work - $output = ctools_wizard_multistep_form($form_info, $step, $form_state); + // Particular overrides + if ($step == 'environments' && isset($project->nid)){ + $project_node = node_load($project->nid); + if (empty($project_node->git_branches)){ + //$form_info['show back'] = FALSE; + } + } + // Generate our ctools form and output + $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; } @@ -211,7 +218,6 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { // Saving the project node now, with these defaults based on the name. $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); $project->base_url = strtr(variable_get('devshop_project_default_base_url', "@name.localhost"), array('@name' => $project_name)); - } /********** @@ -267,7 +273,6 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { // verification. $project->code_path = $form_state['values']['code_path']; $project->base_url = $form_state['values']['base_url']; - } @@ -284,8 +289,18 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); $project_node = node_load($project->nid); + if (empty($project_node->git_branches)){ - drupal_set_message('Please wait while we connect to your repo and find out what branches you have.'); + + // @TODO: Detect and show errors when they occur. + $form['note'] = array( + '#type' => 'markup', + '#value' => t('Please wait while we connect to your repository and determine any branches. For now you must reload the page to continue.'), + ); + $form['not_ready'] = array( + '#type' => 'value', + '#value' => TRUE, + ); return; } @@ -334,6 +349,12 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) $project = &$form_state['project']; $values = $form_state['values']; + // Not ready + if ($values['not_ready']) { + form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); + return; + } + // At least one platform is required $none_chosen = TRUE; foreach ($values['default_platforms'] as $env => $data) { @@ -372,6 +393,5 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Set Values $project->default_platforms = $values['default_platforms']; $project->branch_platforms = $values['branch_platforms']; - } From d032aea7ede3b8b70521c8ecfda43b705cabdd3a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 16:45:43 -0500 Subject: [PATCH 0163/3476] default value for branch platforms, and a small isset --- devshop_projects/devshop_projects.wizard.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index e40d111f5..a7bbee59a 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -30,6 +30,7 @@ function devshop_projects_create_wizard($step = NULL){ $project->git_url = ''; $project->title = ''; $project->default_platforms = array(); + $project->branch_platforms = array(); // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); @@ -325,7 +326,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); $form['default_platforms'][$env]['branch'] = array( '#type' => 'select', - '#default_value' => $project->default_platforms[$env]? $project->default_platforms[$env]: NULL, + '#default_value' => isset($project->default_platforms[$env])? $project->default_platforms[$env]: NULL, '#options' => $branch_options ); } From 01fba86195ebaac7dc298cb5e16e2fe59b59e01f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 16:55:04 -0500 Subject: [PATCH 0164/3476] fixing comments and some notices --- devshop_projects/devshop_projects.wizard.inc | 30 ++++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index a7bbee59a..eb300dd5a 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -305,8 +305,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { return; } - $branch_options = array(t('Choose a branch...')); - $branch_options += array_combine($project_node->git_branches, $project_node->git_branches); + $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); $form['default_platforms'] = array( '#type' => 'fieldset', @@ -327,7 +326,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $form['default_platforms'][$env]['branch'] = array( '#type' => 'select', '#default_value' => isset($project->default_platforms[$env])? $project->default_platforms[$env]: NULL, - '#options' => $branch_options + '#options' => array(t('Choose a branch...')) + $branch_options ); } @@ -344,14 +343,14 @@ function devshop_project_create_step_environments(&$form, &$form_state) { /** - * STEP 2: SUBMIT + * STEP 3: Validate */ function devshop_project_create_step_environments_validate(&$form, &$form_state) { $project = &$form_state['project']; $values = $form_state['values']; // Not ready - if ($values['not_ready']) { + if (isset($values['not_ready'])) { form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); return; } @@ -383,8 +382,9 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) form_set_value($form['branch_platforms'], array_filter($values['branch_platforms']), $form_state); } + /** - * STEP 2: SUBMIT + * STEP 3: SUBMIT */ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Get project and reset properties.. @@ -394,5 +394,23 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Set Values $project->default_platforms = $values['default_platforms']; $project->branch_platforms = $values['branch_platforms']; + + // Run a "Create Platform" task for the chosen platforms. + + } + +/********** + * STEP 4 + * Project Environments + *********/ + +/** + * STEP 4: Form + */ +function devshop_project_create_step_install(&$form, &$form_state) { + $project = &$form_state['project']; + + +} From 3c65420cad062bb084b191f9c209500b9537535d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 3 Dec 2012 17:03:17 -0500 Subject: [PATCH 0165/3476] fixing comment --- devshop_projects/devshop_projects.task.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index f28ecbb93..58289a3ce 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -118,7 +118,7 @@ function devshop_projects_hosting_site_context_options(&$task) { } /** - * Implements hook_hosting_site_context_options() + * Implements hook_hosting_platform_context_options() * * This transfers data from the node to the aegir context object (the alias!) * For site entities. From 3049f4fb64b3a96466d19bf3e6c147161080b44e Mon Sep 17 00:00:00 2001 From: Aegir user Date: Thu, 6 Dec 2012 17:06:54 -0500 Subject: [PATCH 0166/3476] Creating a default project URL based on Hostmaster's URL and variables. --- devshop_projects/devshop_projects.wizard.inc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index eb300dd5a..7d2ff79a0 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -218,7 +218,11 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { // Saving the project node now, with these defaults based on the name. $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); - $project->base_url = strtr(variable_get('devshop_project_default_base_url', "@name.localhost"), array('@name' => $project_name)); + + // Setup project url. + $hostmaster_site = hosting_context_load('hostmaster'); + $server = variable_get('devshop_project_default_base_url', $hostmaster_site->title); + $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => $project_name, '@server' => $server)); } /********** @@ -231,7 +235,6 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { */ function devshop_project_create_step_settings(&$form, &$form_state) { $project = &$form_state['project']; - $form['code_path'] = array( '#type' => 'textfield', '#title' => t('Code path'), @@ -240,7 +243,6 @@ function devshop_project_create_step_settings(&$form, &$form_state) { '#size' => 40, '#default_value' => $project->code_path, '#maxlength' => 255, - '#weight' => 2, ); $form['base_url'] = array( '#type' => 'textfield', @@ -250,7 +252,6 @@ function devshop_project_create_step_settings(&$form, &$form_state) { '#size' => 40, '#default_value' => $project->base_url, '#maxlength' => 255, - '#weight' => 2, ); // @TODO: Put Pull Code settings here From db7a505621575f37efc7d0d5b4b200e2b3fb93d8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 6 Dec 2012 18:15:44 -0500 Subject: [PATCH 0167/3476] Refactoring nodeapi saves --- devshop_projects/devshop_projects.node.inc | 51 +++++++++++----------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index ba01d9d8a..b1fb4e4bf 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -24,38 +24,27 @@ function devshop_projects_node_info() { return $types; } - + /** - * hook_nodeapi() + * Implements hook_nodeapi() + * + * For Platforms and Sites: + * $node->project : The context name of the project. + * $node->environment: The type of environment this platform or site is: + * can be dev, test, or live. */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - if ($node->type == 'platform'){ - // On insert of a platform, determine project and save to hosting_devshop_project_objects - // @TODO: This is how we save the project path at the moment because - // the platform node is coming from hosting-import... - if ($op == 'insert') { - $path = explode('/', $node->publish_path); - $environment = array_pop($path); - $project_path = implode('/', $path); - - $project = devshop_project_load_by_path($project_path); - - // Save to table - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $environment); - } - } - - //Platforms and Sites + // Project-enabled platforms get if ($node->type == 'platform' || $node->type == 'site'){ - // Load Project info + // On Load: load this object's project and environment type if ($op == 'load'){ $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); return $data; } - // Display Project info + // Display Project info if ($op == 'view'){ $project = node_load($node->project_nid); if (!empty($project)){ @@ -73,11 +62,21 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { ); } } - - // The NODE is being deleted. Remove the nid from the objects table - if ($op == 'delete'){ - db_query('DELETE FROM {hosting_devshop_project_object} ' . - 'WHERE object_nid = %d', $node->nid); + + // On update or delete, delete the existing records. We will replace below. + if ($op = 'update' || $op = 'delete') { + db_query('DELETE FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid); + } + + // On insert or update, insert records saving this objects project and environment + if ($op = 'update' || $op = 'delete') { + if (!empty($node->project) && !empty($node->environment)) { + // Get the project node by name. + $project = hosting_context_load($node->project); + + // Save to table + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment); + } } } } From 30d7ef697e17be82edfda907a78e8e89d2c2916a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 12:29:50 -0500 Subject: [PATCH 0168/3476] nodemaker function --- devshop_projects/devshop_projects.node.inc | 3 +- devshop_projects/devshop_projects.wizard.inc | 62 +++++++++++++++----- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index b1fb4e4bf..2de5ebaef 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -32,6 +32,7 @@ function devshop_projects_node_info() { * $node->project : The context name of the project. * $node->environment: The type of environment this platform or site is: * can be dev, test, or live. + * $node->git_branch: The current git branch the project lives on. */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { @@ -44,7 +45,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { return $data; } - // Display Project info + // Display Project info if ($op == 'view'){ $project = node_load($node->project_nid); if (!empty($project)){ diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 7d2ff79a0..8a1baff75 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -46,12 +46,28 @@ function devshop_projects_create_wizard($step = NULL){ //$form_info['show back'] = FALSE; } } - + dsm($project, 'project'); // Generate our ctools form and output $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; } +/** + * Nodemaker + */ +function _devshop_projects_node_create($type, $node = stdClass){ + global $user; + + // @TODO: Validate type + $node->type = $type; + $node->status = 1; + $node->uid = $user->uid; + $node->name = $user->name; + if ($node = node_submit($node)) { + node_save($node); + } + return $node; +} /** * The form_info for the ctools wizard */ @@ -183,10 +199,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { */ function devshop_project_create_step_git_validate(&$from, &$form_state) { $project = &$form_state['project']; - $project->git_url = $form_state['values']['git_url']; - // @TODO: Check for duplicate project name here. - } /** @@ -199,11 +212,13 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project->git_url = $form_state['values']['git_url']; $project->title = $project_name = $form_state['values']['title']; - if (empty($project->nid)){ + if (empty($project->project_nid)){ // Create the project node now. We will update it with the chosen path. // This is so we can check for branches and save the hosting_context as soon // as possible. - $node = $project; + $node = new stdClass; + $node->title = $project->title; + $node->git_url = $project->git_url; $node->type = 'project'; $node->status = 1; $node->uid = $user->uid; @@ -212,11 +227,11 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { node_save($node); } if ($node->nid){ - $project->nid; + $project->project_nid; } } - // Saving the project node now, with these defaults based on the name. + // Now that we have the project name, set the defaults for code path and project URL $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); // Setup project url. @@ -314,7 +329,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#theme' => 'devshop_project_create_platforms_table', '#tree' => TRUE, ); - foreach ($default_platforms as $env) { $form['default_platforms'][$env] = array( '#tree' => TRUE, @@ -322,7 +336,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $form['default_platforms'][$env]['enabled'] = array( '#type' => 'checkbox', '#title' => $env, - '#default_value' => isset($project->default_platforms[$env]), + '#default_value' => !empty($project->default_platforms[$env]), ); $form['default_platforms'][$env]['branch'] = array( '#type' => 'select', @@ -368,8 +382,12 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // If you chose a default platform, you must choose a branch form_set_error("default_platforms][$env][branch", t('You must choose a branch to create a platform.')); } + } else { + // Set a null value. Otherwise this comes through as an array. + form_set_value($form['default_platforms'][$env], NULL, $form_state); } } + $branch_platforms = array_filter($values['branch_platforms']); if (!empty($branch_platforms)){ $none_chosen = FALSE; @@ -392,12 +410,24 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project = &$form_state['project']; $values = $form_state['values']; - // Set Values - $project->default_platforms = $values['default_platforms']; - $project->branch_platforms = $values['branch_platforms']; - - // Run a "Create Platform" task for the chosen platforms. + // Get platforms from form values and save into our object + $project->default_platforms = array_filter($values['default_platforms']); + $project->branch_platforms = array_filter($values['branch_platforms']); + // Create these platforms + if (empty($project->platforms)){ + $all_platforms = array_merge($project->default_platforms, $project->branch_platforms); + foreach ($all_platforms as $platform_name => $branch) { + // Save the damn platform nodes + $platform = new stdClass; + $platform->title = $project->name . '_' . $platform_name; + $platform->project = $project->name; + $platform->git_branch = $branch; + + $platform_node = _devshop_projects_node_create('platform', $project); + dsm($platform_node, $platform_node->title); + } + } } @@ -412,6 +442,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { */ function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; - + dsm($project); } From 373d8e498f41644120aa644100b7f5520c232bd2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 12:37:56 -0500 Subject: [PATCH 0169/3476] Gah! save the project nid --- devshop_projects/devshop_projects.wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 8a1baff75..c33928728 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -227,7 +227,7 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { node_save($node); } if ($node->nid){ - $project->project_nid; + $project->project_nid = $node->nid; } } @@ -305,7 +305,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); - $project_node = node_load($project->nid); + $project_node = node_load($project->project_nid); if (empty($project_node->git_branches)){ From 55776d3c008c27db18ba45f91d814fb990f1d38b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 13:04:24 -0500 Subject: [PATCH 0170/3476] Platform node creation WORKS. --- devshop_projects/devshop_projects.node.inc | 3 +- devshop_projects/devshop_projects.wizard.inc | 31 +++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 2de5ebaef..f92d1ad19 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -70,12 +70,13 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { } // On insert or update, insert records saving this objects project and environment - if ($op = 'update' || $op = 'delete') { + if ($op = 'update' || $op = 'insert') { if (!empty($node->project) && !empty($node->environment)) { // Get the project node by name. $project = hosting_context_load($node->project); // Save to table + // @TODO: start saving branch here too db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment); } } diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index c33928728..38d072309 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -46,7 +46,9 @@ function devshop_projects_create_wizard($step = NULL){ //$form_info['show back'] = FALSE; } } + dsm($project, 'project'); + // Generate our ctools form and output $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; @@ -310,6 +312,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { if (empty($project_node->git_branches)){ // @TODO: Detect and show errors when they occur. + // @TODO: Pretty this up, figure out how to maybe, display the task in the body? $form['note'] = array( '#type' => 'markup', '#value' => t('Please wait while we connect to your repository and determine any branches. For now you must reload the page to continue.'), @@ -415,20 +418,28 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project->branch_platforms = array_filter($values['branch_platforms']); // Create these platforms - if (empty($project->platforms)){ - $all_platforms = array_merge($project->default_platforms, $project->branch_platforms); - foreach ($all_platforms as $platform_name => $branch) { + $all_platforms = array_merge($project->default_platforms, $project->branch_platforms); + foreach ($all_platforms as $platform_name => $branch) { + + if (empty($project->platforms[$platform_name])){ // Save the damn platform nodes $platform = new stdClass; - $platform->title = $project->name . '_' . $platform_name; - $platform->project = $project->name; - $platform->git_branch = $branch; + + // hosting_platform fields + $platform->title = $project->title . '_' . $platform_name; + $platform->publish_path = $project->code_path . '/' . $platform_name; - $platform_node = _devshop_projects_node_create('platform', $project); - dsm($platform_node, $platform_node->title); + $servers = hosting_get_servers('http'); + $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers)); + + $platform->git_branch = $branch; + $platform->project = $project->title; + $platform->environment = $platform_name; + + $platform_node = _devshop_projects_node_create('platform', $platform); + $project->platforms[$platform_name] = $platform_node; } } - } @@ -442,6 +453,4 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { */ function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; - dsm($project); - } From c96f43d33f15e8e065371628f32603f6a237791e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 13:18:29 -0500 Subject: [PATCH 0171/3476] moving replacement tasks table function to module file --- devshop_projects/devshop_projects.module | 77 +++++++++++++++++++++ devshop_projects/devshop_projects.task.inc | 79 ---------------------- 2 files changed, 77 insertions(+), 79 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 51ae8d8d6..fc4ed4dc7 100755 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -212,3 +212,80 @@ function devshop_projects_project_data_get($nid) { } +/** + * A replacement for hosting_task_table, to allow us to add + * tasks from other nodes. + */ +function devshop_projects_hosting_task_table($node) { + $output = ''; + + $headers[] = t('Task'); + $headers[] = array( + 'data' => t('Actions'), + 'class' => 'hosting-actions', + ); + + $tasklist = hosting_task_fetch_tasks($node->nid); + if ($node->project_status != 'sites_ready'){ + return; + } + + // Get tasklists for all sites + $tasks = array(); + foreach ($node->project_objects['site'] as $nid => $env){ + $site_nodes[$env] = node_load($nid); + $tasks[$env] = hosting_task_fetch_tasks($nid); + } + + // Add our own specific tasks + $tasklist['devshop-commit'] = $tasks['dev']['devshop-commit']; + $tasklist['devshop-sync'] = $tasks['test']['devshop-sync']; + $tasklist['devshop-pull'] = $tasks['live']['devshop-pull']; + + // Enhance titles + $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, 'http://'. $site_nodes['dev']->title, array('attributes' => array('target' => '_blank'))); + $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); + $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); + + // Override some + unset($tasklist['devshop-create']['task_permitted']); + unset($tasklist['devshop-create']['nid']); + + unset($tasklist['devshop-platform-create']['task_permitted']); + unset($tasklist['devshop-platform-create']['nid']); + + foreach ($tasklist as $task => $info) { + $row = array(); + + if (!isset($info['nid']) && !$info['task_permitted']) { + // just don't show those tasks, since we'll not be able to run them + continue; + } + + $row['type'] = array( + 'data' => $info['title'], + 'class' => 'hosting-status', + ); + $actions = array(); + + if (isset($info['task_status']) && ($info['task_status'] == 0)) { + $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']); + } + else { + $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); + } + + $actions['log'] = _hosting_task_button(t('Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); + $row['actions'] = array( + 'data' => implode('', $actions), + 'class' => 'hosting-actions', + ); + + $rows[] = array( + 'data' => $row, + 'class' => $info['class'], + ); + } + $output .= theme('table', $headers, $rows, array('class' => 'hosting-table')); + return $output; +} diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index f28ecbb93..4570a1f8e 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -127,85 +127,6 @@ function devshop_projects_hosting_platform_context_options(&$task) { $task->context_options['project'] = $task->ref->project; } - -/** - * A replacement for hosting_task_table, to allow us to add - * tasks from other nodes. - */ -function devshop_projects_hosting_task_table($node) { - $output = ''; - - $headers[] = t('Task'); - $headers[] = array( - 'data' => t('Actions'), - 'class' => 'hosting-actions', - ); - - $tasklist = hosting_task_fetch_tasks($node->nid); - if ($node->project_status != 'sites_ready'){ - return; - } - - // Get tasklists for all sites - $tasks = array(); - foreach ($node->project_objects['site'] as $nid => $env){ - $site_nodes[$env] = node_load($nid); - $tasks[$env] = hosting_task_fetch_tasks($nid); - } - - // Add our own specific tasks - $tasklist['devshop-commit'] = $tasks['dev']['devshop-commit']; - $tasklist['devshop-sync'] = $tasks['test']['devshop-sync']; - $tasklist['devshop-pull'] = $tasks['live']['devshop-pull']; - - // Enhance titles - $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, 'http://'. $site_nodes['dev']->title, array('attributes' => array('target' => '_blank'))); - $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); - $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); - - // Override some - unset($tasklist['devshop-create']['task_permitted']); - unset($tasklist['devshop-create']['nid']); - - unset($tasklist['devshop-platform-create']['task_permitted']); - unset($tasklist['devshop-platform-create']['nid']); - - foreach ($tasklist as $task => $info) { - $row = array(); - - if (!isset($info['nid']) && !$info['task_permitted']) { - // just don't show those tasks, since we'll not be able to run them - continue; - } - - $row['type'] = array( - 'data' => $info['title'], - 'class' => 'hosting-status', - ); - $actions = array(); - - if (isset($info['task_status']) && ($info['task_status'] == 0)) { - $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']); - } - else { - $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); - } - - $actions['log'] = _hosting_task_button(t('Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); - $row['actions'] = array( - 'data' => implode('', $actions), - 'class' => 'hosting-actions', - ); - - $rows[] = array( - 'data' => $row, - 'class' => $info['class'], - ); - } - $output .= theme('table', $headers, $rows, array('class' => 'hosting-table')); - return $output; -} - /* * Implementation of hook_post_hosting_TASK_TYPE_task * From 11fe170789a7ac5d567a464f6629b47cd492a16b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 15:23:50 -0500 Subject: [PATCH 0172/3476] adding git_branch column --- devshop_projects/devshop_projects.install | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 3a325a35f..15cd472a9 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -76,6 +76,12 @@ function devshop_projects_schema() { 'default' => '', 'description' => 'Environment type: dev, test, live, or sandbox.', ), + 'git_branch' => array( + 'type' => 'varchar', + 'length' => 16, + 'not null' => FALSE, + 'description' => 'The current branch of this site or platform.', + ), ), 'primary key' => array('object_nid'), ); @@ -127,3 +133,12 @@ function devshop_projects_update_2() { return $ret; } + +/** + * Adds git_branch column to hosting_devshop_project_objects table. + */ +function devshop_projects_update_3() { + $ret = array(); + $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} ADD COLUMN git_branch VARCHAR(16) NULL"); + return $ret; +} From f943a5edccd2b65684395deb24fbceaf8c7482bb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 15:24:11 -0500 Subject: [PATCH 0173/3476] fixing up node load variables. --- devshop_projects/devshop_projects.node.inc | 33 +++++++++++----------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index f92d1ad19..2a27d02c7 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -41,25 +41,24 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On Load: load this object's project and environment type if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT project_nid, env_type AS environment, n.title as project FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); return $data; } // Display Project info if ($op == 'view'){ - $project = node_load($node->project_nid); - if (!empty($project)){ + if (!empty($node->project)){ $node->content['info']['project'] = array( - '#type' => 'item', - '#title' => t('Project'), - '#value' => l($project->title, "node/$project->nid"), - '#weight' => -12 + '#type' => 'item', + '#title' => t('Project'), + '#value' => l($node->project, "node/$project->project_nid"), + '#weight' => -12 ); $node->content['info']['env'] = array( - '#type' => 'item', - '#title' => t('Environment Type'), - '#value' => $node->project_environment, - '#weight' => -11 + '#type' => 'item', + '#title' => t('Environment Type'), + '#value' => $node->environment, + '#weight' => -11 ); } } @@ -72,12 +71,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On insert or update, insert records saving this objects project and environment if ($op = 'update' || $op = 'insert') { if (!empty($node->project) && !empty($node->environment)) { - // Get the project node by name. - $project = hosting_context_load($node->project); - - // Save to table - // @TODO: start saving branch here too - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment); + // Get the project node by name. + $project = hosting_context_load($node->project); + + // Save to table + // @TODO: start saving branch here too + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment); } } } From c08b25eff2b195232bab35878f30ecd950144959 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 15:26:53 -0500 Subject: [PATCH 0174/3476] bad whitespace --- devshop_projects/devshop_projects.node.inc | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 2a27d02c7..07a992370 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -49,16 +49,16 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { if ($op == 'view'){ if (!empty($node->project)){ $node->content['info']['project'] = array( - '#type' => 'item', - '#title' => t('Project'), - '#value' => l($node->project, "node/$project->project_nid"), - '#weight' => -12 - ); + '#type' => 'item', + '#title' => t('Project'), + '#value' => l($node->project, "node/$project->project_nid"), + '#weight' => -12, + ); $node->content['info']['env'] = array( - '#type' => 'item', - '#title' => t('Environment Type'), - '#value' => $node->environment, - '#weight' => -11 + '#type' => 'item', + '#title' => t('Environment Type'), + '#value' => $node->environment, + '#weight' => -11, ); } } @@ -71,12 +71,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On insert or update, insert records saving this objects project and environment if ($op = 'update' || $op = 'insert') { if (!empty($node->project) && !empty($node->environment)) { - // Get the project node by name. - $project = hosting_context_load($node->project); - - // Save to table - // @TODO: start saving branch here too - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment); + // Get the project node by name. + $project = hosting_context_load($node->project); + + // Save to table + // @TODO: start saving branch here too + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment); } } } From 682c8a3b5479357bdbc82497849506ce748dc8eb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 15:29:25 -0500 Subject: [PATCH 0175/3476] adding git url and branch to site and platform node loads. --- devshop_projects/devshop_projects.node.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 07a992370..153f9fecd 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -41,7 +41,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On Load: load this object's project and environment type if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, env_type AS environment, n.title as project FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT project_nid, env_type AS environment, n.title as project, git_url, git_branch FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); return $data; } From 5196b40375318a012557942b338acc13e2d4c402 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 16:22:24 -0500 Subject: [PATCH 0176/3476] whitespace --- devshop_projects/devshop_projects.task.inc | 19 ++++++++++++++----- devshop_projects/devshop_projects.wizard.inc | 3 +++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/devshop_projects.task.inc index 4570a1f8e..9be88a813 100644 --- a/devshop_projects/devshop_projects.task.inc +++ b/devshop_projects/devshop_projects.task.inc @@ -34,12 +34,11 @@ function devshop_projects_hosting_tasks() { return $tasks; } - /** * Implements hook_hosting_project_context_options() * - * This transfers data from the node to the aegir context object (the alias!) - * For project entities. + * This transfers data from the node to thes aegir context object (the alias!) + * For project entities. This is where we find the branches and tags on the remote. */ function devshop_projects_hosting_project_context_options(&$task) { @@ -61,6 +60,9 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['git_tags'] = $branches['tags']; } +/** + * Helpfer for getting branches and tags from a git URL + */ function getBranchesAndTags($git_url = NULL){ if (is_null($git_url)){ $git_url = drush_get_option('git_url'); @@ -114,7 +116,8 @@ function getBranchesAndTags($git_url = NULL){ * For site entities. */ function devshop_projects_hosting_site_context_options(&$task) { - $task->context_options['project'] = $task->ref->git_url; + $task->context_options['project'] = $task->ref->project; + $task->context_options['nerd'] = 'vision'; } /** @@ -124,7 +127,13 @@ function devshop_projects_hosting_site_context_options(&$task) { * For site entities. */ function devshop_projects_hosting_platform_context_options(&$task) { - $task->context_options['project'] = $task->ref->project; + if (!empty($task->ref->project)){ + $task->context_options['project'] = $task->ref->project; + $task->properties['task properties'] = 'works'; + $task->ref->properties['task ref properties'] = 'works'; + + d()->setProperty('setProperty', 'works'); + } } /* diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 38d072309..be15d2354 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -453,4 +453,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { */ function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; + + + } From d655f79ee98cf3eccd254fd0255da678e933f03b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 17:03:59 -0500 Subject: [PATCH 0177/3476] loading git_branch with platform_node --- devshop_projects/devshop_projects.node.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 153f9fecd..8e0a10a0a 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -76,7 +76,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Save to table // @TODO: start saving branch here too - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment); + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); } } } From c2d6bc64553b443fc8a8ae9d9f4e1157fc7b6b34 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 17:36:18 -0500 Subject: [PATCH 0178/3476] adding a table of platforms --- devshop_projects/devshop_projects.wizard.inc | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index be15d2354..b738d00a7 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -453,7 +453,35 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { */ function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; + $completed = TRUE; + + // Display the platforms + $rows = array(); + $header = array(t('Name'), t('Status')); + foreach ($project->platforms as $name => $node){ + $row = array(); + $row[] = $name; + $row[] = $name->verified? t('Verified'): t('Verification Pending'); + $rows[] = $row; + + if (!$name->verified){ + $completed = FALSE; + } + } + + if ($completed){ + $form['help'] = array( + '#type' => 'markup', + '#value' => 'Now you have to choose your install profile.', + ); + } + + $form['platforms'] = array( + '#type' => 'markup', + '#value' => theme('table', $header, $rows), + ); + } From a52d903be28af008d185740db316df082afd1dd3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 17:45:31 -0500 Subject: [PATCH 0179/3476] - Fixing project link - Redirect from unpublished project pages to wizard (for now) - new projects have status = 0 until final step. - Adding branch to last step table. --- devshop_projects/devshop_projects.node.inc | 10 +++++++++- devshop_projects/devshop_projects.wizard.inc | 14 ++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 8e0a10a0a..7cef3103b 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -51,7 +51,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $node->content['info']['project'] = array( '#type' => 'item', '#title' => t('Project'), - '#value' => l($node->project, "node/$project->project_nid"), + '#value' => l($node->project, "node/{$node->project_nid}"), '#weight' => -12, ); $node->content['info']['env'] = array( @@ -213,6 +213,14 @@ function devshop_projects_load($node) { * Implementation of hook_view(). */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { + + // If not ready yet: + if (!$node->status){ + // Go back to the wizard. + drupal_goto('node/add/project'); + } + + modalframe_parent_js(); $node->content['info'] = array( diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index b738d00a7..9436fd0b2 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -222,7 +222,7 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $node->title = $project->title; $node->git_url = $project->git_url; $node->type = 'project'; - $node->status = 1; + $node->status = 0; $node->uid = $user->uid; $node->name = $user->name; if ($node = node_submit($node)) { @@ -437,7 +437,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform->environment = $platform_name; $platform_node = _devshop_projects_node_create('platform', $platform); - $project->platforms[$platform_name] = $platform_node; + $project->platforms[$platform_name] = $platform_node->nid; } } } @@ -458,14 +458,16 @@ function devshop_project_create_step_install(&$form, &$form_state) { // Display the platforms $rows = array(); - $header = array(t('Name'), t('Status')); - foreach ($project->platforms as $name => $node){ + $header = array(t('Name'), t('Branch'), t('Status')); + foreach ($project->platforms as $name => $nid){ + $platform = node_load($nid); $row = array(); $row[] = $name; - $row[] = $name->verified? t('Verified'): t('Verification Pending'); + $row[] = $platform->git_branch; + $row[] = $platform->verified? t('Verified'): t('Verification Pending'); $rows[] = $row; - if (!$name->verified){ + if (!$platform->verified){ $completed = FALSE; } } From b5d3929afc797f8bb1718ec8dc3c5f291a2262f7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 17:53:50 -0500 Subject: [PATCH 0180/3476] adding form for choosing install profile --- devshop_projects/devshop_projects.wizard.inc | 23 +++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 9436fd0b2..9267afb02 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -455,7 +455,8 @@ function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; $completed = TRUE; - + $profiles = array(); + // Display the platforms $rows = array(); $header = array(t('Name'), t('Branch'), t('Status')); @@ -470,6 +471,15 @@ function devshop_project_create_step_install(&$form, &$form_state) { if (!$platform->verified){ $completed = FALSE; } + + // Collect install profiles + $profiles[$name] = hosting_get_profiles($platform->nid); + + if (!isset($available_profiles)){ + $available_profiles = $profiles[$name]; + } else { + $available_profiles = array_intersect_key($profiles[$name]); + } } if ($completed){ @@ -479,6 +489,17 @@ function devshop_project_create_step_install(&$form, &$form_state) { ); } + + // Install Profile + // @TODO: Handle no available profiles + $form['install_profile'] = array( + '#type' => 'radios', + '#options' => $available_profiles, + '#title' => t('Project Install Profile'), + '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches.'), + ); + + $form['platforms'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), From b31b6918879d0161a9e14396cd0fa766f25d0e92 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 18:00:44 -0500 Subject: [PATCH 0181/3476] Show available install profiles once the platforms have verified. --- devshop_projects/devshop_projects.wizard.inc | 31 ++++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 9267afb02..d9541f2a3 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -474,37 +474,36 @@ function devshop_project_create_step_install(&$form, &$form_state) { // Collect install profiles $profiles[$name] = hosting_get_profiles($platform->nid); - if (!isset($available_profiles)){ $available_profiles = $profiles[$name]; } else { - $available_profiles = array_intersect_key($profiles[$name]); + $available_profiles = array_intersect_key($profiles[$name], $available_profiles); } } - if ($completed){ + // Provide something for people while they wait. + if (!$completed){ $form['help'] = array( '#type' => 'markup', - '#value' => 'Now you have to choose your install profile.', + '#value' => t('Please wait while we download and verify your drupal code.'), + ); + + } else { + // Install Profile + // @TODO: Handle no available profiles + $form['install_profile'] = array( + '#type' => 'radios', + '#options' => $available_profiles, + '#title' => t('Project Install Profile'), + '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches.'), ); } - - // Install Profile - // @TODO: Handle no available profiles - $form['install_profile'] = array( - '#type' => 'radios', - '#options' => $available_profiles, - '#title' => t('Project Install Profile'), - '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches.'), - ); - - + // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" $form['platforms'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), ); - } From 65bc5a28a614f0727a38dea32a7888928edd4575 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 20:08:59 -0500 Subject: [PATCH 0182/3476] adding more site options --- devshop_projects/devshop_projects.wizard.inc | 129 ++++++++++++------- 1 file changed, 84 insertions(+), 45 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index d9541f2a3..fcc2beb5b 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -7,21 +7,21 @@ function devshop_projects_create_wizard($step = NULL){ // required includes for wizard ctools_include('wizard'); ctools_include('object-cache'); - + // Setup form info $form_info = devshop_projects_create_wizard_info(); $form_state = array( 'cache name' => NULL, ); - + // Setup project $project = ctools_object_cache_get('project', NULL); - + // Setup Step. if ($step == NULL){ $step = current(array_keys($form_info['order'])); } - + // Create default project object if (empty($project)){ // set form to first step -- we have no data @@ -35,10 +35,10 @@ function devshop_projects_create_wizard($step = NULL){ // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); } - + // All forms can access $form_state['project']; $form_state['project'] = $project; - + // Particular overrides if ($step == 'environments' && isset($project->nid)){ $project_node = node_load($project->nid); @@ -59,7 +59,7 @@ function devshop_projects_create_wizard($step = NULL){ */ function _devshop_projects_node_create($type, $node = stdClass){ global $user; - + // @TODO: Validate type $node->type = $type; $node->status = 1; @@ -146,7 +146,7 @@ function devshop_projects_create_wizard_cancel(&$form_state) { // Update the cache with changes. ctools_object_cache_clear('project', $form_state['cache name']); $form_state['redirect'] = 'hosting/projects'; - + // @TODO: If step 1 has been completed, we should run Delete on Project node. // to get rid of drush alias context, files, everything that this form // may have created. @@ -155,7 +155,7 @@ function devshop_projects_create_wizard_cancel(&$form_state) { /** * WIZARD STEPS */ - + /********** * STEP 1 @@ -165,7 +165,7 @@ function devshop_projects_create_wizard_cancel(&$form_state) { /** * STEP 1: Form - * + * * Just get the Git URL. If we can't access the code, all else should fail. */ function devshop_project_create_step_git(&$form, &$form_state) { @@ -190,7 +190,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { /** * STEP 1: Validate - * + * * This is where we try and connect to the remote branches. * * As long as the SSH for www-data is setup right, this will work for private repos. @@ -209,7 +209,7 @@ function devshop_project_create_step_git_validate(&$from, &$form_state) { */ function devshop_project_create_step_git_submit(&$from, &$form_state) { global $user; - + $project = &$form_state['project']; $project->git_url = $form_state['values']['git_url']; $project->title = $project_name = $form_state['values']['title']; @@ -232,10 +232,10 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project->project_nid = $node->nid; } } - + // Now that we have the project name, set the defaults for code path and project URL $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); - + // Setup project url. $hostmaster_site = hosting_context_load('hostmaster'); $server = variable_get('devshop_project_default_base_url', $hostmaster_site->title); @@ -270,7 +270,7 @@ function devshop_project_create_step_settings(&$form, &$form_state) { '#default_value' => $project->base_url, '#maxlength' => 255, ); - + // @TODO: Put Pull Code settings here } @@ -287,14 +287,14 @@ function devshop_project_create_step_settings_validate(&$from, &$form_state) { function devshop_project_create_step_settings_submit(&$from, &$form_state) { global $user; $project = &$form_state['project']; - + // For this step, we just save code path and base url for later saving and // verification. $project->code_path = $form_state['values']['code_path']; $project->base_url = $form_state['values']['base_url']; } - - + + /********** * STEP 3 * Project Environments @@ -306,11 +306,11 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); - + $project_node = node_load($project->project_nid); - + if (empty($project_node->git_branches)){ - + // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? $form['note'] = array( @@ -323,9 +323,9 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); return; } - + $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); - + $form['default_platforms'] = array( '#type' => 'fieldset', '#title' => t('Default Platforms'), @@ -347,7 +347,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#options' => array(t('Choose a branch...')) + $branch_options ); } - + $form['branch_platforms'] = array( '#type' => 'checkboxes', '#multiple' => 'true', @@ -356,7 +356,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#default_value' => $project->branch_platforms, '#options' => $branch_options ); - $form_state['no buttons'] = TRUE; + $form_state['no buttons'] = TRUE; } @@ -366,14 +366,14 @@ function devshop_project_create_step_environments(&$form, &$form_state) { function devshop_project_create_step_environments_validate(&$form, &$form_state) { $project = &$form_state['project']; $values = $form_state['values']; - + // Not ready if (isset($values['not_ready'])) { form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); return; } - - // At least one platform is required + + // At least one platform is required $none_chosen = TRUE; foreach ($values['default_platforms'] as $env => $data) { if ($data['enabled']){ @@ -390,33 +390,33 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) form_set_value($form['default_platforms'][$env], NULL, $form_state); } } - + $branch_platforms = array_filter($values['branch_platforms']); if (!empty($branch_platforms)){ $none_chosen = FALSE; } - + if ($none_chosen){ form_set_error('form', t('You must choose to build at least one platform.')); } - + // Filter out branch platforms forms_api so we can keep the submit clean. form_set_value($form['branch_platforms'], array_filter($values['branch_platforms']), $form_state); - + } /** * STEP 3: SUBMIT */ function devshop_project_create_step_environments_submit(&$form, &$form_state) { - // Get project and reset properties.. + // Get project and reset properties.. $project = &$form_state['project']; $values = $form_state['values']; // Get platforms from form values and save into our object $project->default_platforms = array_filter($values['default_platforms']); $project->branch_platforms = array_filter($values['branch_platforms']); - + // Create these platforms $all_platforms = array_merge($project->default_platforms, $project->branch_platforms); foreach ($all_platforms as $platform_name => $branch) { @@ -424,18 +424,18 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { if (empty($project->platforms[$platform_name])){ // Save the damn platform nodes $platform = new stdClass; - + // hosting_platform fields $platform->title = $project->title . '_' . $platform_name; $platform->publish_path = $project->code_path . '/' . $platform_name; $servers = hosting_get_servers('http'); $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers)); - + $platform->git_branch = $branch; $platform->project = $project->title; $platform->environment = $platform_name; - + $platform_node = _devshop_projects_node_create('platform', $platform); $project->platforms[$platform_name] = $platform_node->nid; } @@ -454,9 +454,9 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; $completed = TRUE; - + $profiles = array(); - + // Display the platforms $rows = array(); $header = array(t('Name'), t('Branch'), t('Status')); @@ -467,11 +467,11 @@ function devshop_project_create_step_install(&$form, &$form_state) { $row[] = $platform->git_branch; $row[] = $platform->verified? t('Verified'): t('Verification Pending'); $rows[] = $row; - + if (!$platform->verified){ $completed = FALSE; } - + // Collect install profiles $profiles[$name] = hosting_get_profiles($platform->nid); if (!isset($available_profiles)){ @@ -479,15 +479,54 @@ function devshop_project_create_step_install(&$form, &$form_state) { } else { $available_profiles = array_intersect_key($profiles[$name], $available_profiles); } + + // Build site options + $defaults = array(); + $defaults['dev']['pull'] = 1; + $defaults['dev']['dev_modules'] = 1; + $defaults['test']['run_tests'] = 1; + $defaults['live']['live_modules'] = 1; + + // @TODO: Would be fun to use hooks to do this stuff... + $form['site_options'][$name] = array( + '#type' => 'fieldset', + '#title' => $name, + ); + $form['site_options'][$name]['branch'] = array( + '#type' => 'item', + '#title' => t('Branch'), + '#description' => t('This can be changed later.'), + '#value' => $platform->git_branch, + ); + $form['site_options'][$name]['pull'] = array( + '#type' => 'checkbox', + '#title' => t('Pull on Commit'), + '#default_value' => $defaults[$name]['pull'], + ); + $form['site_options'][$name]['dev_modules'] = array( + '#type' => 'checkbox', + '#title' => t('Enable dev modules'), + '#default_value' => $defaults[$name]['dev_modules'], + ); + $form['site_options'][$name]['run_tests'] = array( + '#type' => 'checkbox', + '#title' => t('Allow Run Tests'), + '#default_value' => $defaults[$name]['run_tests'], + ); + $form['site_options'][$name]['live_modules'] = array( + '#type' => 'checkbox', + '#title' => t('Enable live modules'), + '#default_value' => $defaults[$name]['live_modules'], + ); } - + // Provide something for people while they wait. if (!$completed){ $form['help'] = array( '#type' => 'markup', '#value' => t('Please wait while we download and verify your drupal code.'), ); - + } else { // Install Profile // @TODO: Handle no available profiles @@ -498,12 +537,12 @@ function devshop_project_create_step_install(&$form, &$form_state) { '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches.'), ); } - + // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" $form['platforms'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), ); - + } From 8fb4e955c2e2d97ab032d910ca9a52b5a39efce2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 20:14:49 -0500 Subject: [PATCH 0183/3476] adding submit and validate and @TODOs... almomst done! --- devshop_projects/devshop_projects.wizard.inc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index fcc2beb5b..d66fb0543 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -534,6 +534,7 @@ function devshop_project_create_step_install(&$form, &$form_state) { '#type' => 'radios', '#options' => $available_profiles, '#title' => t('Project Install Profile'), + '#required' => 1, '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches.'), ); } @@ -543,6 +544,21 @@ function devshop_project_create_step_install(&$form, &$form_state) { '#type' => 'markup', '#value' => theme('table', $header, $rows), ); +} + +/** + * STEP 4: VALIDATE + */ +function devshop_project_create_step_install_validate(&$form, &$form_state) { + +} + +/** + * STEP 4: SUBMIT + */ +function devshop_project_create_step_install_submit(&$form, &$form_state) { + // @TODO: Save install profile, base_url, etc to project node. + // @TODO: Save site nodes to the chosen platforms } From e4efcd7747a21ca801398a6cb4631c377b4e6e70 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 23:09:34 -0500 Subject: [PATCH 0184/3476] removing stuff that will be services --- devshop_projects/devshop_projects.wizard.inc | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index d66fb0543..fa38122c6 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -498,26 +498,6 @@ function devshop_project_create_step_install(&$form, &$form_state) { '#description' => t('This can be changed later.'), '#value' => $platform->git_branch, ); - $form['site_options'][$name]['pull'] = array( - '#type' => 'checkbox', - '#title' => t('Pull on Commit'), - '#default_value' => $defaults[$name]['pull'], - ); - $form['site_options'][$name]['dev_modules'] = array( - '#type' => 'checkbox', - '#title' => t('Enable dev modules'), - '#default_value' => $defaults[$name]['dev_modules'], - ); - $form['site_options'][$name]['run_tests'] = array( - '#type' => 'checkbox', - '#title' => t('Allow Run Tests'), - '#default_value' => $defaults[$name]['run_tests'], - ); - $form['site_options'][$name]['live_modules'] = array( - '#type' => 'checkbox', - '#title' => t('Enable live modules'), - '#default_value' => $defaults[$name]['live_modules'], - ); } // Provide something for people while they wait. From 3b88e67dfc9873a92c23492a221251c4a491c966 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 23:16:00 -0500 Subject: [PATCH 0185/3476] adding TODO --- devshop_projects/devshop_projects.wizard.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index fa38122c6..cf012604d 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -450,6 +450,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { /** * STEP 4: Form + * @TODO: MOVE TO PROJECT NODE FORM! */ function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; From 3aabf8f764ce9ab0ebd04b5141dc5c1130caa997 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 23:26:57 -0500 Subject: [PATCH 0186/3476] adding verification on insert --- devshop_projects/devshop_projects.node.inc | 55 ++++++++++++---------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 7cef3103b..2460bd824 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -1,6 +1,6 @@ 'devshop_project', + "type" => 'devshop_project', "name" => 'DevShop Project', "module" => 'devshop_projects', "has_title" => TRUE, "title_label" => t('Project Codename'), "description" => t('Create a project'), - "has_body" => 0, - "body_label" => '', + "has_body" => 0, + "body_label" => '', "min_word_count" => 0 ); return $types; } - + /** * Implements hook_nodeapi() * @@ -38,13 +38,13 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Project-enabled platforms get if ($node->type == 'platform' || $node->type == 'site'){ - + // On Load: load this object's project and environment type if ($op == 'load'){ $data = db_fetch_array(db_query('SELECT project_nid, env_type AS environment, n.title as project, git_url, git_branch FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); return $data; } - + // Display Project info if ($op == 'view'){ if (!empty($node->project)){ @@ -62,18 +62,18 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { ); } } - + // On update or delete, delete the existing records. We will replace below. if ($op = 'update' || $op = 'delete') { db_query('DELETE FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid); } - + // On insert or update, insert records saving this objects project and environment if ($op = 'update' || $op = 'insert') { if (!empty($node->project) && !empty($node->environment)) { // Get the project node by name. $project = hosting_context_load($node->project); - + // Save to table // @TODO: start saving branch here too db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); @@ -94,6 +94,9 @@ function devshop_project_load_by_path($project_path){ * Implementation of hook_insert(). */ function devshop_projects_insert($node) { + if (!isset($node->no_verify)) { + hosting_add_task($node->nid, 'verify'); + } db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url) ". "VALUES (%d, '%s', '%s', '%s')", @@ -103,7 +106,7 @@ function devshop_projects_insert($node) { if ((!$node->old_vid)) { hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); } - + // Create hostmaster task hosting_add_task($node->nid, 'verify'); } @@ -120,7 +123,7 @@ function devshop_projects_update($node) { db_query("UPDATE {hosting_devshop_project} " . "SET git_url = '%s', code_path = '%s', base_url = '%s', data='%s' " . "WHERE nid = %d", - $node->git_url, hosting_path_normalize($node->code_path), + $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, serialize($data), $node->nid); // //// If this is a rerry, kick start another devshop-create @@ -139,7 +142,7 @@ function devshop_projects_delete($node) { db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); - + hosting_context_delete($node->nid); } @@ -161,7 +164,7 @@ function devshop_projects_load($node) { $data = unserialize($additions['data']); $additions['git_branches'] = $data['git_branches']; $additions['git_tags'] = $data['git_tags']; - + $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); $objects = array(); @@ -169,12 +172,12 @@ function devshop_projects_load($node) { $objects[$project_object->object_type][$project_object->object_nid] = $project_object->env_type; } $additions['project_objects'] = $objects; - + //Project status $platforms_ready = TRUE; $sites_ready = TRUE; $sites_installing = FALSE; - + // PLATFORMS STATUS: Determine if all platforms are verified. if (isset($additions['project_objects']['platform'])){ foreach ($additions['project_objects']['platform'] as $nid => $env){ @@ -198,14 +201,14 @@ function devshop_projects_load($node) { } else { $sites_ready = FALSE; } - + // Set status $additions['project_status'] = $sites_ready? 'sites_ready': ( $sites_installing? 'sites_installing': ( $platforms_ready? 'platforms_ready': 'platforms_verifying' - ) + ) ); - + return $additions; } @@ -213,14 +216,14 @@ function devshop_projects_load($node) { * Implementation of hook_view(). */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { - + // If not ready yet: if (!$node->status){ // Go back to the wizard. - drupal_goto('node/add/project'); + //drupal_goto('node/add/project'); } - + modalframe_parent_js(); $node->content['info'] = array( @@ -248,7 +251,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#value' => l($url, $url), '#weight' => -10 ); - + if (!empty($node->install_profile)){ $node->content['info']['install_profile'] = array( '#type' => 'item', @@ -303,7 +306,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($node->project_status == 'sites_ready'){ $node->content['sites']['add_platform'] = array( '#type' => 'item', - '#value' => l(t('Create and add additional platforms'), + '#value' => l(t('Create and add additional platforms'), "hosting/projects/platform/create/$node->nid"), ); } @@ -313,7 +316,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#value' => l("Delete this project", "hosting/projects/delete/$node->nid") ); - //Tasks + //Tasks $node->content['tasks_view'] = array( '#type' => 'item', //'#value' => devshop_projects_hosting_task_table($node), @@ -327,7 +330,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'changed' => $node->changed, ); drupal_add_js($settings, 'setting'); - drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); + drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); // MAIN DISPLAY $node->content['devshop'] = array( From 6d5eae4b9ca2aec37b993536d2442136bccc82fb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 23:27:43 -0500 Subject: [PATCH 0187/3476] on wizard finish, save the project node. --- devshop_projects/devshop_projects.wizard.inc | 46 ++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index cf012604d..72b123d41 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -54,22 +54,6 @@ function devshop_projects_create_wizard($step = NULL){ return $output; } -/** - * Nodemaker - */ -function _devshop_projects_node_create($type, $node = stdClass){ - global $user; - - // @TODO: Validate type - $node->type = $type; - $node->status = 1; - $node->uid = $user->uid; - $node->name = $user->name; - if ($node = node_submit($node)) { - node_save($node); - } - return $node; -} /** * The form_info for the ctools wizard */ @@ -112,6 +96,23 @@ function devshop_projects_create_wizard_info(){ * WIZARD TOOLS */ +/** + * Nodemaker + */ +function _devshop_projects_node_create($type, $node = stdClass){ + global $user; + + // @TODO: Validate type + $node->type = $type; + $node->status = 1; + $node->uid = $user->uid; + $node->name = $user->name; + if ($node = node_submit($node)) { + node_save($node); + } + return $node; +} + /** * NEXT callback * Saves anything in $form_state['project'] to ctools cache. @@ -132,6 +133,16 @@ function devshop_projects_create_wizard_next(&$form_state) { */ function devshop_projects_create_wizard_finish(&$form_state) { $project = &$form_state['project']; + + // Save the extra options to the project + $node = node_load($project->project_nid); + $node->code_path = $project->code_path; + $node->base_url = $project->base_url; + + // Everything else is done on the Node Page. We will fake the last step on + // the node page. + node_save($node); + ctools_object_cache_clear('project', $form_state['cache name']); $form_state['redirect'] = 'hosting/projects'; } @@ -455,7 +466,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { function devshop_project_create_step_install(&$form, &$form_state) { $project = &$form_state['project']; $completed = TRUE; - +/* $profiles = array(); // Display the platforms @@ -525,6 +536,7 @@ function devshop_project_create_step_install(&$form, &$form_state) { '#type' => 'markup', '#value' => theme('table', $header, $rows), ); + */ } /** From 343f34974b4e9f8935b285da1f773fcb85beb404 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Dec 2012 23:58:14 -0500 Subject: [PATCH 0188/3476] node page site install form almost ready --- devshop_projects/devshop_projects.form.inc | 164 ++++++++++++++------- devshop_projects/devshop_projects.node.inc | 13 +- 2 files changed, 119 insertions(+), 58 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 92ef156e1..79653b8c8 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -1,9 +1,9 @@ nid) { $form['code_path']['#value'] = $form['code_path']['#default_value']; $form['code_path']['#disabled'] = TRUE; - + $form['title']['#value'] = $form['title']['#default_value']; $form['title']['#disabled'] = TRUE; @@ -111,14 +111,14 @@ function devshop_projects_validate($node, &$form) { // been initialized yet. $node = node_load($node->nid); - + // No validation if op == Delete or project_status == site_ready. The // latter case happens when the user is editing the tests to run fied. if ($node->op == t('Delete') || $node->project_status == 'sites_ready') { return; } - + // Full validation on when a creating a new node $add = (arg(1) == 'add' ? TRUE : FALSE); @@ -140,7 +140,7 @@ function devshop_projects_validate($node, &$form) { form_set_error('title', t('Project code name is already is use by another project')); } - // Make sure the path is unique. + // Make sure the path is unique. $cp = hosting_path_normalize($node->code_path); if (!$node->retry && $add && $result = db_fetch_object(db_query("SELECT code_path FROM {hosting_devshop_project} WHERE code_path = '%s' AND nid <> %d", $cp, $node->nid))) { form_set_error('code_path', t('Code path is already in use by another project')); @@ -156,12 +156,12 @@ function devshop_projects_validate($node, &$form) { * Form for site installation */ function devshop_projects_install_sites_form($form_state, $project_node) { - + $form = array(); $platforms_ready = TRUE; $sites_ready = TRUE; $sites_installing = FALSE; - + // PLATFORMS STATUS: Determine if all platforms are verified. if (isset($project_node->project_objects['platform'])){ foreach ($project_node->project_objects['platform'] as $nid => $env){ @@ -185,56 +185,39 @@ function devshop_projects_install_sites_form($form_state, $project_node) { } else { $sites_ready = FALSE; } - + // OUTPUT - if ($platforms_ready == FALSE){ - $retry = devshop_project_project_create_failed($project_node->nid, $task); - if ($retry) { - $form['note'] = array( - '#type' => 'item', - '#title' => t('NOTICE!'), - '#description' => t('Project Create failed! You can ' . l('view the task log here.', "node/$task->nid") . ' Please ' . l('edit the project settings', "node/$project_node->nid/edit") . ' and make any necessary corrections and resubmit the form to try again.'), - '#weight' => -1, - ); - } - else { - $form['note'] = array( - '#type' => 'item', - '#title' => t('Clone & Verify'), - '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), - ); - } - return $form; - } elseif ($sites_installing) { + if ($sites_installing) { $form['note'] = array( '#type' => 'item', '#title' => t('Sites Installing'), - '#value' => t('Your Live, Dev, and Test sites are being installed!'), + '#value' => t('Your sites are being installed!'), ); return $form; } elseif ($sites_ready) { return; } - + // Load all install profiles found on dev platform + /* $platform_nid = key($project_node->project_objects['platform']); - + $form['ready'] = array( '#type' => 'item', '#value' => t("Drupal Code has been verified! Now you must create your sites by choosing your installation profile!"), ); - + $profiles = array_combine((array) hosting_get_profiles($platform_nid, 'short_name'), (array) hosting_get_profiles($platform_nid)); - + // Sensible default? // Lets go with standard for now... we can update later. if (isset($profiles['standard'])) { $default_profile = 'standard'; } - // If 'drupal' profile exists, it is likely drupal6! + // If 'drupal' profile exists, it is likely drupal6! elseif (isset($profiles['drupal'])) { $default_profile = 'drupal'; - } + } $form['install_profile'] = array( '#type' => 'radios', '#title' => t('Choose your install profile'), @@ -243,11 +226,90 @@ function devshop_projects_install_sites_form($form_state, $project_node) { ); $form['nid'] = array( '#type' => 'value', - '#value' => $project_node->nid, + '#value' => $project_node->nid, + ); + */ + $project = $project_node; + $profiles = array(); + $completed = TRUE; + + // Display the platforms + $rows = array(); + $header = array(t('Name'), t('Branch'), t('Status')); + foreach ($project->project_objects['platform'] as $nid => $name){ + $platform = node_load($nid); + $row = array(); + $row[] = $name; + $row[] = $platform->git_branch; + $row[] = $platform->verified? t('Verified'): t('Verification Pending'); + $rows[] = $row; + + if (!$platform->verified){ + $completed = FALSE; + } + + // Collect install profiles + // This fancy footwork gives us an array with items like 'shortname' => 'Name' + $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); + + if (!isset($available_profiles)){ + $available_profiles = $profiles[$name]; + } else { + // @TODO: This joins profiles by name, not profile name. + $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + } + + // Build site options + $defaults = array(); + $defaults['dev']['pull'] = 1; + $defaults['dev']['dev_modules'] = 1; + $defaults['test']['run_tests'] = 1; + $defaults['live']['live_modules'] = 1; + } +dsm($profiles); + // Provide something for people while they wait. + if (!$completed){ + $form['help'] = array( + '#type' => 'markup', + '#value' => t('Please wait while we download and verify your drupal code.'), + ); + + } elseif (count($available_profiles) == 0) { + $form['error'] = array( + '#type' => 'markup', + '#value' => t('WARNING: No profile was found in all of your platforms. Please check your branches and code and try again.'), + ); + } else { + // Install Profile + // Sensible default? + // Lets go with standard for now... we can update later. + if (isset($available_profiles['standard'])) { + $default_profile = 'standard'; + } + // If 'drupal' profile exists, it is likely drupal6! + elseif (isset($available_profiles['drupal'])) { + $default_profile = 'drupal'; + } + + // @TODO: Handle no available profiles + $form['install_profile'] = array( + '#type' => 'radios', + '#options' => $available_profiles, + '#title' => t('Project Install Profile'), + '#required' => 1, + '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), + '#default_value' => $default_profile, + ); + } + + // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" + $form['platforms'] = array( + '#type' => 'markup', + '#value' => theme('table', $header, $rows), ); $form['submit'] = array( '#type' => 'submit', - '#value' => t('Create dev, test, and live sites'), + '#value' => t('Create Platforms'), ); return $form; } @@ -259,8 +321,8 @@ function devshop_projects_install_sites_form_validate(&$form, &$form_state){ if (empty($form_state['values']['install_profile'])){ form_set_error('install_profile', t('You must choose an install profile')); } - - + + } /** @@ -282,7 +344,7 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ // Save installation profile to database db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); - + // Create the site nodes foreach ($project_node->project_objects['platform'] as $nid => $env){ devshop_projects_create_site($project_node, node_load($nid), $env); @@ -296,7 +358,7 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { $project_node = node_load($project_nid); $form = array(); - + //Bail if no platforms yet. if (!isset($project_node->project_objects['platform'])){ $retry = devshop_project_project_create_failed($project_nid, $task); @@ -330,7 +392,7 @@ function devshop_projects_platform_create_form($form_state, $project_nid) { ); $form['nid'] = array( '#type' => 'value', - '#value' => $project_node->nid, + '#value' => $project_node->nid, ); $form['submit'] = array( '#type' => 'submit', @@ -347,8 +409,8 @@ function devshop_projects_platform_create_form_validate(&$form, &$form_state){ if (empty($form_state['values']['platform_name'])){ form_set_error('install_profile', t('You must include a platform name')); } - - + + } /** @@ -416,7 +478,7 @@ function devshop_projects_project_delete_form($form_state, $project_nid) { $form['cancel'] = array( '#type' => 'submit', - '#value' => t('Cancel'), + '#value' => t('Cancel'), '#weight' => 33, ); @@ -432,15 +494,15 @@ function devshop_projects_project_delete_form_submit(&$form, &$form_state){ if ($form_state['clicked_button']['#value'] == t('Delete Project')) { $nid = $form['nid']['#value']; $data = devshop_projects_project_data_get($nid); - + // First get a list of all of the objects for this project $query = db_query("SELECT * " . "FROM {hosting_devshop_project_object} " . "WHERE project_nid = %d", $nid); - + $snid = array(); $pnid = array(); - + while ($obj = db_fetch_object($query)) { if ($obj->object_type == 'site') { $snid[] = $obj->object_nid; @@ -449,7 +511,7 @@ function devshop_projects_project_delete_form_submit(&$form, &$form_state){ $pnid[] = $obj->object_nid; } } - + if (count($snid) > 0) { $data['project_delete_site_disable'] = $snid; $data['project_delete_site_delete'] = $snid; @@ -458,14 +520,14 @@ function devshop_projects_project_delete_form_submit(&$form, &$form_state){ unset($data['project_delete_site_disable']); unset($data['project_delete_site_delete']); } - + if (count($pnid) > 0) { $data['project_delete_platform_delete'] = $pnid; } else { unset($data['project_delete_platform_delete']); } - + if ($data['project_delete_platform_delete'] || $data['project_delete_site_disable'] || $data['project_delete_site_delete'] ) { diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 2460bd824..5a53399a5 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -219,8 +219,12 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // If not ready yet: if (!$node->status){ - // Go back to the wizard. - //drupal_goto('node/add/project'); + // Site install form + $node->content['devshop']['install'] = array( + '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), + '#type' => 'markup', + ); + return $node; } @@ -337,11 +341,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#type' => 'fieldset', '#weight' => 100, ); - // Site install form - $node->content['devshop']['install'] = array( - '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), - '#type' => 'markup', - ); return $node; } From 892e1fb43e8eef008e04f77fcf51fe63709eeea2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 00:07:57 -0500 Subject: [PATCH 0189/3476] removing commented out code --- devshop_projects/devshop_projects.form.inc | 43 ++------ devshop_projects/devshop_projects.wizard.inc | 109 +------------------ 2 files changed, 10 insertions(+), 142 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 79653b8c8..c63d10a24 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -198,45 +198,23 @@ function devshop_projects_install_sites_form($form_state, $project_node) { return; } - // Load all install profiles found on dev platform - /* - $platform_nid = key($project_node->project_objects['platform']); - - $form['ready'] = array( - '#type' => 'item', - '#value' => t("Drupal Code has been verified! Now you must create your sites by choosing your installation profile!"), - ); - - $profiles = array_combine((array) hosting_get_profiles($platform_nid, 'short_name'), (array) hosting_get_profiles($platform_nid)); + $profiles = array(); + $completed = TRUE; - // Sensible default? - // Lets go with standard for now... we can update later. - if (isset($profiles['standard'])) { - $default_profile = 'standard'; - } - // If 'drupal' profile exists, it is likely drupal6! - elseif (isset($profiles['drupal'])) { - $default_profile = 'drupal'; - } - $form['install_profile'] = array( - '#type' => 'radios', - '#title' => t('Choose your install profile'), - '#options' => $profiles, - '#default_value' => $default_profile, - ); $form['nid'] = array( '#type' => 'value', '#value' => $project_node->nid, ); - */ - $project = $project_node; - $profiles = array(); - $completed = TRUE; // Display the platforms $rows = array(); $header = array(t('Name'), t('Branch'), t('Status')); - foreach ($project->project_objects['platform'] as $nid => $name){ + foreach ($project_node->project_objects['platform'] as $nid => $name){ + + //@TODO: Something is saving records in devshop_project_objects with object_nid = 0. + if (!$nid){ + continue; + } $platform = node_load($nid); $row = array(); $row[] = $name; @@ -309,7 +287,7 @@ dsm($profiles); ); $form['submit'] = array( '#type' => 'submit', - '#value' => t('Create Platforms'), + '#value' => t('Create Sites'), ); return $form; } @@ -321,8 +299,6 @@ function devshop_projects_install_sites_form_validate(&$form, &$form_state){ if (empty($form_state['values']['install_profile'])){ form_set_error('install_profile', t('You must choose an install profile')); } - - } /** @@ -339,7 +315,6 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ global $user; $project_node = node_load($form_state['values']['nid']); - $project_node->install_profile = $form_state['values']['install_profile']; // Save installation profile to database diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 72b123d41..882a56c7e 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -73,7 +73,6 @@ function devshop_projects_create_wizard_info(){ 'git_url' => t('Step 1: Git'), 'settings' => t('Step 2: Settings'), 'environments' => t('Step 3: Platforms'), - 'install' => t('Step 4: Install Drupal'), ), 'forms' => array( 'git_url' => array( @@ -85,9 +84,6 @@ function devshop_projects_create_wizard_info(){ 'environments' => array( 'form id' => 'devshop_project_create_step_environments' ), - 'install' => array( - 'form id' => 'devshop_project_create_step_install' - ), ), ); } @@ -144,7 +140,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { node_save($node); ctools_object_cache_clear('project', $form_state['cache name']); - $form_state['redirect'] = 'hosting/projects'; + $form_state['redirect'] = 'node/' . $node->nid; } @@ -452,106 +448,3 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } } } - - -/********** - * STEP 4 - * Project Environments - *********/ - -/** - * STEP 4: Form - * @TODO: MOVE TO PROJECT NODE FORM! - */ -function devshop_project_create_step_install(&$form, &$form_state) { - $project = &$form_state['project']; - $completed = TRUE; -/* - $profiles = array(); - - // Display the platforms - $rows = array(); - $header = array(t('Name'), t('Branch'), t('Status')); - foreach ($project->platforms as $name => $nid){ - $platform = node_load($nid); - $row = array(); - $row[] = $name; - $row[] = $platform->git_branch; - $row[] = $platform->verified? t('Verified'): t('Verification Pending'); - $rows[] = $row; - - if (!$platform->verified){ - $completed = FALSE; - } - - // Collect install profiles - $profiles[$name] = hosting_get_profiles($platform->nid); - if (!isset($available_profiles)){ - $available_profiles = $profiles[$name]; - } else { - $available_profiles = array_intersect_key($profiles[$name], $available_profiles); - } - - // Build site options - $defaults = array(); - $defaults['dev']['pull'] = 1; - $defaults['dev']['dev_modules'] = 1; - $defaults['test']['run_tests'] = 1; - $defaults['live']['live_modules'] = 1; - - // @TODO: Would be fun to use hooks to do this stuff... - $form['site_options'][$name] = array( - '#type' => 'fieldset', - '#title' => $name, - ); - $form['site_options'][$name]['branch'] = array( - '#type' => 'item', - '#title' => t('Branch'), - '#description' => t('This can be changed later.'), - '#value' => $platform->git_branch, - ); - } - - // Provide something for people while they wait. - if (!$completed){ - $form['help'] = array( - '#type' => 'markup', - '#value' => t('Please wait while we download and verify your drupal code.'), - ); - - } else { - // Install Profile - // @TODO: Handle no available profiles - $form['install_profile'] = array( - '#type' => 'radios', - '#options' => $available_profiles, - '#title' => t('Project Install Profile'), - '#required' => 1, - '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches.'), - ); - } - - // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" - $form['platforms'] = array( - '#type' => 'markup', - '#value' => theme('table', $header, $rows), - ); - */ -} - -/** - * STEP 4: VALIDATE - */ -function devshop_project_create_step_install_validate(&$form, &$form_state) { - -} - -/** - * STEP 4: SUBMIT - */ -function devshop_project_create_step_install_submit(&$form, &$form_state) { - - // @TODO: Save install profile, base_url, etc to project node. - // @TODO: Save site nodes to the chosen platforms - -} From fedffac1af375e147778903f6f491683e497368d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 00:09:46 -0500 Subject: [PATCH 0190/3476] removing dsm --- devshop_projects/devshop_projects.form.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index c63d10a24..0321d0857 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -244,7 +244,7 @@ function devshop_projects_install_sites_form($form_state, $project_node) { $defaults['test']['run_tests'] = 1; $defaults['live']['live_modules'] = 1; } -dsm($profiles); + // Provide something for people while they wait. if (!$completed){ $form['help'] = array( From 56d848f3c6970899c9e97ddc55b7235afb2b34c5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 00:28:02 -0500 Subject: [PATCH 0191/3476] mostly whitespace --- devshop_projects/devshop_projects.form.inc | 4 ---- devshop_projects/devshop_projects.node.inc | 9 ++++----- devshop_projects/devshop_projects.wizard.inc | 4 +--- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/devshop_projects.form.inc index 0321d0857..fdbfb1c84 100644 --- a/devshop_projects/devshop_projects.form.inc +++ b/devshop_projects/devshop_projects.form.inc @@ -211,10 +211,6 @@ function devshop_projects_install_sites_form($form_state, $project_node) { $header = array(t('Name'), t('Branch'), t('Status')); foreach ($project_node->project_objects['platform'] as $nid => $name){ - //@TODO: Something is saving records in devshop_project_objects with object_nid = 0. - if (!$nid){ - continue; - } $platform = node_load($nid); $row = array(); $row[] = $name; diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 5a53399a5..5a7ce9ed9 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -71,12 +71,11 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On insert or update, insert records saving this objects project and environment if ($op = 'update' || $op = 'insert') { if (!empty($node->project) && !empty($node->environment)) { - // Get the project node by name. - $project = hosting_context_load($node->project); + // Get the project node by name. + $project = hosting_context_load($node->project); - // Save to table - // @TODO: start saving branch here too - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); + // Save to table + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); } } } diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/devshop_projects.wizard.inc index 882a56c7e..f2310872c 100755 --- a/devshop_projects/devshop_projects.wizard.inc +++ b/devshop_projects/devshop_projects.wizard.inc @@ -47,8 +47,6 @@ function devshop_projects_create_wizard($step = NULL){ } } - dsm($project, 'project'); - // Generate our ctools form and output $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; @@ -409,7 +407,6 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // Filter out branch platforms forms_api so we can keep the submit clean. form_set_value($form['branch_platforms'], array_filter($values['branch_platforms']), $form_state); - } /** @@ -426,6 +423,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Create these platforms $all_platforms = array_merge($project->default_platforms, $project->branch_platforms); + foreach ($all_platforms as $platform_name => $branch) { if (empty($project->platforms[$platform_name])){ From 02b2ca9c9a0dab3e651c4c159191ce2a1ddec651 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 11:20:14 -0500 Subject: [PATCH 0192/3476] Cleanup --- devshop_projects/devshop_projects.module | 300 +++++++---------------- 1 file changed, 87 insertions(+), 213 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index fc4ed4dc7..a92d70428 100755 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -2,8 +2,8 @@ /** * @file devshop_projects.module - * a module in the DevShop module group which enables the user to create - * proects and group sites/platforms into project groups. + * + * Provides Node Type, UI, and tools for DevShop Projects. */ include_once('devshop_projects.form.inc'); @@ -12,6 +12,9 @@ include_once('devshop_projects.task.inc'); /** * Implementation of hook_perm() + * + * Since we have a node type, "create project content permission is + * automatically added by Drupal */ function devshop_projects_perm() { return array( @@ -33,92 +36,37 @@ function devshop_projects_menu() { 'menu_name' => 'primary-links', 'weight' => 1, ); - - $items['hosting/projects/platform/create/%'] = array( - 'title' => 'Create and add a platform', - 'description' => 'Create and add a platform to an existing project', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('devshop_projects_platform_create_form', 4), - 'access arguments' => array('view projects'), - ); - - $items['hosting/projects/delete/%'] = array( - 'title' => t('Delete Project'), - 'description' => 'Delete a project and all of it\'s associated sites and platforms', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('devshop_projects_project_delete_form', 3), - 'access arguments' => array('delete project'), - ); - + // + //$items['hosting/projects/platform/create/%'] = array( + // 'title' => 'Create and add a platform', + // 'description' => 'Create and add a platform to an existing project', + // 'page callback' => 'drupal_get_form', + // 'page arguments' => array('devshop_projects_platform_create_form', 4), + // 'access arguments' => array('view projects'), + //); + // + //$items['hosting/projects/delete/%'] = array( + // 'title' => t('Delete Project'), + // 'description' => 'Delete a project and all of it\'s associated sites and platforms', + // 'page callback' => 'drupal_get_form', + // 'page arguments' => array('devshop_projects_project_delete_form', 3), + // 'access arguments' => array('delete project'), + //); return ($items); } /** * Implementation of hook_menu_alter() + * + * Replaces node/add/project with a ctools wizard. */ function devshop_projects_menu_alter(&$items) { - //$items['hosting/tasks/%node/list']['page callback'] = 'devshop_projects_hosting_task_ajax_list'; - //$items['hosting/js']['page callback'] = 'devshop_projects_hosting_js_page'; - - $items['node/add/project']['page callback'] = 'devshop_projects_create_wizard'; $items['node/add/project']['page arguments'] = array(3); $items['node/add/project']['file'] = 'devshop_projects.wizard.inc'; $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects'); } -/** - * Replacement menu callback for hosting_task_ajax_list - */ -function devshop_projects_hosting_task_ajax_list($node) { - if ($node->type == 'project') { - module_load_include('tasks.inc', 'devshop_projects'); - $return['markup'] = devshop_projects_hosting_task_table($node); - $return['changed'] = $node->changed; - drupal_json($return); - exit(); - } else { - hosting_task_ajax_list($node); - } -} - -/** - * Replacement menu callback for hosting_js_page(). - * - * This is used for Project Tasks. Since the tasks are really tasks on the site nodes, - * we have to modify the JS call to modify what node gets passed in. - */ -function devshop_projects_hosting_js_page(){ - $args = func_get_args(); - $nid = &$args[1]; - $task = &$args[2]; - - // Get task type from $task argument... For whatver reason, they don't make it an argument - list($aegir_context, $task_type) = explode('_', $task); - - // If this is coming from a project page: - if ($aegir_context == 'project'){ - // Change the task object from project to site - $task = str_replace('project', 'site', $task); - - // Load the project - $project = node_load($nid); - $sites = array_flip($project->project_objects['site']); - - // Change the NID based on the task type - if ($task_type == 'devshop-commit') { - $nid = $sites['dev']; - } elseif ($task_type == 'devshop-sync') { - $nid = $sites['test']; - } elseif ($task_type == 'devshop-pull') { - $nid = $sites['live']; - } - } - - $output .= call_user_func_array('hosting_js_page', $args); - return $output; -} - /** * Status display * @@ -142,150 +90,76 @@ function devshop_projects_project_status(){ /** * Implements hook_hosting_drush_aliases_name() + * + * See http://drupal.org/project/hosting_drush_aliases */ function devshop_projects_hosting_drush_aliases_name($node) { if (isset($node->project_name)){ return $node->project_name .".". $node->project_environment; } } +// +///* +// * Helper function to create a site in a project +// */ +//function devshop_projects_create_site($project_node, $platform_node, $env) { +// +// global $user; +// +// // Create the site nodes +// $node = new stdClass(); +// $node->type = 'site'; +// $node->status = 1; +// $node->uid = $user->uid; +// $node->title = $env .'.'. $project_node->base_url; +// +// // Aegir properties +// // @TODO: better client support +// $node->client = HOSTING_DEFAULT_CLIENT; +// $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); +// +// $node->platform = $platform_node->nid; +// $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); +// +// //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); +// +// // @TODO: Improve site language handling? +// $node->site_language = !empty($user->language)? $user->language: 'en'; +// +// // Save the node +// if ($node = node_submit($node)) { +// node_save($node); +// +// //And save the association to the project +// db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); +// } +//} + +///** +// * Helper function which writes a serialize array in the project file +// */ +//function devshop_projects_project_data_set($nid, $data) { +// db_query("UPDATE {hosting_devshop_project} SET data = '%s' WHERE nid = %d", +// serialize($data), $nid); +//} +// +///** +// * Helper function which reads the serialize array in the project file +// */ +//function devshop_projects_project_data_get($nid) { +// +// $sdata = db_result(db_query("SELECT data FROM {hosting_devshop_project} " . +// "WHERE nid = %d", $nid)); +// +// if (!$sdata || strlen($sdata) < 1) { +// $data = array(); +// } +// else { +// $data = unserialize($sdata); +// } +// +// return $data; +//} -/* - * Helper function to create a site in a project - */ -function devshop_projects_create_site($project_node, $platform_node, $env) { - - global $user; - - // Create the site nodes - $node = new stdClass(); - $node->type = 'site'; - $node->status = 1; - $node->uid = $user->uid; - $node->title = $env .'.'. $project_node->base_url; - - // Aegir properties - // @TODO: better client support - $node->client = HOSTING_DEFAULT_CLIENT; - $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); - - $node->platform = $platform_node->nid; - $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); - - //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); - - // @TODO: Improve site language handling? - $node->site_language = !empty($user->language)? $user->language: 'en'; - - // Save the node - if ($node = node_submit($node)) { - node_save($node); - - //And save the association to the project - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); - } -} - -/** - * Helper function which writes a serialize array in the project file - */ -function devshop_projects_project_data_set($nid, $data) { - db_query("UPDATE {hosting_devshop_project} SET data = '%s' WHERE nid = %d", - serialize($data), $nid); -} -/** - * Helper function which reads the serialize array in the project file - */ -function devshop_projects_project_data_get($nid) { - - $sdata = db_result(db_query("SELECT data FROM {hosting_devshop_project} " . - "WHERE nid = %d", $nid)); - if (!$sdata || strlen($sdata) < 1) { - $data = array(); - } - else { - $data = unserialize($sdata); - } - - return $data; -} - - -/** - * A replacement for hosting_task_table, to allow us to add - * tasks from other nodes. - */ -function devshop_projects_hosting_task_table($node) { - $output = ''; - - $headers[] = t('Task'); - $headers[] = array( - 'data' => t('Actions'), - 'class' => 'hosting-actions', - ); - - $tasklist = hosting_task_fetch_tasks($node->nid); - if ($node->project_status != 'sites_ready'){ - return; - } - - // Get tasklists for all sites - $tasks = array(); - foreach ($node->project_objects['site'] as $nid => $env){ - $site_nodes[$env] = node_load($nid); - $tasks[$env] = hosting_task_fetch_tasks($nid); - } - - // Add our own specific tasks - $tasklist['devshop-commit'] = $tasks['dev']['devshop-commit']; - $tasklist['devshop-sync'] = $tasks['test']['devshop-sync']; - $tasklist['devshop-pull'] = $tasks['live']['devshop-pull']; - - // Enhance titles - $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, 'http://'. $site_nodes['dev']->title, array('attributes' => array('target' => '_blank'))); - $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); - $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); - - // Override some - unset($tasklist['devshop-create']['task_permitted']); - unset($tasklist['devshop-create']['nid']); - - unset($tasklist['devshop-platform-create']['task_permitted']); - unset($tasklist['devshop-platform-create']['nid']); - - foreach ($tasklist as $task => $info) { - $row = array(); - - if (!isset($info['nid']) && !$info['task_permitted']) { - // just don't show those tasks, since we'll not be able to run them - continue; - } - - $row['type'] = array( - 'data' => $info['title'], - 'class' => 'hosting-status', - ); - $actions = array(); - - if (isset($info['task_status']) && ($info['task_status'] == 0)) { - $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']); - } - else { - $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); - } - - $actions['log'] = _hosting_task_button(t('Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); - $row['actions'] = array( - 'data' => implode('', $actions), - 'class' => 'hosting-actions', - ); - - $rows[] = array( - 'data' => $row, - 'class' => $info['class'], - ); - } - $output .= theme('table', $headers, $rows, array('class' => 'hosting-table')); - return $output; -} From 4edfd66b14244bb32a03a8e826852824fe07ede2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 11:20:25 -0500 Subject: [PATCH 0193/3476] adding the hostmaster hacks to its own file --- .../inc/devshop_projects.hack.inc | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 devshop_projects/inc/devshop_projects.hack.inc diff --git a/devshop_projects/inc/devshop_projects.hack.inc b/devshop_projects/inc/devshop_projects.hack.inc new file mode 100644 index 000000000..38ce76840 --- /dev/null +++ b/devshop_projects/inc/devshop_projects.hack.inc @@ -0,0 +1,129 @@ +type == 'project') { + module_load_include('tasks.inc', 'devshop_projects'); + $return['markup'] = devshop_projects_hosting_task_table($node); + $return['changed'] = $node->changed; + drupal_json($return); + exit(); + } else { + hosting_task_ajax_list($node); + } +} + +/** + * Replacement menu callback for hosting_js_page(). + * + * This is used for Project Tasks. Since the tasks are really tasks on the site nodes, + * we have to modify the JS call to modify what node gets passed in. + */ +function devshop_projects_hosting_js_page(){ + $args = func_get_args(); + $nid = &$args[1]; + $task = &$args[2]; + + // Get task type from $task argument... For whatver reason, they don't make it an argument + list($aegir_context, $task_type) = explode('_', $task); + + // If this is coming from a project page: + if ($aegir_context == 'project'){ + // Change the task object from project to site + $task = str_replace('project', 'site', $task); + + // Load the project + $project = node_load($nid); + $sites = array_flip($project->project_objects['site']); + + // Change the NID based on the task type + if ($task_type == 'devshop-commit') { + $nid = $sites['dev']; + } elseif ($task_type == 'devshop-sync') { + $nid = $sites['test']; + } elseif ($task_type == 'devshop-pull') { + $nid = $sites['live']; + } + } + + $output .= call_user_func_array('hosting_js_page', $args); + return $output; +} +/** + * A replacement for hosting_task_table, to allow us to add + * tasks from other nodes. + */ +function devshop_projects_hosting_task_table($node) { + $output = ''; + + $headers[] = t('Task'); + $headers[] = array( + 'data' => t('Actions'), + 'class' => 'hosting-actions', + ); + + $tasklist = hosting_task_fetch_tasks($node->nid); + if ($node->project_status != 'sites_ready'){ + return; + } + + // Get tasklists for all sites + $tasks = array(); + foreach ($node->project_objects['site'] as $nid => $env){ + $site_nodes[$env] = node_load($nid); + $tasks[$env] = hosting_task_fetch_tasks($nid); + } + + // Add our own specific tasks + $tasklist['devshop-commit'] = $tasks['dev']['devshop-commit']; + $tasklist['devshop-sync'] = $tasks['test']['devshop-sync']; + $tasklist['devshop-pull'] = $tasks['live']['devshop-pull']; + + // Enhance titles + $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, 'http://'. $site_nodes['dev']->title, array('attributes' => array('target' => '_blank'))); + $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); + $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); + + // Override some + unset($tasklist['devshop-create']['task_permitted']); + unset($tasklist['devshop-create']['nid']); + + unset($tasklist['devshop-platform-create']['task_permitted']); + unset($tasklist['devshop-platform-create']['nid']); + + foreach ($tasklist as $task => $info) { + $row = array(); + + if (!isset($info['nid']) && !$info['task_permitted']) { + // just don't show those tasks, since we'll not be able to run them + continue; + } + + $row['type'] = array( + 'data' => $info['title'], + 'class' => 'hosting-status', + ); + $actions = array(); + + if (isset($info['task_status']) && ($info['task_status'] == 0)) { + $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']); + } + else { + $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); + } + + $actions['log'] = _hosting_task_button(t('Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); + $row['actions'] = array( + 'data' => implode('', $actions), + 'class' => 'hosting-actions', + ); + + $rows[] = array( + 'data' => $row, + 'class' => $info['class'], + ); + } + $output .= theme('table', $headers, $rows, array('class' => 'hosting-table')); + return $output; +} \ No newline at end of file From 8d3a40b9d7e7d28d9bcb7d1677ff69d0e8ed1f4a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 11:29:42 -0500 Subject: [PATCH 0194/3476] Moving frontend code to ui --- devshop_projects/devshop_projects.module | 3 + devshop_projects/devshop_projects.node.inc | 188 +++---------------- devshop_projects/inc/devshop_projects.ui.inc | 166 ++++++++++++++++ 3 files changed, 191 insertions(+), 166 deletions(-) create mode 100644 devshop_projects/inc/devshop_projects.ui.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a92d70428..03f7c677c 100755 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -10,6 +10,9 @@ include_once('devshop_projects.form.inc'); include_once('devshop_projects.node.inc'); include_once('devshop_projects.task.inc'); +// Lets keep front end code here. +include_once('inc/devshop_projects.ui.inc'); + /** * Implementation of hook_perm() * diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/devshop_projects.node.inc index 5a7ce9ed9..f6eeadd29 100644 --- a/devshop_projects/devshop_projects.node.inc +++ b/devshop_projects/devshop_projects.node.inc @@ -1,14 +1,15 @@ 'devshop_project', "name" => 'DevShop Project', @@ -19,16 +20,14 @@ function devshop_projects_node_info() { "has_body" => 0, "body_label" => '', "min_word_count" => 0 - ); - + ); return $types; } - /** - * Implements hook_nodeapi() + * Implementation of hook_nodeapi() * - * For Platforms and Sites: + * Handle project information for Platforms and Sites: * $node->project : The context name of the project. * $node->environment: The type of environment this platform or site is: * can be dev, test, or live. @@ -82,7 +81,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { } /** - * Helper to load a project node by path + * Helper to load a project node by code path. */ function devshop_project_load_by_path($project_path){ $nid = db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s"', array($project_path))); @@ -91,6 +90,12 @@ function devshop_project_load_by_path($project_path){ /** * Implementation of hook_insert(). + * + * 1. Saves data into our table. + * 2. Saves a hosting context name. + * 3. Adds a "Verify" task for this project. + * + * @see hosting_platform_insert() */ function devshop_projects_insert($node) { if (!isset($node->no_verify)) { @@ -112,6 +117,10 @@ function devshop_projects_insert($node) { /** * Implementation of hook_update(). + * + * 1. Updates our table. + * 2. Adds a "Verify" task for this project. + * */ function devshop_projects_update($node) { @@ -124,6 +133,10 @@ function devshop_projects_update($node) { "WHERE nid = %d", $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, serialize($data), $node->nid); + + // Create hostmaster task + hosting_add_task($node->nid, 'verify'); + // //// If this is a rerry, kick start another devshop-create //if (isset($node->retry)) { @@ -211,160 +224,3 @@ function devshop_projects_load($node) { return $additions; } -/** - * Implementation of hook_view(). - */ -function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { - - // If not ready yet: - if (!$node->status){ - // Site install form - $node->content['devshop']['install'] = array( - '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), - '#type' => 'markup', - ); - return $node; - } - - - modalframe_parent_js(); - - $node->content['info'] = array( - '#prefix' => '
', - '#suffix' => '
' - ); - - $node->content['info']['git_url'] = array( - '#type' => 'item', - '#title' => t('Git URL'), - '#value' => $node->git_url, - '#weight' => -10 - ); - - $node->content['info']['code_path'] = array( - '#type' => 'item', - '#title' => t('Code path'), - '#value' => filter_xss($node->code_path), - '#weight' => -8 - ); - $url = 'http://' . $node->base_url; - $node->content['info']['base_url'] = array( - '#type' => 'item', - '#title' => t('Base URL'), - '#value' => l($url, $url), - '#weight' => -10 - ); - - if (!empty($node->install_profile)){ - $node->content['info']['install_profile'] = array( - '#type' => 'item', - '#title' => t('Install profile'), - '#value' => ($node->install_profile), - '#weight' => -8 - ); - } - - $rows = array(); - if (isset($node->project_objects['site'])) { - foreach($node->project_objects['site'] as $nid => $env) { - $site = node_load($nid); - $platform = node_load($site->platform); - if ($site->site_status == -2 && $platform->platform_status == -2) { - continue; - } - $row = array(); - if ($site->site_status != -2) { - $url = "http://$site->title"; - $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); - $row[] = l('Site', "node/$site->nid"); - } - else { - // Site if deleted, just show the platform name - $row[] = $platform->title; - $row[] = ''; - } - $row[] = l('Platform', "node/$site->platform"); - if ($site->site_status != -2) { - $row[] = l('Download Database', "$url/admin/status/"); - } - else { - $row[] = ''; - } - $row[] = l('Commit Log', "node/$site->platform/gitlog"); - $rows[] = $row; - } - $header = array(); - $table = theme('table', $header, $rows); - - $node->content['sites'] = array( - '#type' => 'fieldset', - '#title' => t('Project Environments'), - '#weight' => 12, - ); - $node->content['sites']['table'] = array( - '#type' => 'item', - '#value' => $table, - ); - } - if ($node->project_status == 'sites_ready'){ - $node->content['sites']['add_platform'] = array( - '#type' => 'item', - '#value' => l(t('Create and add additional platforms'), - "hosting/projects/platform/create/$node->nid"), - ); - } - - $node->content['sites']['delete_project'] = array( - '#type' => 'item', - '#value' => l("Delete this project", "hosting/projects/delete/$node->nid") - ); - - //Tasks - $node->content['tasks_view'] = array( - '#type' => 'item', - //'#value' => devshop_projects_hosting_task_table($node), - '#value' => hosting_task_table($node), - '#prefix' => '
', - '#suffix' => '
', - '#weight' => 10 - ); - $settings['hostingTaskRefresh'] = array( - 'nid' => $node->nid, - 'changed' => $node->changed, - ); - drupal_add_js($settings, 'setting'); - drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); - - // MAIN DISPLAY - $node->content['devshop'] = array( - '#type' => 'fieldset', - '#weight' => 100, - ); - return $node; -} - -/* - * Callback that is invoked when the user points the brower to - * hosting/projects. It displays a nice tabulated list of projects - * and cool thing you can do with them. - */ -function devshop_projects_projects_view() { - - $header = array('Projects', 'Platforms', 'Sites'); - - $r = db_query("SELECT * FROM {hosting_devshop_project}"); - $rows = array(); - - while(($proj = db_fetch_object($r))) { - $node = node_load($proj->nid); - $row = array(); - $row[] = l($node->title, "node/$proj->nid"); - $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='platform'", $node->nid)); - $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); - $rows[] = $row; - } - - $output = theme('table', $header, $rows, array('class' => 'hosting-table')); - $output .= l(t('Create a new project?'), 'node/add/project'); - return $output; -} diff --git a/devshop_projects/inc/devshop_projects.ui.inc b/devshop_projects/inc/devshop_projects.ui.inc new file mode 100644 index 000000000..a7576a281 --- /dev/null +++ b/devshop_projects/inc/devshop_projects.ui.inc @@ -0,0 +1,166 @@ +nid); + $row = array(); + $row[] = l($node->title, "node/$proj->nid"); + $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='platform'", $node->nid)); + $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); + $rows[] = $row; + } + + $output = theme('table', $header, $rows, array('class' => 'hosting-table')); + $output .= l(t('Create a new project?'), 'node/add/project'); + return $output; +} + +/** + * Implementation of hook_view(). + * + * Project Page Display + */ +function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { + + // If not ready yet: + if (!$node->status){ + // Site install form + $node->content['devshop']['install'] = array( + '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), + '#type' => 'markup', + ); + return $node; + } + + + modalframe_parent_js(); + + $node->content['info'] = array( + '#prefix' => '
', + '#suffix' => '
' + ); + + $node->content['info']['git_url'] = array( + '#type' => 'item', + '#title' => t('Git URL'), + '#value' => $node->git_url, + '#weight' => -10 + ); + + $node->content['info']['code_path'] = array( + '#type' => 'item', + '#title' => t('Code path'), + '#value' => filter_xss($node->code_path), + '#weight' => -8 + ); + $url = 'http://' . $node->base_url; + $node->content['info']['base_url'] = array( + '#type' => 'item', + '#title' => t('Base URL'), + '#value' => l($url, $url), + '#weight' => -10 + ); + + if (!empty($node->install_profile)){ + $node->content['info']['install_profile'] = array( + '#type' => 'item', + '#title' => t('Install profile'), + '#value' => ($node->install_profile), + '#weight' => -8 + ); + } + + $rows = array(); + if (isset($node->project_objects['site'])) { + foreach($node->project_objects['site'] as $nid => $env) { + $site = node_load($nid); + $platform = node_load($site->platform); + if ($site->site_status == -2 && $platform->platform_status == -2) { + continue; + } + $row = array(); + if ($site->site_status != -2) { + $url = "http://$site->title"; + $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); + $row[] = l('Site', "node/$site->nid"); + } + else { + // Site if deleted, just show the platform name + $row[] = $platform->title; + $row[] = ''; + } + $row[] = l('Platform', "node/$site->platform"); + if ($site->site_status != -2) { + $row[] = l('Download Database', "$url/admin/status/"); + } + else { + $row[] = ''; + } + $row[] = l('Commit Log', "node/$site->platform/gitlog"); + $rows[] = $row; + } + $header = array(); + $table = theme('table', $header, $rows); + + $node->content['sites'] = array( + '#type' => 'fieldset', + '#title' => t('Project Environments'), + '#weight' => 12, + ); + $node->content['sites']['table'] = array( + '#type' => 'item', + '#value' => $table, + ); + } + if ($node->project_status == 'sites_ready'){ + $node->content['sites']['add_platform'] = array( + '#type' => 'item', + '#value' => l(t('Create and add additional platforms'), + "hosting/projects/platform/create/$node->nid"), + ); + } + + $node->content['sites']['delete_project'] = array( + '#type' => 'item', + '#value' => l("Delete this project", "hosting/projects/delete/$node->nid") + ); + + //Tasks + $node->content['tasks_view'] = array( + '#type' => 'item', + //'#value' => devshop_projects_hosting_task_table($node), + '#value' => hosting_task_table($node), + '#prefix' => '
', + '#suffix' => '
', + '#weight' => 10 + ); + $settings['hostingTaskRefresh'] = array( + 'nid' => $node->nid, + 'changed' => $node->changed, + ); + drupal_add_js($settings, 'setting'); + drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); + + // MAIN DISPLAY + $node->content['devshop'] = array( + '#type' => 'fieldset', + '#weight' => 100, + ); + return $node; +} From 80491ee36cfa94d07f6a63a759ca119096012fee Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 11:33:37 -0500 Subject: [PATCH 0195/3476] moving include files --- devshop_projects/devshop_projects.module | 12 ++++++------ .../create-wizard.inc} | 0 .../{devshop_projects.form.inc => inc/forms.inc} | 0 .../{devshop_projects.node.inc => inc/nodes.inc} | 0 .../{devshop_projects.task.inc => inc/tasks.inc} | 0 5 files changed, 6 insertions(+), 6 deletions(-) rename devshop_projects/{devshop_projects.wizard.inc => inc/create-wizard.inc} (100%) rename devshop_projects/{devshop_projects.form.inc => inc/forms.inc} (100%) rename devshop_projects/{devshop_projects.node.inc => inc/nodes.inc} (100%) rename devshop_projects/{devshop_projects.task.inc => inc/tasks.inc} (100%) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 03f7c677c..75f2fac89 100755 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -6,12 +6,12 @@ * Provides Node Type, UI, and tools for DevShop Projects. */ -include_once('devshop_projects.form.inc'); -include_once('devshop_projects.node.inc'); -include_once('devshop_projects.task.inc'); +include_once('inc/forms.inc'); +include_once('inc/nodes.inc'); +include_once('inc/tasks.inc'); // Lets keep front end code here. -include_once('inc/devshop_projects.ui.inc'); +include_once('inc/ui.inc'); /** * Implementation of hook_perm() @@ -66,8 +66,8 @@ function devshop_projects_menu() { function devshop_projects_menu_alter(&$items) { $items['node/add/project']['page callback'] = 'devshop_projects_create_wizard'; $items['node/add/project']['page arguments'] = array(3); - $items['node/add/project']['file'] = 'devshop_projects.wizard.inc'; - $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects'); + $items['node/add/project']['file'] = 'create-wizard.inc'; + $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects') . '/inc'; } /** diff --git a/devshop_projects/devshop_projects.wizard.inc b/devshop_projects/inc/create-wizard.inc similarity index 100% rename from devshop_projects/devshop_projects.wizard.inc rename to devshop_projects/inc/create-wizard.inc diff --git a/devshop_projects/devshop_projects.form.inc b/devshop_projects/inc/forms.inc similarity index 100% rename from devshop_projects/devshop_projects.form.inc rename to devshop_projects/inc/forms.inc diff --git a/devshop_projects/devshop_projects.node.inc b/devshop_projects/inc/nodes.inc similarity index 100% rename from devshop_projects/devshop_projects.node.inc rename to devshop_projects/inc/nodes.inc diff --git a/devshop_projects/devshop_projects.task.inc b/devshop_projects/inc/tasks.inc similarity index 100% rename from devshop_projects/devshop_projects.task.inc rename to devshop_projects/inc/tasks.inc From 1c4d2ee73c1d893c3583df7e4df24aed743e0d37 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 11:51:19 -0500 Subject: [PATCH 0196/3476] chmod change --- devshop_projects/devshop_projects.info | 0 devshop_projects/devshop_projects.module | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 devshop_projects/devshop_projects.info mode change 100755 => 100644 devshop_projects/devshop_projects.module diff --git a/devshop_projects/devshop_projects.info b/devshop_projects/devshop_projects.info old mode 100755 new mode 100644 diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module old mode 100755 new mode 100644 From e8b71f298f7fdb544c3439f299d91e910ad51f07 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 12:01:07 -0500 Subject: [PATCH 0197/3476] cleaning up comments --- devshop_log/devshop_log.info | 2 ++ devshop_log/devshop_log.module | 19 +++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/devshop_log/devshop_log.info b/devshop_log/devshop_log.info index cd11803ee..b60a6efdf 100644 --- a/devshop_log/devshop_log.info +++ b/devshop_log/devshop_log.info @@ -2,3 +2,5 @@ name = DevShop Log description = A DevShop module to display the git commit log for each platform. core = 6.x package = DevShop + +dependencies[] = devshop_projects \ No newline at end of file diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index ea1d43349..b8ce5594e 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -1,8 +1,8 @@ Date: Sat, 8 Dec 2012 12:01:51 -0500 Subject: [PATCH 0198/3476] renaming --- devshop_projects/inc/{devshop_projects.hack.inc => hack.inc} | 0 devshop_projects/inc/{devshop_projects.ui.inc => ui.inc} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename devshop_projects/inc/{devshop_projects.hack.inc => hack.inc} (100%) rename devshop_projects/inc/{devshop_projects.ui.inc => ui.inc} (100%) diff --git a/devshop_projects/inc/devshop_projects.hack.inc b/devshop_projects/inc/hack.inc similarity index 100% rename from devshop_projects/inc/devshop_projects.hack.inc rename to devshop_projects/inc/hack.inc diff --git a/devshop_projects/inc/devshop_projects.ui.inc b/devshop_projects/inc/ui.inc similarity index 100% rename from devshop_projects/inc/devshop_projects.ui.inc rename to devshop_projects/inc/ui.inc From 97329151c8f51e7c566f87ca0efe80e5278a2a3e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 12:02:05 -0500 Subject: [PATCH 0199/3476] chmod --- devshop_projects/inc/create-wizard.inc | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 devshop_projects/inc/create-wizard.inc diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc old mode 100755 new mode 100644 From 694d076c2b211194b5ca3b1c24a932fead8ceadc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 12:10:22 -0500 Subject: [PATCH 0200/3476] Improving the project node page --- devshop_projects/inc/ui.inc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index a7576a281..c6aaaa65f 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -39,7 +39,8 @@ function devshop_projects_projects_view() { function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // If not ready yet: - if (!$node->status){ + + if ($node->project_status != 'sites_ready'){ // Site install form $node->content['devshop']['install'] = array( '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), @@ -48,7 +49,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { return $node; } - modalframe_parent_js(); $node->content['info'] = array( @@ -56,27 +56,29 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#suffix' => '' ); - $node->content['info']['git_url'] = array( - '#type' => 'item', - '#title' => t('Git URL'), - '#value' => $node->git_url, - '#weight' => -10 - ); - $node->content['info']['code_path'] = array( '#type' => 'item', '#title' => t('Code path'), '#value' => filter_xss($node->code_path), '#weight' => -8 ); + $url = 'http://' . $node->base_url; $node->content['info']['base_url'] = array( '#type' => 'item', '#title' => t('Base URL'), - '#value' => l($url, $url), + '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), + '#description' => t('A link to this page. @TODO!'), '#weight' => -10 ); + $node->content['info']['git_url'] = array( + '#type' => 'item', + '#title' => t('Git URL'), + '#value' => strtr("", array('!url' => $node->git_url)), + '#weight' => -10 + ); + if (!empty($node->install_profile)){ $node->content['info']['install_profile'] = array( '#type' => 'item', From 60df37b95485711e947dcdb60a55544c50ea279a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 15:00:16 -0500 Subject: [PATCH 0201/3476] trying to get our access callback to work --- devshop_projects/devshop_projects.module | 26 +++++++++++++ devshop_projects/inc/tasks.inc | 47 ++++++++++++++++++------ 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 75f2fac89..660e5e311 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -70,6 +70,32 @@ function devshop_projects_menu_alter(&$items) { $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects') . '/inc'; } + +/** + * Task access controls + * + * This function defines which tasks should be showed to the user but + * especially which will be accessible to him, in the permissions system. + * + * @arg $node object + * the node object we're trying to access + * + * @arg $task string + * the task type we're trying to do on the $node + * + * @see hosting_task_menu() + */ +function devshop_hosting_task_menu_access($node, $task) { + if (!isset($node->nid) && is_int($node)){ + $node = node_load($node); + } + if (user_access("create " . $task . " task")) { + if ($node->type == 'project') { + return TRUE; + } + } +} + /** * Status display * diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 9be88a813..84a9e5d41 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -13,27 +13,50 @@ function devshop_projects_hosting_tasks() { 'title' => t('Verify Project'), 'description' => t('Verifies access to the git repository, downloads branch and tag information.'), 'provision_save' => TRUE, + 'access callback' => 'devshop_hosting_task_menu_access', ); - $tasks['project']['devshop-create'] = array( - 'title' => t('Create Project'), - 'description' => t('Clones the repo, and creates the platforms.'), - 'provision_save' => TRUE, + $tasks['project']['create'] = array( + 'title' => t('Create New Platform'), + 'description' => t('Creates a new platform within this project.'), + 'access callback' => 'devshop_hosting_task_menu_access', + 'dialog' => TRUE, ); $tasks['project']['delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), + 'access callback' => 'devshop_hosting_task_menu_access', + 'dialog' => TRUE, ); - $tasks['project']['devshop-install'] = array( - 'title' => t('Install Sites'), - 'description' => t('Runs drupal installation on each of the three platforms.'), + return $tasks; +} + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form(). + * + * For "Commit" task. + */ +function devshop_hosting_hosting_task_create_form($node) { + + $form['message'] = array( + '#title' => t('Commit Message'), + '#type' => 'textarea', + '#description' => $descr, ); - $tasks['project']['devshop-platform-create'] = array( - 'title' => t('Create Platform'), - 'description' => t('Create a new platform to add to an existin project.'), + $form['push'] = array( + '#title' => t('Push code after commit?'), + '#type' => 'checkbox', + '#default_value' => 1, ); - - return $tasks; + $form['revert'] = array( + '#title' => t('Force revert features after commit?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + // @TODO: Provide a DIFF display to give the user an idea of what has changed. + return $form; } + + /** * Implements hook_hosting_project_context_options() * From 626e63b45aae0dc5e4b75e40087130d3316db87f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 15:47:06 -0500 Subject: [PATCH 0202/3476] trying to fix project task pages. --- devshop_projects/devshop_projects.module | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 660e5e311..17ce29d4b 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -39,22 +39,23 @@ function devshop_projects_menu() { 'menu_name' => 'primary-links', 'weight' => 1, ); - // - //$items['hosting/projects/platform/create/%'] = array( - // 'title' => 'Create and add a platform', - // 'description' => 'Create and add a platform to an existing project', - // 'page callback' => 'drupal_get_form', - // 'page arguments' => array('devshop_projects_platform_create_form', 4), - // 'access arguments' => array('view projects'), - //); - // - //$items['hosting/projects/delete/%'] = array( - // 'title' => t('Delete Project'), - // 'description' => 'Delete a project and all of it\'s associated sites and platforms', - // 'page callback' => 'drupal_get_form', - // 'page arguments' => array('devshop_projects_project_delete_form', 3), - // 'access arguments' => array('delete project'), - //); + + + // hosting tasks ajax pages. + // @TODO: This doesn't work and is making me very angry! + foreach (hosting_available_tasks('project') as $task => $info){ + $path = 'node/%devshop_project_node/project_' . $task; + $items[$path] = array( + 'title' => $info['title'], + 'description' => $info['description'], + 'page callback' => 'drupal_get_form', + 'page arguments' => array('hosting_task_confirm_form', 1, $task), + 'access callback' => 'hosting_task_menu_access_csrf', + 'access arguments' => array(1, $task), + 'type' => MENU_CALLBACK, + ); + $items[$path] = array_merge($items[$path], $info); + } return ($items); } From 2585da2c40a4c13c5ebe0cb4f4c7c271b5d13188 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 16:01:45 -0500 Subject: [PATCH 0203/3476] OMG tasks work for projects!! --- devshop_projects/devshop_projects.module | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 17ce29d4b..fe78e060d 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -44,12 +44,12 @@ function devshop_projects_menu() { // hosting tasks ajax pages. // @TODO: This doesn't work and is making me very angry! foreach (hosting_available_tasks('project') as $task => $info){ - $path = 'node/%devshop_project_node/project_' . $task; + $path = 'node/%/project_' . $task; $items[$path] = array( 'title' => $info['title'], 'description' => $info['description'], - 'page callback' => 'drupal_get_form', - 'page arguments' => array('hosting_task_confirm_form', 1, $task), + 'page callback' => 'devshop_projects_hosting_task_confirm_form_page', + 'page arguments' => array(1, $task), 'access callback' => 'hosting_task_menu_access_csrf', 'access arguments' => array(1, $task), 'type' => MENU_CALLBACK, @@ -59,6 +59,10 @@ function devshop_projects_menu() { return ($items); } +function devshop_projects_hosting_task_confirm_form_page($nid, $task){ + $node = node_load($nid); + return drupal_get_form('hosting_task_confirm_form', $node, $task); +} /** * Implementation of hook_menu_alter() * @@ -87,7 +91,7 @@ function devshop_projects_menu_alter(&$items) { * @see hosting_task_menu() */ function devshop_hosting_task_menu_access($node, $task) { - if (!isset($node->nid) && is_int($node)){ + if (!isset($node->nid)){ $node = node_load($node); } if (user_access("create " . $task . " task")) { From 6d659925cfaa866a674b3a7dc8f7c886b859497e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 16:18:01 -0500 Subject: [PATCH 0204/3476] fixing a notice --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index f6eeadd29..2361d462f 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -194,7 +194,7 @@ function devshop_projects_load($node) { if (isset($additions['project_objects']['platform'])){ foreach ($additions['project_objects']['platform'] as $nid => $env){ $platform_nodes[$env] = node_load($nid); - if ($platform_nodes[$env]->platform_status == 0){ + if (!empty($platform_nodes[$env]) && $platform_nodes[$env]->platform_status == 0){ $platforms_ready = FALSE; } } From 05289c072713bcb70589ba2a963c0151359b54fc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 16:25:23 -0500 Subject: [PATCH 0205/3476] project status function --- devshop_projects/devshop_projects.module | 49 +++++++++++++++++------- devshop_projects/inc/forms.inc | 31 +-------------- devshop_projects/inc/nodes.inc | 36 +---------------- 3 files changed, 38 insertions(+), 78 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index fe78e060d..e1e179a6b 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -102,24 +102,47 @@ function devshop_hosting_task_menu_access($node, $task) { } /** - * Status display + * Status of Project * - * @TODO: All of it! + * @param $node + * a project node */ -function devshop_projects_project_status(){ +function devshop_project_status($node){ - if ($create_task_exists) { - $msg = t('Git Cloning...'); - } elseif ($platforms_exist) { - $msg = t('Verifying Platforms...'); - } elseif ($platforms_verified) { - $msg = t('Platforms Verified!'); - $msg .= drupal_get_form('devshop_projects_project_site_install'); + //Project status + $platforms_ready = TRUE; + $sites_ready = TRUE; + $sites_installing = FALSE; + + // PLATFORMS STATUS: Determine if all platforms are verified. + if (isset($node->project_objects['platform'])){ + foreach ($node->project_objects['platform'] as $nid => $env){ + $platform_nodes[$env] = node_load($nid); + if (!empty($platform_nodes[$env]) && $platform_nodes[$env]->platform_status == 0){ + $platforms_ready = FALSE; + } + } } else { - $msg = t('@TODO'); + $platforms_ready = FALSE; } - - return $msg; + // SITES STATUS: Determine if sites exist and are enabled + if ($platforms_ready && isset($node->project_objects['site'])){ + foreach ($node->project_objects['site'] as $nid => $env){ + $site_nodes[$env] = node_load($nid); + if ($site_nodes[$env]->site_status == 0){ + $sites_ready = FALSE; + $sites_installing = TRUE; + } + } + } else { + $sites_ready = FALSE; + } + + return $sites_ready? 'sites_ready': ( + $sites_installing? 'sites_installing': ( + $platforms_ready? 'platforms_ready': 'platforms_verifying' + ) + ); } /** diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index fdbfb1c84..bd10ed4b1 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -157,37 +157,8 @@ function devshop_projects_validate($node, &$form) { */ function devshop_projects_install_sites_form($form_state, $project_node) { - $form = array(); - $platforms_ready = TRUE; - $sites_ready = TRUE; - $sites_installing = FALSE; - - // PLATFORMS STATUS: Determine if all platforms are verified. - if (isset($project_node->project_objects['platform'])){ - foreach ($project_node->project_objects['platform'] as $nid => $env){ - $platform_nodes[$env] = node_load($nid); - if ($platform_nodes[$env]->platform_status == 0){ - $platforms_ready = FALSE; - } - } - } else { - $platforms_ready = FALSE; - } - // SITES STATUS: Determine if sites exist and are enabled - if ($platforms_ready && isset($project_node->project_objects['site'])){ - foreach ($project_node->project_objects['site'] as $nid => $env){ - $site_nodes[$env] = node_load($nid); - if ($site_nodes[$env]->site_status == 0){ - $sites_ready = FALSE; - $sites_installing = TRUE; - } - } - } else { - $sites_ready = FALSE; - } - // OUTPUT - if ($sites_installing) { + if ($project_node->project_status == 'sites_installing') { $form['note'] = array( '#type' => 'item', '#title' => t('Sites Installing'), diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2361d462f..fd74c04f5 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -185,42 +185,8 @@ function devshop_projects_load($node) { } $additions['project_objects'] = $objects; - //Project status - $platforms_ready = TRUE; - $sites_ready = TRUE; - $sites_installing = FALSE; - - // PLATFORMS STATUS: Determine if all platforms are verified. - if (isset($additions['project_objects']['platform'])){ - foreach ($additions['project_objects']['platform'] as $nid => $env){ - $platform_nodes[$env] = node_load($nid); - if (!empty($platform_nodes[$env]) && $platform_nodes[$env]->platform_status == 0){ - $platforms_ready = FALSE; - } - } - } else { - $platforms_ready = FALSE; - } - // SITES STATUS: Determine if sites exist and are enabled - if ($platforms_ready && isset($additions['project_objects']['site'])){ - foreach ($additions['project_objects']['site'] as $nid => $env){ - $site_nodes[$env] = node_load($nid); - if ($site_nodes[$env]->site_status == 0){ - $sites_ready = FALSE; - $sites_installing = TRUE; - } - } - } else { - $sites_ready = FALSE; - } - // Set status - $additions['project_status'] = $sites_ready? 'sites_ready': ( - $sites_installing? 'sites_installing': ( - $platforms_ready? 'platforms_ready': 'platforms_verifying' - ) - ); - + $additions['project_status'] = devshop_project_status((object) $additions); return $additions; } From 1d2cfc423680795e580868c6540ddcc048769833 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 17:48:53 -0500 Subject: [PATCH 0206/3476] don't verify twice, pls! --- devshop_projects/inc/nodes.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index fd74c04f5..fa70d3ca5 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -110,9 +110,6 @@ function devshop_projects_insert($node) { if ((!$node->old_vid)) { hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); } - - // Create hostmaster task - hosting_add_task($node->nid, 'verify'); } /** From 550788193b8cca4d69310e1a0b22e14d0529f391 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 17:54:22 -0500 Subject: [PATCH 0207/3476] check for branch first --- devshop_projects/inc/create-wizard.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f2310872c..3e2c2ad08 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -423,10 +423,9 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Create these platforms $all_platforms = array_merge($project->default_platforms, $project->branch_platforms); - foreach ($all_platforms as $platform_name => $branch) { - - if (empty($project->platforms[$platform_name])){ + // If platform hasn't been created yet, do so now! + if (!empty($branch) && empty($project->platforms[$platform_name])){ // Save the damn platform nodes $platform = new stdClass; From de11be5b9ab99a9524fbe8989a49f6ce430f1d08 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 17:56:43 -0500 Subject: [PATCH 0208/3476] returning our create site node function --- devshop_projects/devshop_projects.module | 69 ++++++++++++------------ 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index e1e179a6b..aa014102f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -155,42 +155,39 @@ function devshop_projects_hosting_drush_aliases_name($node) { return $node->project_name .".". $node->project_environment; } } -// -///* -// * Helper function to create a site in a project -// */ -//function devshop_projects_create_site($project_node, $platform_node, $env) { -// -// global $user; -// -// // Create the site nodes -// $node = new stdClass(); -// $node->type = 'site'; -// $node->status = 1; -// $node->uid = $user->uid; -// $node->title = $env .'.'. $project_node->base_url; -// -// // Aegir properties -// // @TODO: better client support -// $node->client = HOSTING_DEFAULT_CLIENT; -// $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); -// -// $node->platform = $platform_node->nid; -// $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); -// -// //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); -// -// // @TODO: Improve site language handling? -// $node->site_language = !empty($user->language)? $user->language: 'en'; -// -// // Save the node -// if ($node = node_submit($node)) { -// node_save($node); -// -// //And save the association to the project -// db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type) VALUES (%d, %d, "%s", "%s")', $project_node->nid, $node->nid, $node->type, $env); -// } -//} + +/* + * Helper function to create a site in a project + */ +function devshop_projects_create_site($project_node, $platform_node, $env) { + + global $user; + + // Create the site nodes + $node = new stdClass(); + $node->type = 'site'; + $node->status = 1; + $node->uid = $user->uid; + $node->title = $env .'.'. $project_node->base_url; + + // Aegir properties + // @TODO: better client support + $node->client = HOSTING_DEFAULT_CLIENT; + $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); + + $node->platform = $platform_node->nid; + $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); + + //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); + + // @TODO: Improve site language handling? + $node->site_language = !empty($user->language)? $user->language: 'en'; + + // Save the node + if ($node = node_submit($node)) { + node_save($node); + } +} ///** // * Helper function which writes a serialize array in the project file From 2e9402a566f474c2bae9c53f9e1749069181aec6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:13:13 -0500 Subject: [PATCH 0209/3476] I am a horrible programmer --- devshop_projects/inc/nodes.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index fa70d3ca5..eb4365c99 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -63,12 +63,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { } // On update or delete, delete the existing records. We will replace below. - if ($op = 'update' || $op = 'delete') { + if ($op == 'update' || $op == 'delete') { db_query('DELETE FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid); } // On insert or update, insert records saving this objects project and environment - if ($op = 'update' || $op = 'insert') { + if ($op == 'update' || $op == 'insert') { if (!empty($node->project) && !empty($node->environment)) { // Get the project node by name. $project = hosting_context_load($node->project); From f4480ba522a97b6732b8bd8b15b5c2d3c5440ffc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:14:20 -0500 Subject: [PATCH 0210/3476] adding project properties to the node we are saving --- devshop_projects/devshop_projects.module | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index aa014102f..14a691f3b 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -113,7 +113,6 @@ function devshop_project_status($node){ $platforms_ready = TRUE; $sites_ready = TRUE; $sites_installing = FALSE; - // PLATFORMS STATUS: Determine if all platforms are verified. if (isset($node->project_objects['platform'])){ foreach ($node->project_objects['platform'] as $nid => $env){ @@ -127,6 +126,7 @@ function devshop_project_status($node){ } // SITES STATUS: Determine if sites exist and are enabled if ($platforms_ready && isset($node->project_objects['site'])){ + foreach ($node->project_objects['site'] as $nid => $env){ $site_nodes[$env] = node_load($nid); if ($site_nodes[$env]->site_status == 0){ @@ -137,7 +137,6 @@ function devshop_project_status($node){ } else { $sites_ready = FALSE; } - return $sites_ready? 'sites_ready': ( $sites_installing? 'sites_installing': ( $platforms_ready? 'platforms_ready': 'platforms_verifying' @@ -177,9 +176,10 @@ function devshop_projects_create_site($project_node, $platform_node, $env) { $node->platform = $platform_node->nid; $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); - - //$node->port = db_result(db_query("SELECT ports FROM {hosting_platform} p WHERE p.nid = %d", $nid)); - + + $node->environment = $env; + $node->project = $project_node->title; + // @TODO: Improve site language handling? $node->site_language = !empty($user->language)? $user->language: 'en'; From e1ed253619832c87cc82817e3cedadcc4ccd8d8f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:29:02 -0500 Subject: [PATCH 0211/3476] fixing status check --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index eb4365c99..2440472b5 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -183,7 +183,7 @@ function devshop_projects_load($node) { $additions['project_objects'] = $objects; // Set status - $additions['project_status'] = devshop_project_status((object) $additions); + $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node)); return $additions; } From a9e0b6b22c4523b3834be842ce4667097d1c9310 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:31:33 -0500 Subject: [PATCH 0212/3476] adding "preparing-project" status --- devshop_projects/devshop_projects.module | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 14a691f3b..16e19faf5 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -113,6 +113,12 @@ function devshop_project_status($node){ $platforms_ready = TRUE; $sites_ready = TRUE; $sites_installing = FALSE; + + // PROJECT STATUS + if (empty($node->project_objects) || empty($node->git_branches)){ + return 'preparing-project'; + } + // PLATFORMS STATUS: Determine if all platforms are verified. if (isset($node->project_objects['platform'])){ foreach ($node->project_objects['platform'] as $nid => $env){ From 6992d8c0d99ffc82672ed7fb850f71f3c6b52a6c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:31:45 -0500 Subject: [PATCH 0213/3476] fixing typo --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2440472b5..39702c0d0 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -183,7 +183,7 @@ function devshop_projects_load($node) { $additions['project_objects'] = $objects; // Set status - $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node)); + $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node))); return $additions; } From 19efc81c9a68e626022ada3d25ecbd2d6589ad1a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:36:08 -0500 Subject: [PATCH 0214/3476] if Project is still being prepared, redirect to wizard when trying to view node. if project sites are installed, return an empty form. --- devshop_projects/inc/forms.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index bd10ed4b1..e69df326f 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -165,7 +165,11 @@ function devshop_projects_install_sites_form($form_state, $project_node) { '#value' => t('Your sites are being installed!'), ); return $form; - } elseif ($sites_ready) { + } + elseif ($project_node->project_status == 'preparing-project') { + drupal_goto('node/add/project'); + } + elseif ($project_node->project_status == 'sites-installed') { return; } From e6fbe290adc7a452f34b47739b1bb34ceb3f9eb4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:39:47 -0500 Subject: [PATCH 0215/3476] Removing uneeded links --- devshop_projects/inc/ui.inc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index c6aaaa65f..67d94d0fe 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -130,18 +130,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#value' => $table, ); } - if ($node->project_status == 'sites_ready'){ - $node->content['sites']['add_platform'] = array( - '#type' => 'item', - '#value' => l(t('Create and add additional platforms'), - "hosting/projects/platform/create/$node->nid"), - ); - } - - $node->content['sites']['delete_project'] = array( - '#type' => 'item', - '#value' => l("Delete this project", "hosting/projects/delete/$node->nid") - ); //Tasks $node->content['tasks_view'] = array( From 2dca203b3a84f493f15ec98191d59b104ddaf88e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:52:18 -0500 Subject: [PATCH 0216/3476] adding expected hook name --- devshop_projects/inc/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 84a9e5d41..a88bbb4ea 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -35,7 +35,7 @@ function devshop_projects_hosting_tasks() { * * For "Commit" task. */ -function devshop_hosting_hosting_task_create_form($node) { +function hosting_task_create_form($node) { $form['message'] = array( '#title' => t('Commit Message'), From 53b506e8e4dab82a3e2d4aa54b18cb69640201d5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 18:58:50 -0500 Subject: [PATCH 0217/3476] create platform task form --- devshop_projects/inc/tasks.inc | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index a88bbb4ea..b6dfd6ac4 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -15,7 +15,7 @@ function devshop_projects_hosting_tasks() { 'provision_save' => TRUE, 'access callback' => 'devshop_hosting_task_menu_access', ); - $tasks['project']['create'] = array( + $tasks['project']['devshop-create'] = array( 'title' => t('Create New Platform'), 'description' => t('Creates a new platform within this project.'), 'access callback' => 'devshop_hosting_task_menu_access', @@ -35,27 +35,31 @@ function devshop_projects_hosting_tasks() { * * For "Commit" task. */ -function hosting_task_create_form($node) { +function hosting_task_devshop_create_form($node) { - $form['message'] = array( - '#title' => t('Commit Message'), - '#type' => 'textarea', - '#description' => $descr, + $form['branch'] = array( + '#title' => t('Branch'), + '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, re-verify your project.'), + '#type' => 'select', + '#options' => $node->git_branches, ); - $form['push'] = array( - '#title' => t('Push code after commit?'), - '#type' => 'checkbox', - '#default_value' => 1, + // @TODO: Add "create branch" functionality. + $form['platform_name'] = array( + '#title' => t('Platform Name'), + '#type' => 'textfield', + '#description' => t('Enter the system name of your platform. For consistency, you should make this match the branch name.'), ); - $form['revert'] = array( - '#title' => t('Force revert features after commit?'), + $form['pull'] = array( + '#title' => t('Enable Pull on Commit'), '#type' => 'checkbox', '#default_value' => 1, ); - // @TODO: Provide a DIFF display to give the user an idea of what has changed. + return $form; } +// @TODO: Validate platform name + /** * Implements hook_hosting_project_context_options() From 4edddc1890ded0951227fbcf753e03d896e68c58 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 19:20:20 -0500 Subject: [PATCH 0218/3476] fixing branch options array --- devshop_projects/inc/tasks.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index b6dfd6ac4..74e6bae24 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -36,12 +36,12 @@ function devshop_projects_hosting_tasks() { * For "Commit" task. */ function hosting_task_devshop_create_form($node) { - + $branch_options = array_combine($node->git_branches, $node->git_branches); $form['branch'] = array( '#title' => t('Branch'), '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, re-verify your project.'), '#type' => 'select', - '#options' => $node->git_branches, + '#options' => $branch_options, ); // @TODO: Add "create branch" functionality. $form['platform_name'] = array( From c9d411152249f2d216dd162101b8b5e0743981fb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Dec 2012 19:33:59 -0500 Subject: [PATCH 0219/3476] removing devshop_projects_platform --- .../devshop_projects_platform.info | 5 - .../devshop_projects_platform.install | 63 -------- .../devshop_projects_platform.module | 140 ------------------ 3 files changed, 208 deletions(-) delete mode 100644 devshop_projects_platform/devshop_projects_platform.info delete mode 100644 devshop_projects_platform/devshop_projects_platform.install delete mode 100644 devshop_projects_platform/devshop_projects_platform.module diff --git a/devshop_projects_platform/devshop_projects_platform.info b/devshop_projects_platform/devshop_projects_platform.info deleted file mode 100644 index 230b5ff34..000000000 --- a/devshop_projects_platform/devshop_projects_platform.info +++ /dev/null @@ -1,5 +0,0 @@ -name = DevShop Projects Platform -description = A DevShop Projects module that allows the user to create platforms with different git repo and branch than the original project setting. -core = 6.x -package = DevShop - diff --git a/devshop_projects_platform/devshop_projects_platform.install b/devshop_projects_platform/devshop_projects_platform.install deleted file mode 100644 index b8c745d3c..000000000 --- a/devshop_projects_platform/devshop_projects_platform.install +++ /dev/null @@ -1,63 +0,0 @@ - array( - 'project_nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Project/Node ID.', - ), - 'platform_name' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => 255, - 'default' => '', - 'description' => 'The name this platform', - ), - 'git_url' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => 255, - 'default' => '', - 'description' => 'The git URL of this platform', - ), - 'branch' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => 255, - 'default' => '', - 'description' => 'The git branch for this platform', - ), - ), - 'primary key' => array('platform_name'), - ); - - return $schema; -} - -/** - * Implementation of hook_install(). - */ -function devshop_projects_platform_install() { - // Create tables. - drupal_install_schema('devshop_projects_platform'); -} - -/** - * Implementation of hook_uninstall(). - */ -function devshop_projects_platform_uninstall() { - // Delete tables. - drupal_uninstall_schema('devshop_projects_platform'); -} - diff --git a/devshop_projects_platform/devshop_projects_platform.module b/devshop_projects_platform/devshop_projects_platform.module deleted file mode 100644 index 9b0838f1f..000000000 --- a/devshop_projects_platform/devshop_projects_platform.module +++ /dev/null @@ -1,140 +0,0 @@ - 'textfield', - '#title' => t('Git URL'), - '#required' => FALSE, - '#description' => t(''), - '#size' => 40, - '#default_value' => $project_node->git_url, - '#maxlength' => 255, - ); - $form['branch'] = array( - '#type' => 'textfield', - '#title' => t('Branch'), - '#description' => t(''), - '#size' => 40, - '#default_value' => 'master', - '#maxlength' => 255, - ); - - // Setup a call back when this form is submitted - array_unshift($form['#submit'], - devshop_projects_platform_platform_create_save); - } -} - -/* - * This is a pseudo form submit function that gets invoked when the - * project platform create form is submitted. - */ - -function devshop_projects_platform_platform_create_save($form, &$form_state) { - - $project_node = node_load($form_state['values']['nid']); - $pname = $form_state['values']['platform_name']; - $pname = "{$project_node->title}_{$pname}"; - db_query("INSERT INTO {hosting_devshop_project_platform} " . - "(project_nid, platform_name, git_url, branch) " . - "VALUES (%d, '%s', '%s', '%s')", - $project_node->nid, - $pname, - $form_state['values']['git_url'], - $form_state['values']['branch']); -} - -/** - * Implements hook_nodeapi() - */ -function devshop_projects_platform_nodeapi(&$node, $op, $a3 = null) { - //Platforms and Sites - if ($node->type == 'platform' || $node->type == 'site'){ - - if ($node->type == 'platform') { - $pname = $node->title; - } - else { - $pname = node_load($node->platform)->title; - } - - // Load Project info - if ($op == 'load') { - $data = db_fetch_array(db_query("SELECT git_url, branch " . - "FROM {hosting_devshop_project_platform} " . - "WHERE platform_name = '%s'", - $pname)); - return $data; - } - - // Display Project info - if ($op == 'view') { - if($node->git_url) { - $node->content['info']['git_url'] = array( - '#type' => 'item', - '#title' => t('Git URL'), - '#value' => $node->git_url, - '#weight' => 0 - ); - } - - if($node->branch) { - $node->content['info']['branch'] = array( - '#type' => 'item', - '#title' => t('Git Branch'), - '#value' => $node->branch, - '#weight' => 0 - ); - } - } - } - - if ($node->type == 'platform' && $op == 'delete') { - db_query("DELETE FROM {hosting_devshop_project_platform} " . - "WHERE platform_name = '%s'", $pname); - return; - } -} - - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task - * - */ - -function devshop_projects_platform_post_hosting_create_task($task, $data) { - watchdog('devshop', "Create post task hook called"); - watchdog('devshop', print_r($task, 1)); - - -} - -/** - * Implementation of hook_hosting_TASK_TYPE_task_rollback(). - * - */ -function devshop_projects_platform_hosting_create_task_rollback($task, $data) { - watchdog('devshop', "Create task rollback hook called"); - watchdog('devshop', print_r($task, 1)); -} From 7c56aca065d929196f3764196697f44b995dec01 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 10:35:44 -0500 Subject: [PATCH 0220/3476] OK Sites get created on platform verify, but we have some schema problems. gotta save branch and name to platforms. --- devshop_projects/devshop_projects.drush.inc | 56 +++ devshop_projects/devshop_projects.module | 84 +++-- devshop_projects/inc/create-wizard.inc | 16 - devshop_projects/inc/delete.inc | 263 ++++++++++++++ devshop_projects/inc/forms.inc | 12 +- devshop_projects/inc/nodes.inc | 7 +- devshop_projects/inc/tasks.inc | 369 +++----------------- devshop_projects/inc/ui.inc | 5 +- 8 files changed, 425 insertions(+), 387 deletions(-) create mode 100644 devshop_projects/devshop_projects.drush.inc create mode 100644 devshop_projects/inc/delete.inc diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc new file mode 100644 index 000000000..a6b6b88e0 --- /dev/null +++ b/devshop_projects/devshop_projects.drush.inc @@ -0,0 +1,56 @@ +ref->type != 'platform') { + return; + } + + // Get objects + $nid = $task->ref->nid; + $platform = node_load($nid); + + // If this platform isn't in a project, bail. + if (empty($platform->project_nid)){ + drush_log('[DEVSHOP] No project found for this platform.', 'notice'); + return; + } + + // Get the project + $project = node_load($platform->project_nid); + + // If the project doesn't have an install profile chosen yet, bail. + if (empty($project->install_profile)){ + drush_log('[DEVSHOP] No install profile found for this platform\'s project.', 'notice'); + return; + } + + // If the project has a site already, bail. + $sites = array_flip($project->project_objects['site']); + $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); + if (!empty($sites)){ + drush_log('[DEVSHOP] Platform already has a site.', 'notice'); + return; + } + + // live. Let's create a site based off of this platform. + drush_log('[DEVSHOP] Platform verified. Creating your site.'); + $site_node = devshop_projects_create_site($project, $platform, $platform->environment); + + drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); + +} diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 16e19faf5..aa509fd36 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -39,8 +39,8 @@ function devshop_projects_menu() { 'menu_name' => 'primary-links', 'weight' => 1, ); - - + + // hosting tasks ajax pages. // @TODO: This doesn't work and is making me very angry! foreach (hosting_available_tasks('project') as $task => $info){ @@ -104,21 +104,24 @@ function devshop_hosting_task_menu_access($node, $task) { /** * Status of Project * + * @TODO: When creating a new platform for an existing project, the status + * goes back to "platforms not-verified" + * * @param $node * a project node */ function devshop_project_status($node){ - + //Project status $platforms_ready = TRUE; $sites_ready = TRUE; $sites_installing = FALSE; - + // PROJECT STATUS if (empty($node->project_objects) || empty($node->git_branches)){ return 'preparing-project'; } - + // PLATFORMS STATUS: Determine if all platforms are verified. if (isset($node->project_objects['platform'])){ foreach ($node->project_objects['platform'] as $nid => $env){ @@ -161,31 +164,36 @@ function devshop_projects_hosting_drush_aliases_name($node) { } } -/* - * Helper function to create a site in a project +/** + * Helper function to create a site in a project. + * Used by Wizard & "Create Platform" > Post Verify */ -function devshop_projects_create_site($project_node, $platform_node, $env) { +function devshop_projects_create_site($project_node, $platform_node, $platform_name) { global $user; - + // Create the site nodes $node = new stdClass(); $node->type = 'site'; $node->status = 1; $node->uid = $user->uid; - $node->title = $env .'.'. $project_node->base_url; + $node->title = $platform_name .'.'. $project_node->base_url; // Aegir properties - // @TODO: better client support - $node->client = HOSTING_DEFAULT_CLIENT; - $node->db_server = db_result(db_query('SELECT nid FROM {hosting_db_server}')); - + // @TODO: better client & DB support + $node->client = HOSTING_DEFAULT_CLIENT; + $servers = hosting_get_servers('db'); + $node->db_server = key($servers); + $node->platform = $platform_node->nid; + + // Lookup this platforms install profile $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); - - $node->environment = $env; + + $node->environment = $platform_name; + $node->git_branch = $platform_node->git_branch; $node->project = $project_node->title; - + // @TODO: Improve site language handling? $node->site_language = !empty($user->language)? $user->language: 'en'; @@ -193,33 +201,23 @@ function devshop_projects_create_site($project_node, $platform_node, $env) { if ($node = node_submit($node)) { node_save($node); } + return $node; } -///** -// * Helper function which writes a serialize array in the project file -// */ -//function devshop_projects_project_data_set($nid, $data) { -// db_query("UPDATE {hosting_devshop_project} SET data = '%s' WHERE nid = %d", -// serialize($data), $nid); -//} -// -///** -// * Helper function which reads the serialize array in the project file -// */ -//function devshop_projects_project_data_get($nid) { -// -// $sdata = db_result(db_query("SELECT data FROM {hosting_devshop_project} " . -// "WHERE nid = %d", $nid)); -// -// if (!$sdata || strlen($sdata) < 1) { -// $data = array(); -// } -// else { -// $data = unserialize($sdata); -// } -// -// return $data; -//} - +/** + * Nodemaker + */ +function _devshop_projects_node_create($type, $node = stdClass){ + global $user; + // @TODO: Validate type + $node->type = $type; + $node->status = 1; + $node->uid = $user->uid; + $node->name = $user->name; + if ($node = node_submit($node)) { + node_save($node); + } + return $node; +} diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 3e2c2ad08..7b8b3a59a 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -90,22 +90,6 @@ function devshop_projects_create_wizard_info(){ * WIZARD TOOLS */ -/** - * Nodemaker - */ -function _devshop_projects_node_create($type, $node = stdClass){ - global $user; - - // @TODO: Validate type - $node->type = $type; - $node->status = 1; - $node->uid = $user->uid; - $node->name = $user->name; - if ($node = node_submit($node)) { - node_save($node); - } - return $node; -} /** * NEXT callback diff --git a/devshop_projects/inc/delete.inc b/devshop_projects/inc/delete.inc new file mode 100644 index 000000000..4addc1502 --- /dev/null +++ b/devshop_projects/inc/delete.inc @@ -0,0 +1,263 @@ +nid); + + // Keys to tasks to be performed. Objects must be processed in this + // order due to task dependencies. + $keys = array(array('type' => 'site', + 'task' => 'disable'), + array('type' => 'site', + 'task' => 'delete'), + array('type' => 'platform', + 'task' => 'delete')); + + // Iterate through arrays of tasks => nids and queue one tasks that + // is left to do + + foreach ($keys as $oper) { + $key1 = "project_delete_{$oper['type']}_{$oper['task']}"; + + if (isset($data[$key1])) { + if (count($data[$key1]) > 0) { + // Use the first nid in the list and stop looking for any more work + $nid = $data[$key1][0]; + break; + } + else { + // Array is empty, delete it + unset($data[$key1]); + } + $updated = TRUE; + } + + } + + // If $nid != FALSE, then we need to act on that node. If it is FALSE, + // then we are all done. We need to set the project node status + // field to 0 and delete that row from the hosting_devshop_project + // table + + if ($nid) { + hosting_add_task($nid, $oper['task']); + } + + // If we changed the project data array in any way, we need to save it + if ($updated) { + devshop_projects_project_data_set($pnode->nid, $data); + } + + if (!$nid) { + $pnode->status = 0; + + //$pnode->rid = 0; + node_save($pnode); + + // Check to see if the user wanted the project directory removed. + // If so, make sure the directory path is valid and isn't "/"!! + if ($data['deleting_project_remove_dir'] && + strlen($pnode->code_path) > 1) { + @exec("rm -rf {$pnode->code_path}"); + } + + // delete this project + db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", + $pnode->nid); + } +} + +/* + * This function get called when we are in the process of deleting a + * project and all of its related sites & platforms. It is invoked + * by the one of the hook_post_hosting_TASK_TYPE_task() hook when + * a site disable or site/platform delete tasks complete. + * + * All of the sites that we need to disable and delete and all of the + * platforms we need to delete are kept in the project node's + * $node->data field. There are four elements in that array: + * + * + * project data['deleting_project']: if TRUE, we are in the process + * of delete this project and all sites & platforms. + * project data['project_delete_site_disable']: array of site NID's + * that are to be disabled. + * project data['project_delete_site_delete']: an array of site NID's + * that are to be deleted once they are disabled. + * project data['project_delete_platform_delete']: an array of platform + * NID's that are to be deleted once the sites based on these platforms + * are disabled and deleted. + * + * We first disable all of the sites, then we delete them. Next we delete + * all of the platforms. As soon as a site or platform is deleted, we remove + * the NID from the corresponding array. When everything is deleted, we + * remove the rows from the object table. Hostmaster will remove them from + * the hosting context table during the delete task. + * + * @param $nid + * The NID of the object which the task just completed on. It can be + * the project node (when the actual project is being deleted). The + * site site node (when the site was disabled or deleted), or the + * platform node (when the platform was deleted). + * @param $op + * The operation (task type) that just complted. Will be one of: + * 'project delete', 'site disable', 'site delete', or + * 'platform delete'. + * @param $failed + * If TRUE, this task did not complete succesfully. We know this + * the 'rollback' hook was invoked rather then the 'post task' + * hook. There isn't much we can do but log it and carry on. + */ +function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { + + if (!($node = node_load($nid))) { + watchdog('devshop', "Delete continue: nid $nid not found"); + return; + } + + if ($node->type == 'project') { + // First step complete: project deleted. Now we need to + // disable all of the sites, delete them. And then delete + // all of the platforms. + devshop_projects_project_delete_queue_next_task($node); + return; + } + else if (($node->type != 'site') && + ($node->type != 'platform')) { + // We only care about sites and platforms + return; + } + + + // Does this object belong to a devshop project? If not, + // then just ignore it. + if (!($node->project_nid)) { + return; + } + + // load project node + if (!($project_node = node_load($node->project_nid))) { + watchdog('devshop', + "Delete continue: can't load project nid $node->project_nid"); + return; + } + + $data = devshop_projects_project_data_get($project_node->nid); + + if (!$data['deleting_project']) { + return; + } + + if ($op == 'delete') { + // delete this object if the task just complete was a delete + db_query("DELETE FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d AND object_nid = %d", + $project_node->nid, $node->nid); + } + + // Remove this nid from our array of things to do + $key1 = "project_delete_{$node->type}_{$op}"; + $key2 = array_search($node->nid, $data[$key1]); + + if ($key2 === FALSE) { + watchdog('devshop', "search for nid {$node->nid} failed, key = $key2"); + } + else { + array_shift($data[$key1]); + if (count($data[$key1]) == 0) { + unset($data[$key1]); + } + devshop_projects_project_data_set($project_node->nid, $data); + } + + // do the next delete task + devshop_projects_project_delete_queue_next_task($project_node); +} + +/* + * Implementation of hook_post_hosting_TASK_TYPE_task + * + */ +function devshop_projects_post_hosting_delete_task($task, $data) { + watchdog('devshop', "Delete post task hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'delete'); + +} + +/** + * Implementation of hook_hosting_TASK_TYPE_task_rollback(). + * + * This hook is invoked when the delete task fails. + * Carry on my wayward son. There will be peace when you are done. + * Lay your weary head to rest. Don't you cry no more. + */ +function devshop_projects_hosting_delete_task_rollback($task, $data) { + watchdog('devshop', "Delete task rollback hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); +} + +/** + * Implementation of hook_post_hosting_TASK_TYPE_task + * + */ +function devshop_projects_post_hosting_disable_task($task, $data) { + watchdog('devshop', "Disable post task hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'disable'); +} + +/** + * Implementation of hook_hosting_TASK_TYPE_task_rollback(). + * + * This hook is invoked when the disable task fails. But we must carry on! + */ +function devshop_projects_hosting_disable_task_rollback($task, $data) { + watchdog('devshop', "Disable task rollback hook called"); + devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); +} + + +/** + * Returns TRUE is the project create task for the given node failed. + */ +function devshop_project_project_create_failed($nid, &$task) { + + if ($nid) { + $task = hosting_get_most_recent_task($nid, 'devshop-create'); + if ($task && $task->task_status != 1) { + // Project Create failed. + return TRUE; + } + } + + // No task found OR it is successful + return FALSE; +} + diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e69df326f..1019295b6 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -21,6 +21,11 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ $form['buttons']['submit']['#value'] = t('Save and Retry'); } } + + // On Hosting Task form + if ($form_id == 'hosting_task_confirm_form'){ + array_unshift($form['#submit'], 'hosting_task_devshop_create_form_submit'); + } } /** @@ -153,7 +158,10 @@ function devshop_projects_validate($node, &$form) { } /** - * Form for site installation + * Form for site installation + * + * Displayed on a PROJECT node page. This is only used once per project, + * during the "new project" process. */ function devshop_projects_install_sites_form($form_state, $project_node) { @@ -288,7 +296,7 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ $project_node = node_load($form_state['values']['nid']); $project_node->install_profile = $form_state['values']['install_profile']; - // Save installation profile to database + // Save project installation profile to database db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); // Create the site nodes diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 39702c0d0..531f3e669 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -3,7 +3,8 @@ * @file devshop_project.node.inc * * DevShop Project Node related hooks and support functions. - * + * + * @TODO: Add default http and db servers to project nodes. */ /** @@ -117,7 +118,7 @@ function devshop_projects_insert($node) { * * 1. Updates our table. * 2. Adds a "Verify" task for this project. - * + * */ function devshop_projects_update($node) { @@ -183,7 +184,7 @@ function devshop_projects_load($node) { $additions['project_objects'] = $objects; // Set status - $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node))); + $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node))); return $additions; } diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 74e6bae24..6025f12c6 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -1,6 +1,6 @@ git_branches, $node->git_branches); @@ -42,33 +42,64 @@ function hosting_task_devshop_create_form($node) { '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, re-verify your project.'), '#type' => 'select', '#options' => $branch_options, + '#required' => TRUE, ); // @TODO: Add "create branch" functionality. $form['platform_name'] = array( '#title' => t('Platform Name'), '#type' => 'textfield', '#description' => t('Enter the system name of your platform. For consistency, you should make this match the branch name.'), + '#required' => TRUE, ); + /* @TODO: Let devshop_pull handle this. $form['pull'] = array( '#title' => t('Enable Pull on Commit'), '#type' => 'checkbox', '#default_value' => 1, ); - + */ + $form['#submit'][] = 'hosting_task_devshop_create_form_submit'; return $form; } -// @TODO: Validate platform name +/** + * Extra submit function for hosting_task_confirm_form() + * + * @see devshop_projects_form_alter(). We had to add the submit hadler there. + */ +function hosting_task_devshop_create_form_submit($form, &$form_state) { + + $project = node_load($form_state['values']['nid']); + $platform_name = $form_state['values']['parameters']['platform_name']; + $branch = $form_state['values']['parameters']['branch']; + $servers = hosting_get_servers('http'); + $server = variable_get('devshop_projects_default_web_server', key($servers)); + + // hosting_platform fields + $platform = new stdClass; + $platform->title = $project->title . '_' . $platform_name; + $platform->publish_path = $project->code_path . '/' . $platform_name; + $platform->web_server = $server; + $platform->git_branch = $branch; + $platform->project = $project->title; + $platform->environment = $branch; + watchdog('debug', 'Form state: ' . print_r($form_state['values'],1)); + watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); + + $platform_node = _devshop_projects_node_create('platform', $platform); + + +} /** * Implements hook_hosting_project_context_options() - * + * * This transfers data from the node to thes aegir context object (the alias!) * For project entities. This is where we find the branches and tags on the remote. */ function devshop_projects_hosting_project_context_options(&$task) { - + drush_log('[DEVSHOP] devshop_projects_hosting_project_context_options()', 'ok'); $task->context_options['server'] = '@server_master'; $task->context_options['project_name'] = $task->ref->title; @@ -76,13 +107,13 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['base_url'] = $task->ref->base_url; $task->context_options['code_path'] = trim($task->ref->code_path, " "); $task->context_options['git_url'] = $task->ref->git_url; - + $branches = getBranchesAndTags($task->ref->git_url); - + $task->ref->git_branches = $branches['branches']; $task->ref->git_tags = $branches['tags']; node_save($task->ref); - + $task->context_options['git_branches'] = $branches['branches']; $task->context_options['git_tags'] = $branches['tags']; } @@ -97,7 +128,7 @@ function getBranchesAndTags($git_url = NULL){ $command = "git ls-remote {$git_url}"; drush_shell_exec($command); $exec_output = drush_shell_exec_output(); - + drush_log('[DEVSHOP] running '.$command, 'ok'); drush_log('[DEVSHOP] ' . implode("\n", $exec_output), 'ok'); @@ -106,26 +137,26 @@ function getBranchesAndTags($git_url = NULL){ if ('Permission denied' == substr($exec_output[0], 0, 17)){ drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error:'). implode("\n", $exec_output), 'error'); } - + // If remote list is empty, something else went wrong. if (count($exec_output) == 1 && empty($exec_output[0])){ drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('Something went wrong. Check the git URL and try again.'), 'error'); return; } - + // Build tag and branch list $branches = array(); $tags = array(); - + foreach ($exec_output AS $line_string){ // @TODO: Would love some regex love here // Example remote line: // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 $line = explode(" ", $line_string); $ref = explode("/", $line[1]); - + $branch = array_pop($ref); - + if ($ref[1] == 'heads') { $branches[] = $branch; } else if ($ref[1] == 'tags') { @@ -138,7 +169,7 @@ function getBranchesAndTags($git_url = NULL){ /** * Implements hook_hosting_site_context_options() - * + * * This transfers data from the node to the aegir context object (the alias!) * For site entities. */ @@ -149,7 +180,7 @@ function devshop_projects_hosting_site_context_options(&$task) { /** * Implements hook_hosting_site_context_options() - * + * * This transfers data from the node to the aegir context object (the alias!) * For site entities. */ @@ -158,310 +189,8 @@ function devshop_projects_hosting_platform_context_options(&$task) { $task->context_options['project'] = $task->ref->project; $task->properties['task properties'] = 'works'; $task->ref->properties['task ref properties'] = 'works'; - - d()->setProperty('setProperty', 'works'); - } -} - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task - * - * If a new platform has been added to a project and the env - * is not dev, test, or live, auto-create a site.... - * - * - * @TODO: This is used only for creating the new site when a "branch platform" - * is created... we should save something in the task or platform node so we - * don't have to use this logic. - */ -function devshop_projects_post_hosting_verify_task($task, $data) { - - // We only case about platforms. - if ($task->ref->type != 'platform') { - return; - } - - // Get objects - $nid = $task->ref->nid; - $platform = node_load($nid); - // If this platform isn't in a project, bail. - if (empty($platform->project_nid)){ - return; - } - - // Get the project - $project = node_load($platform->project_nid); - - // If the project doesn't have an install profile chosen yet, bail. - if (empty($project->install_profile)){ - return; - } - - // If the project has a site already - $sites = array_flip($project->project_objects['site']); - if (isset($sites[$platform->project_environment])){ - return; - } - - // So this is a platform which is in a project and it not dev, test, or - // live. Let's create a site based off of this platform. - devshop_projects_create_site($project, $platform, $platform->project_environment); -} - -function watchcat($m, $l) { - $f = fopen("/tmp/{$m}.catlog", "a+"); - $cr = ($l[strlen($l) - 1] == '\n') ? true : false; - fwrite($f, sprintf("%s %s%s", date('Y-m-d H:i:s'), $l, $cr ? "" : "\n")); - fclose($f); -} - - -/* - * This function checks to see if there if there are any more objects - * to disable or delete during the 'project delete' task. The list of - * tasks that need to be done are kept in the project data field. See the - * following two function for more detail on those arrays: - * - * devshop_projects_project_delete_form_submit - * devshop_projects_project_delete_continue - * - * @param $pnode - * The project node that is to be deleted. - * - */ -function devshop_projects_project_delete_queue_next_task($pnode) { - // Assume all done - $updated = FALSE; - $nid = FALSE; - $data = devshop_projects_project_data_get($pnode->nid); - - // Keys to tasks to be performed. Objects must be processed in this - // order due to task dependencies. - $keys = array(array('type' => 'site', - 'task' => 'disable'), - array('type' => 'site', - 'task' => 'delete'), - array('type' => 'platform', - 'task' => 'delete')); - - // Iterate through arrays of tasks => nids and queue one tasks that - // is left to do - - foreach ($keys as $oper) { - $key1 = "project_delete_{$oper['type']}_{$oper['task']}"; - - if (isset($data[$key1])) { - if (count($data[$key1]) > 0) { - // Use the first nid in the list and stop looking for any more work - $nid = $data[$key1][0]; - break; - } - else { - // Array is empty, delete it - unset($data[$key1]); - } - $updated = TRUE; - } - - } - - // If $nid != FALSE, then we need to act on that node. If it is FALSE, - // then we are all done. We need to set the project node status - // field to 0 and delete that row from the hosting_devshop_project - // table - - if ($nid) { - hosting_add_task($nid, $oper['task']); - } - - // If we changed the project data array in any way, we need to save it - if ($updated) { - devshop_projects_project_data_set($pnode->nid, $data); - } - - if (!$nid) { - $pnode->status = 0; - - //$pnode->rid = 0; - node_save($pnode); - - // Check to see if the user wanted the project directory removed. - // If so, make sure the directory path is valid and isn't "/"!! - if ($data['deleting_project_remove_dir'] && - strlen($pnode->code_path) > 1) { - @exec("rm -rf {$pnode->code_path}"); - } - - // delete this project - db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", - $pnode->nid); - } -} - -/* - * This function get called when we are in the process of deleting a - * project and all of its related sites & platforms. It is invoked - * by the one of the hook_post_hosting_TASK_TYPE_task() hook when - * a site disable or site/platform delete tasks complete. - * - * All of the sites that we need to disable and delete and all of the - * platforms we need to delete are kept in the project node's - * $node->data field. There are four elements in that array: - * - * - * project data['deleting_project']: if TRUE, we are in the process - * of delete this project and all sites & platforms. - * project data['project_delete_site_disable']: array of site NID's - * that are to be disabled. - * project data['project_delete_site_delete']: an array of site NID's - * that are to be deleted once they are disabled. - * project data['project_delete_platform_delete']: an array of platform - * NID's that are to be deleted once the sites based on these platforms - * are disabled and deleted. - * - * We first disable all of the sites, then we delete them. Next we delete - * all of the platforms. As soon as a site or platform is deleted, we remove - * the NID from the corresponding array. When everything is deleted, we - * remove the rows from the object table. Hostmaster will remove them from - * the hosting context table during the delete task. - * - * @param $nid - * The NID of the object which the task just completed on. It can be - * the project node (when the actual project is being deleted). The - * site site node (when the site was disabled or deleted), or the - * platform node (when the platform was deleted). - * @param $op - * The operation (task type) that just complted. Will be one of: - * 'project delete', 'site disable', 'site delete', or - * 'platform delete'. - * @param $failed - * If TRUE, this task did not complete succesfully. We know this - * the 'rollback' hook was invoked rather then the 'post task' - * hook. There isn't much we can do but log it and carry on. - */ -function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { - - if (!($node = node_load($nid))) { - watchdog('devshop', "Delete continue: nid $nid not found"); - return; - } - - if ($node->type == 'project') { - // First step complete: project deleted. Now we need to - // disable all of the sites, delete them. And then delete - // all of the platforms. - devshop_projects_project_delete_queue_next_task($node); - return; - } - else if (($node->type != 'site') && - ($node->type != 'platform')) { - // We only care about sites and platforms - return; - } - - - // Does this object belong to a devshop project? If not, - // then just ignore it. - if (!($node->project_nid)) { - return; - } - - // load project node - if (!($project_node = node_load($node->project_nid))) { - watchdog('devshop', - "Delete continue: can't load project nid $node->project_nid"); - return; - } - - $data = devshop_projects_project_data_get($project_node->nid); - - if (!$data['deleting_project']) { - return; - } - - if ($op == 'delete') { - // delete this object if the task just complete was a delete - db_query("DELETE FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d AND object_nid = %d", - $project_node->nid, $node->nid); - } - - // Remove this nid from our array of things to do - $key1 = "project_delete_{$node->type}_{$op}"; - $key2 = array_search($node->nid, $data[$key1]); - - if ($key2 === FALSE) { - watchdog('devshop', "search for nid {$node->nid} failed, key = $key2"); - } - else { - array_shift($data[$key1]); - if (count($data[$key1]) == 0) { - unset($data[$key1]); - } - devshop_projects_project_data_set($project_node->nid, $data); - } - - // do the next delete task - devshop_projects_project_delete_queue_next_task($project_node); -} - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task - * - */ -function devshop_projects_post_hosting_delete_task($task, $data) { - watchdog('devshop', "Delete post task hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'delete'); - -} - -/** - * Implementation of hook_hosting_TASK_TYPE_task_rollback(). - * - * This hook is invoked when the delete task fails. - * Carry on my wayward son. There will be peace when you are done. - * Lay your weary head to rest. Don't you cry no more. - */ -function devshop_projects_hosting_delete_task_rollback($task, $data) { - watchdog('devshop', "Delete task rollback hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); -} - -/** - * Implementation of hook_post_hosting_TASK_TYPE_task - * - */ -function devshop_projects_post_hosting_disable_task($task, $data) { - watchdog('devshop', "Disable post task hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'disable'); -} - -/** - * Implementation of hook_hosting_TASK_TYPE_task_rollback(). - * - * This hook is invoked when the disable task fails. But we must carry on! - */ -function devshop_projects_hosting_disable_task_rollback($task, $data) { - watchdog('devshop', "Disable task rollback hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); -} - - -/** - * Returns TRUE is the project create task for the given node failed. - */ -function devshop_project_project_create_failed($nid, &$task) { - - if ($nid) { - $task = hosting_get_most_recent_task($nid, 'devshop-create'); - if ($task && $task->task_status != 1) { - // Project Create failed. - return TRUE; - } + d()->setProperty('setProperty', 'works'); } - - // No task found OR it is successful - return FALSE; } diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 67d94d0fe..a541ad2e2 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -39,7 +39,6 @@ function devshop_projects_projects_view() { function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // If not ready yet: - if ($node->project_status != 'sites_ready'){ // Site install form $node->content['devshop']['install'] = array( @@ -62,7 +61,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#value' => filter_xss($node->code_path), '#weight' => -8 ); - + $url = 'http://' . $node->base_url; $node->content['info']['base_url'] = array( '#type' => 'item', @@ -78,7 +77,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#value' => strtr("", array('!url' => $node->git_url)), '#weight' => -10 ); - + if (!empty($node->install_profile)){ $node->content['info']['install_profile'] = array( '#type' => 'item', From 3bfc139b7b99e4994f5354388c999a2624e7b1e0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 10:51:30 -0500 Subject: [PATCH 0221/3476] renaming env_type environment --- devshop_projects/devshop_projects.install | 19 +++++++++++++++---- devshop_projects/inc/nodes.inc | 6 +++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 15cd472a9..76ceb6f5a 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -69,12 +69,12 @@ function devshop_projects_schema() { 'default' => '', 'description' => 'The node ID of the platform.', ), - 'env_type' => array( + 'environment' => array( 'type' => 'varchar', 'not null' => TRUE, 'length' => 10, 'default' => '', - 'description' => 'Environment type: dev, test, live, or sandbox.', + 'description' => 'Environment name. Either dev test live or platform name.', ), 'git_branch' => array( 'type' => 'varchar', @@ -92,7 +92,7 @@ function devshop_projects_schema() { /** * Implementation of hook_install(). */ -function devshop_projects_install() { +function devshop_projects_install() { // Create tables. drupal_install_schema('devshop_projects'); } @@ -100,7 +100,7 @@ function devshop_projects_install() { /** * Implementation of hook_uninstall(). */ -function devshop_projects_uninstall() { +function devshop_projects_uninstall() { // Delete tables. drupal_uninstall_schema('devshop_projects'); } @@ -142,3 +142,14 @@ function devshop_projects_update_3() { $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} ADD COLUMN git_branch VARCHAR(16) NULL"); return $ret; } + +/** + * Changes env_type to environment in {hosting_devshop_project_object}. + */ +function devshop_projects_update_4() { + $ret = array(); + $schema = devshop_projects_schema(); + $spec = $schema['hosting_devshop_project_object']['fields']['environment']; + db_change_field($ret, 'hosting_devshop_project_object', 'env_type', 'environment', $spec); + return $ret; +} diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 531f3e669..ac0e1b4cc 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -41,7 +41,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On Load: load this object's project and environment type if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, env_type AS environment, n.title as project, git_url, git_branch FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT project_nid, environment, n.title as project, git_url, git_branch FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); return $data; } @@ -75,7 +75,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $project = hosting_context_load($node->project); // Save to table - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, env_type, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); + db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, environment, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); } } } @@ -179,7 +179,7 @@ function devshop_projects_load($node) { $objects = array(); while($project_object = db_fetch_object($query)) { - $objects[$project_object->object_type][$project_object->object_nid] = $project_object->env_type; + $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; } $additions['project_objects'] = $objects; From d88e4676d9fee5dc23f5e461b3a99715de2b314e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 10:59:15 -0500 Subject: [PATCH 0222/3476] use environment, not branch! --- devshop_projects/inc/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 6025f12c6..9bc62f2b0 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -82,7 +82,7 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $platform->web_server = $server; $platform->git_branch = $branch; $platform->project = $project->title; - $platform->environment = $branch; + $platform->environment = $platform_name; watchdog('debug', 'Form state: ' . print_r($form_state['values'],1)); From 34a0dff3131eed99ff5420a48cd1ac2852174d6a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 11:36:20 -0500 Subject: [PATCH 0223/3476] moving pre hosting task hook to devshop hosting module --- devshop_projects/devshop_projects.drush.inc | 178 ++++++++++++++++++++ devshop_projects/inc/tasks.inc | 102 ----------- 2 files changed, 178 insertions(+), 102 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index a6b6b88e0..c17fb2c6d 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -4,6 +4,75 @@ * Keeping things organized. All code that is really run via drush commands * will go here. */ + + +/** + * Implements drush_HOOK_pre_COMMAND() + * + * This runs for each tasks during the command + * drush @hostmaster hosting-tasks + * + * NOTE: This ONLY runs when being called from a hostmaster task. + * This hook should ONLY be used to pass Options from a hostmaster task form to + * the $task object, or if you don't need this functionality from the command + * line. + */ +function drush_devshop_projects_pre_hosting_task() { + + // Verify Platform + $task =& drush_get_context('HOSTING_TASK'); + + // Verify Platform + // For our platforms, we have to clone it if it has a git_remote + // If it has a git branch, we should checkout as well. + if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->git_url)) { + + $platform = $task->ref; + $root = $platform->publish_path; + $git_remote = $platform->git_url; + $git_branch = $platform->git_branch; + + // Check if a repo exists + if (!is_dir($root) && !drush_shell_cd_and_exec($root, 'git status')){ + drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); + + // Build the command string + $command = "git clone $git_remote $root"; + if ($git_branch) { + $command .= " --branch $git_branch"; + } + } + // If the platform has been verified and has a branch and git url + else { + $root = $platform->publish_path; + $git_remote = $platform->git_url; + $git_branch = $platform->git_branch; + + // Build the command string + $command = "git checkout $git_branch"; + } + + // Execute + if (!empty($command)){ + drush_log('[DEVSHOP] Running: ' . $command); + + // @TODO: Create a d()->server->shell_cd_and_exec() function + // server->shell_exec() uses escapeshellcmd(), so we cannot cd_and_exec! + // So instead, this is the code from d()->server->shell_exec + // d()->server->shell_exec($cmd); + if (provision_is_local_host(d()->server->remote_host)) { + return drush_shell_cd_and_exec($root, escapeshellcmd($command)); + } + else { + return drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); + } + + $output = drush_shell_exec_output(); + drush_log('Shell Output: ' . implode("\n", $output) , 'notice'); + } + } +} + /** * Implementation of hook_post_hosting_TASK_TYPE_task * @@ -49,8 +118,117 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // live. Let's create a site based off of this platform. drush_log('[DEVSHOP] Platform verified. Creating your site.'); + + $site_node = devshop_projects_create_site($project, $platform, $platform->environment); drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); } + +/** + * Implements hook_hosting_project_context_options() + * + * This transfers data from the node to thes aegir context object (the alias!) + * For project entities. This is where we find the branches and tags on the remote. + */ +function devshop_projects_hosting_project_context_options(&$task) { + + drush_log('[DEVSHOP] devshop_projects_hosting_project_context_options()', 'ok'); + $task->context_options['server'] = '@server_master'; + $task->context_options['project_name'] = $task->ref->title; + $task->context_options['install_profile'] = $task->ref->install_profile; + $task->context_options['base_url'] = $task->ref->base_url; + $task->context_options['code_path'] = trim($task->ref->code_path, " "); + $task->context_options['git_url'] = $task->ref->git_url; + + $branches = getBranchesAndTags($task->ref->git_url); + + $task->ref->git_branches = $branches['branches']; + $task->ref->git_tags = $branches['tags']; + + // @TODO: Fix this infinite verification! + // node_save($task->ref); + + $task->context_options['git_branches'] = $branches['branches']; + $task->context_options['git_tags'] = $branches['tags']; +} + +/** + * Helpfer for getting branches and tags from a git URL + */ +function getBranchesAndTags($git_url = NULL){ + if (is_null($git_url)){ + $git_url = drush_get_option('git_url'); + } + $command = "git ls-remote {$git_url}"; + drush_shell_exec($command); + $exec_output = drush_shell_exec_output(); + + drush_log('[DEVSHOP] running '.$command, 'ok'); + drush_log('[DEVSHOP] ' . implode("\n", $exec_output), 'ok'); + + // Check for Permission Denied + // @TODO: Provide link to the Public key for the server. + if ('Permission denied' == substr($exec_output[0], 0, 17)){ + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error:'). implode("\n", $exec_output), 'error'); + } + + // If remote list is empty, something else went wrong. + if (count($exec_output) == 1 && empty($exec_output[0])){ + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('Something went wrong. Check the git URL and try again.'), 'error'); + return; + } + + // Build tag and branch list + $branches = array(); + $tags = array(); + + foreach ($exec_output AS $line_string){ + // @TODO: Would love some regex love here + // Example remote line: + // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 + $line = explode(" ", $line_string); + $ref = explode("/", $line[1]); + + $branch = array_pop($ref); + + if ($ref[1] == 'heads') { + $branches[] = $branch; + } else if ($ref[1] == 'tags') { + $tags[] = $branch; + } + } + drush_log(dt('[DEVSHOP] Found !b branches and !t tags.', array('!b' => count($branches), '!t' => count($tags), )), 'ok'); + return array('branches' => $branches, 'tags' => $tags); +} + +/** + * Implements hook_hosting_site_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For site entities. + */ +function devshop_projects_hosting_site_context_options(&$task) { + $task->context_options['project'] = $task->ref->project; + $task->context_options['nerd'] = 'vision'; +} + +/** + * Implements hook_hosting_site_context_options() + * + * This transfers data from the node to the aegir context object (the alias!) + * For site entities. + */ +function devshop_projects_hosting_platform_context_options(&$task) { + if (!empty($task->ref->project)){ + $task->context_options['project'] = $task->ref->project; + $task->properties['task properties'] = 'works'; + $task->ref->properties['task ref properties'] = 'works'; + + d()->setProperty('setProperty', 'works'); + + // @TODO: none of these work --^ + } +} + diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 9bc62f2b0..62d78ce95 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -92,105 +92,3 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { } -/** - * Implements hook_hosting_project_context_options() - * - * This transfers data from the node to thes aegir context object (the alias!) - * For project entities. This is where we find the branches and tags on the remote. - */ -function devshop_projects_hosting_project_context_options(&$task) { - - drush_log('[DEVSHOP] devshop_projects_hosting_project_context_options()', 'ok'); - $task->context_options['server'] = '@server_master'; - $task->context_options['project_name'] = $task->ref->title; - $task->context_options['install_profile'] = $task->ref->install_profile; - $task->context_options['base_url'] = $task->ref->base_url; - $task->context_options['code_path'] = trim($task->ref->code_path, " "); - $task->context_options['git_url'] = $task->ref->git_url; - - $branches = getBranchesAndTags($task->ref->git_url); - - $task->ref->git_branches = $branches['branches']; - $task->ref->git_tags = $branches['tags']; - node_save($task->ref); - - $task->context_options['git_branches'] = $branches['branches']; - $task->context_options['git_tags'] = $branches['tags']; -} - -/** - * Helpfer for getting branches and tags from a git URL - */ -function getBranchesAndTags($git_url = NULL){ - if (is_null($git_url)){ - $git_url = drush_get_option('git_url'); - } - $command = "git ls-remote {$git_url}"; - drush_shell_exec($command); - $exec_output = drush_shell_exec_output(); - - drush_log('[DEVSHOP] running '.$command, 'ok'); - drush_log('[DEVSHOP] ' . implode("\n", $exec_output), 'ok'); - - // Check for Permission Denied - // @TODO: Provide link to the Public key for the server. - if ('Permission denied' == substr($exec_output[0], 0, 17)){ - drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error:'). implode("\n", $exec_output), 'error'); - } - - // If remote list is empty, something else went wrong. - if (count($exec_output) == 1 && empty($exec_output[0])){ - drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('Something went wrong. Check the git URL and try again.'), 'error'); - return; - } - - // Build tag and branch list - $branches = array(); - $tags = array(); - - foreach ($exec_output AS $line_string){ - // @TODO: Would love some regex love here - // Example remote line: - // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 - $line = explode(" ", $line_string); - $ref = explode("/", $line[1]); - - $branch = array_pop($ref); - - if ($ref[1] == 'heads') { - $branches[] = $branch; - } else if ($ref[1] == 'tags') { - $tags[] = $branch; - } - } - drush_log(dt('[DEVSHOP] Found !b branches and !t tags.', array('!b' => count($branches), '!t' => count($tags), )), 'ok'); - return array('branches' => $branches, 'tags' => $tags); -} - -/** - * Implements hook_hosting_site_context_options() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_site_context_options(&$task) { - $task->context_options['project'] = $task->ref->project; - $task->context_options['nerd'] = 'vision'; -} - -/** - * Implements hook_hosting_site_context_options() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_platform_context_options(&$task) { - if (!empty($task->ref->project)){ - $task->context_options['project'] = $task->ref->project; - $task->properties['task properties'] = 'works'; - $task->ref->properties['task ref properties'] = 'works'; - - d()->setProperty('setProperty', 'works'); - } -} - From d0fc2efb2b9de05992ca318c208ac384b115a1ec Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 11:38:23 -0500 Subject: [PATCH 0224/3476] found it! verify updates the node, which triggers a verify! --- devshop_projects/inc/nodes.inc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index ac0e1b4cc..34603fbdf 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -131,18 +131,6 @@ function devshop_projects_update($node) { "WHERE nid = %d", $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, serialize($data), $node->nid); - - // Create hostmaster task - hosting_add_task($node->nid, 'verify'); - - // - //// If this is a rerry, kick start another devshop-create - //if (isset($node->retry)) { - // $args = array('retry' => 1); - // - // // Create hostmaster task - // hosting_add_task($node->nid, 'devshop-create', $args); - //} } /** From 58d235a9260aa4a1f586209114582d3b4da555ac Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 11:57:23 -0500 Subject: [PATCH 0225/3476] fixing getBranchesAndTags --- devshop_projects/devshop_projects.drush.inc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index c17fb2c6d..1ee2b7b61 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -134,7 +134,6 @@ function devshop_projects_post_hosting_verify_task($task, $data) { */ function devshop_projects_hosting_project_context_options(&$task) { - drush_log('[DEVSHOP] devshop_projects_hosting_project_context_options()', 'ok'); $task->context_options['server'] = '@server_master'; $task->context_options['project_name'] = $task->ref->title; $task->context_options['install_profile'] = $task->ref->install_profile; @@ -147,8 +146,9 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->ref->git_branches = $branches['branches']; $task->ref->git_tags = $branches['tags']; - // @TODO: Fix this infinite verification! - // node_save($task->ref); + // Save the project node now that we have branches and tags. + drush_log('[DEVSHOP] Trying to node save ' . $task->ref->title); + node_save($task->ref); $task->context_options['git_branches'] = $branches['branches']; $task->context_options['git_tags'] = $branches['tags']; @@ -172,6 +172,7 @@ function getBranchesAndTags($git_url = NULL){ // @TODO: Provide link to the Public key for the server. if ('Permission denied' == substr($exec_output[0], 0, 17)){ drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error:'). implode("\n", $exec_output), 'error'); + return; } // If remote list is empty, something else went wrong. @@ -185,12 +186,12 @@ function getBranchesAndTags($git_url = NULL){ $tags = array(); foreach ($exec_output AS $line_string){ + // @TODO: Would love some regex love here // Example remote line: // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 - $line = explode(" ", $line_string); - $ref = explode("/", $line[1]); - + $line = trim(substr($line_string, 40)); + $ref = explode("/", $line); $branch = array_pop($ref); if ($ref[1] == 'heads') { From cda08ca1761d9714977ed195e1bb3be7aa9d3ad7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 12:08:39 -0500 Subject: [PATCH 0226/3476] fixing bad merge --- devshop_projects/inc/tasks.inc | 291 --------------------------------- 1 file changed, 291 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index c23bff3c0..ea2dcad71 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -61,294 +61,3 @@ function hosting_task_devshop_create_form($node) { $form['#submit'][] = 'hosting_task_devshop_create_form_submit'; return $form; } - -/** - * Extra submit function for hosting_task_confirm_form() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_platform_context_options(&$task) { - if (!empty($task->ref->project)){ - $task->context_options['project'] = $task->ref->project; - $task->properties['task properties'] = 'works'; - $task->ref->properties['task ref properties'] = 'works'; - - d()->setProperty('setProperty', 'works'); - } -} - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task - * - * If a new platform has been added to a project and the env - * is not dev, test, or live, auto-create a site.... - * - * - * @TODO: This is used only for creating the new site when a "branch platform" - * is created... we should save something in the task or platform node so we - * don't have to use this logic. - */ -function devshop_projects_post_hosting_verify_task($task, $data) { - - // We only case about platforms. - if ($task->ref->type != 'platform') { - return; - } - - // Get objects - $nid = $task->ref->nid; - $platform = node_load($nid); - - // If this platform isn't in a project, bail. - if (empty($platform->project_nid)){ - return; - } - - // Get the project - $project = node_load($platform->project_nid); - - // If the project doesn't have an install profile chosen yet, bail. - if (empty($project->install_profile)){ - return; - } - - // If the project has a site already - $sites = array_flip($project->project_objects['site']); - if (isset($sites[$platform->project_environment])){ - return; - } - - // So this is a platform which is in a project and it not dev, test, or - // live. Let's create a site based off of this platform. - devshop_projects_create_site($project, $platform, $platform->project_environment); -} - -function watchcat($m, $l) { - $f = fopen("/tmp/{$m}.catlog", "a+"); - $cr = ($l[strlen($l) - 1] == '\n') ? true : false; - fwrite($f, sprintf("%s %s%s", date('Y-m-d H:i:s'), $l, $cr ? "" : "\n")); - fclose($f); -} - - -/* - * This function checks to see if there if there are any more objects - * to disable or delete during the 'project delete' task. The list of - * tasks that need to be done are kept in the project data field. See the - * following two function for more detail on those arrays: - * - * devshop_projects_project_delete_form_submit - * devshop_projects_project_delete_continue - * - * @param $pnode - * The project node that is to be deleted. - * - */ -function devshop_projects_project_delete_queue_next_task($pnode) { - // Assume all done - $updated = FALSE; - $nid = FALSE; - $data = devshop_projects_project_data_get($pnode->nid); - - // Keys to tasks to be performed. Objects must be processed in this - // order due to task dependencies. - $keys = array(array('type' => 'site', - 'task' => 'disable'), - array('type' => 'site', - 'task' => 'delete'), - array('type' => 'platform', - 'task' => 'delete')); - - // Iterate through arrays of tasks => nids and queue one tasks that - // is left to do - - foreach ($keys as $oper) { - $key1 = "project_delete_{$oper['type']}_{$oper['task']}"; - - if (isset($data[$key1])) { - if (count($data[$key1]) > 0) { - // Use the first nid in the list and stop looking for any more work - $nid = $data[$key1][0]; - break; - } - else { - // Array is empty, delete it - unset($data[$key1]); - } - $updated = TRUE; - } - - } - - // If $nid != FALSE, then we need to act on that node. If it is FALSE, - // then we are all done. We need to set the project node status - // field to 0 and delete that row from the hosting_devshop_project - // table - - if ($nid) { - hosting_add_task($nid, $oper['task']); - } - - // If we changed the project data array in any way, we need to save it - if ($updated) { - devshop_projects_project_data_set($pnode->nid, $data); - } - - if (!$nid) { - $pnode->status = 0; - - //$pnode->rid = 0; - node_save($pnode); - - // Check to see if the user wanted the project directory removed. - // If so, make sure the directory path is valid and isn't "/"!! - if ($data['deleting_project_remove_dir'] && - strlen($pnode->code_path) > 1) { - @exec("rm -rf {$pnode->code_path}"); - } - - // delete this project - db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", - $pnode->nid); - } -} - -/* - * This function get called when we are in the process of deleting a - * project and all of its related sites & platforms. It is invoked - * by the one of the hook_post_hosting_TASK_TYPE_task() hook when - * a site disable or site/platform delete tasks complete. - * - * All of the sites that we need to disable and delete and all of the - * platforms we need to delete are kept in the project node's - * $node->data field. There are four elements in that array: - * - * - * project data['deleting_project']: if TRUE, we are in the process - * of delete this project and all sites & platforms. - * project data['project_delete_site_disable']: array of site NID's - * that are to be disabled. - * project data['project_delete_site_delete']: an array of site NID's - * that are to be deleted once they are disabled. - * project data['project_delete_platform_delete']: an array of platform - * NID's that are to be deleted once the sites based on these platforms - * are disabled and deleted. - * - * We first disable all of the sites, then we delete them. Next we delete - * all of the platforms. As soon as a site or platform is deleted, we remove - * the NID from the corresponding array. When everything is deleted, we - * remove the rows from the object table. Hostmaster will remove them from - * the hosting context table during the delete task. - * - * @param $nid - * The NID of the object which the task just completed on. It can be - * the project node (when the actual project is being deleted). The - * site site node (when the site was disabled or deleted), or the - * platform node (when the platform was deleted). - * @param $op - * The operation (task type) that just complted. Will be one of: - * 'project delete', 'site disable', 'site delete', or - * 'platform delete'. - * @param $failed - * If TRUE, this task did not complete succesfully. We know this - * the 'rollback' hook was invoked rather then the 'post task' - * hook. There isn't much we can do but log it and carry on. - */ -function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { - - if (!($node = node_load($nid))) { - watchdog('devshop', "Delete continue: nid $nid not found"); - return; - } - - if ($node->type == 'project') { - // First step complete: project deleted. Now we need to - // disable all of the sites, delete them. And then delete - // all of the platforms. - devshop_projects_project_delete_queue_next_task($node); - return; - } - else if (($node->type != 'site') && - ($node->type != 'platform')) { - // We only care about sites and platforms - return; - } - - - // Does this object belong to a devshop project? If not, - // then just ignore it. - if (!($node->project_nid)) { - return; - } - - // load project node - if (!($project_node = node_load($node->project_nid))) { - watchdog('devshop', - "Delete continue: can't load project nid $node->project_nid"); - return; - } - - $data = devshop_projects_project_data_get($project_node->nid); - - if (!$data['deleting_project']) { - return; - } - - if ($op == 'delete') { - // delete this object if the task just complete was a delete - db_query("DELETE FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d AND object_nid = %d", - $project_node->nid, $node->nid); - } - - // Remove this nid from our array of things to do - $key1 = "project_delete_{$node->type}_{$op}"; - $key2 = array_search($node->nid, $data[$key1]); - - if ($key2 === FALSE) { - watchdog('devshop', "search for nid {$node->nid} failed, key = $key2"); - } - else { - array_shift($data[$key1]); - if (count($data[$key1]) == 0) { - unset($data[$key1]); - } - devshop_projects_project_data_set($project_node->nid, $data); - } - - // do the next delete task - devshop_projects_project_delete_queue_next_task($project_node); -} - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task ->>>>>>> 648db064d26aa789328bf198f3b3e2fe45fdf029 - * - * @see devshop_projects_form_alter(). We had to add the submit hadler there. - */ -function hosting_task_devshop_create_form_submit($form, &$form_state) { - - $project = node_load($form_state['values']['nid']); - $platform_name = $form_state['values']['parameters']['platform_name']; - $branch = $form_state['values']['parameters']['branch']; - $servers = hosting_get_servers('http'); - $server = variable_get('devshop_projects_default_web_server', key($servers)); - - // hosting_platform fields - $platform = new stdClass; - $platform->title = $project->title . '_' . $platform_name; - $platform->publish_path = $project->code_path . '/' . $platform_name; - $platform->web_server = $server; - $platform->git_branch = $branch; - $platform->project = $project->title; - $platform->environment = $platform_name; - - watchdog('debug', 'Form state: ' . print_r($form_state['values'],1)); - - watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); - - $platform_node = _devshop_projects_node_create('platform', $platform); - - -} From e34a0924ce965fb445f15a7bc80d538b2288ff5c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 12:11:19 -0500 Subject: [PATCH 0227/3476] Revert "fixing bad merge" This reverts commit cda08ca1761d9714977ed195e1bb3be7aa9d3ad7. --- devshop_projects/inc/tasks.inc | 291 +++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index ea2dcad71..c23bff3c0 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -61,3 +61,294 @@ function hosting_task_devshop_create_form($node) { $form['#submit'][] = 'hosting_task_devshop_create_form_submit'; return $form; } + +/** + * Extra submit function for hosting_task_confirm_form() + * + * This transfers data from the node to the aegir context object (the alias!) + * For site entities. + */ +function devshop_projects_hosting_platform_context_options(&$task) { + if (!empty($task->ref->project)){ + $task->context_options['project'] = $task->ref->project; + $task->properties['task properties'] = 'works'; + $task->ref->properties['task ref properties'] = 'works'; + + d()->setProperty('setProperty', 'works'); + } +} + +/* + * Implementation of hook_post_hosting_TASK_TYPE_task + * + * If a new platform has been added to a project and the env + * is not dev, test, or live, auto-create a site.... + * + * + * @TODO: This is used only for creating the new site when a "branch platform" + * is created... we should save something in the task or platform node so we + * don't have to use this logic. + */ +function devshop_projects_post_hosting_verify_task($task, $data) { + + // We only case about platforms. + if ($task->ref->type != 'platform') { + return; + } + + // Get objects + $nid = $task->ref->nid; + $platform = node_load($nid); + + // If this platform isn't in a project, bail. + if (empty($platform->project_nid)){ + return; + } + + // Get the project + $project = node_load($platform->project_nid); + + // If the project doesn't have an install profile chosen yet, bail. + if (empty($project->install_profile)){ + return; + } + + // If the project has a site already + $sites = array_flip($project->project_objects['site']); + if (isset($sites[$platform->project_environment])){ + return; + } + + // So this is a platform which is in a project and it not dev, test, or + // live. Let's create a site based off of this platform. + devshop_projects_create_site($project, $platform, $platform->project_environment); +} + +function watchcat($m, $l) { + $f = fopen("/tmp/{$m}.catlog", "a+"); + $cr = ($l[strlen($l) - 1] == '\n') ? true : false; + fwrite($f, sprintf("%s %s%s", date('Y-m-d H:i:s'), $l, $cr ? "" : "\n")); + fclose($f); +} + + +/* + * This function checks to see if there if there are any more objects + * to disable or delete during the 'project delete' task. The list of + * tasks that need to be done are kept in the project data field. See the + * following two function for more detail on those arrays: + * + * devshop_projects_project_delete_form_submit + * devshop_projects_project_delete_continue + * + * @param $pnode + * The project node that is to be deleted. + * + */ +function devshop_projects_project_delete_queue_next_task($pnode) { + // Assume all done + $updated = FALSE; + $nid = FALSE; + $data = devshop_projects_project_data_get($pnode->nid); + + // Keys to tasks to be performed. Objects must be processed in this + // order due to task dependencies. + $keys = array(array('type' => 'site', + 'task' => 'disable'), + array('type' => 'site', + 'task' => 'delete'), + array('type' => 'platform', + 'task' => 'delete')); + + // Iterate through arrays of tasks => nids and queue one tasks that + // is left to do + + foreach ($keys as $oper) { + $key1 = "project_delete_{$oper['type']}_{$oper['task']}"; + + if (isset($data[$key1])) { + if (count($data[$key1]) > 0) { + // Use the first nid in the list and stop looking for any more work + $nid = $data[$key1][0]; + break; + } + else { + // Array is empty, delete it + unset($data[$key1]); + } + $updated = TRUE; + } + + } + + // If $nid != FALSE, then we need to act on that node. If it is FALSE, + // then we are all done. We need to set the project node status + // field to 0 and delete that row from the hosting_devshop_project + // table + + if ($nid) { + hosting_add_task($nid, $oper['task']); + } + + // If we changed the project data array in any way, we need to save it + if ($updated) { + devshop_projects_project_data_set($pnode->nid, $data); + } + + if (!$nid) { + $pnode->status = 0; + + //$pnode->rid = 0; + node_save($pnode); + + // Check to see if the user wanted the project directory removed. + // If so, make sure the directory path is valid and isn't "/"!! + if ($data['deleting_project_remove_dir'] && + strlen($pnode->code_path) > 1) { + @exec("rm -rf {$pnode->code_path}"); + } + + // delete this project + db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", + $pnode->nid); + } +} + +/* + * This function get called when we are in the process of deleting a + * project and all of its related sites & platforms. It is invoked + * by the one of the hook_post_hosting_TASK_TYPE_task() hook when + * a site disable or site/platform delete tasks complete. + * + * All of the sites that we need to disable and delete and all of the + * platforms we need to delete are kept in the project node's + * $node->data field. There are four elements in that array: + * + * + * project data['deleting_project']: if TRUE, we are in the process + * of delete this project and all sites & platforms. + * project data['project_delete_site_disable']: array of site NID's + * that are to be disabled. + * project data['project_delete_site_delete']: an array of site NID's + * that are to be deleted once they are disabled. + * project data['project_delete_platform_delete']: an array of platform + * NID's that are to be deleted once the sites based on these platforms + * are disabled and deleted. + * + * We first disable all of the sites, then we delete them. Next we delete + * all of the platforms. As soon as a site or platform is deleted, we remove + * the NID from the corresponding array. When everything is deleted, we + * remove the rows from the object table. Hostmaster will remove them from + * the hosting context table during the delete task. + * + * @param $nid + * The NID of the object which the task just completed on. It can be + * the project node (when the actual project is being deleted). The + * site site node (when the site was disabled or deleted), or the + * platform node (when the platform was deleted). + * @param $op + * The operation (task type) that just complted. Will be one of: + * 'project delete', 'site disable', 'site delete', or + * 'platform delete'. + * @param $failed + * If TRUE, this task did not complete succesfully. We know this + * the 'rollback' hook was invoked rather then the 'post task' + * hook. There isn't much we can do but log it and carry on. + */ +function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { + + if (!($node = node_load($nid))) { + watchdog('devshop', "Delete continue: nid $nid not found"); + return; + } + + if ($node->type == 'project') { + // First step complete: project deleted. Now we need to + // disable all of the sites, delete them. And then delete + // all of the platforms. + devshop_projects_project_delete_queue_next_task($node); + return; + } + else if (($node->type != 'site') && + ($node->type != 'platform')) { + // We only care about sites and platforms + return; + } + + + // Does this object belong to a devshop project? If not, + // then just ignore it. + if (!($node->project_nid)) { + return; + } + + // load project node + if (!($project_node = node_load($node->project_nid))) { + watchdog('devshop', + "Delete continue: can't load project nid $node->project_nid"); + return; + } + + $data = devshop_projects_project_data_get($project_node->nid); + + if (!$data['deleting_project']) { + return; + } + + if ($op == 'delete') { + // delete this object if the task just complete was a delete + db_query("DELETE FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d AND object_nid = %d", + $project_node->nid, $node->nid); + } + + // Remove this nid from our array of things to do + $key1 = "project_delete_{$node->type}_{$op}"; + $key2 = array_search($node->nid, $data[$key1]); + + if ($key2 === FALSE) { + watchdog('devshop', "search for nid {$node->nid} failed, key = $key2"); + } + else { + array_shift($data[$key1]); + if (count($data[$key1]) == 0) { + unset($data[$key1]); + } + devshop_projects_project_data_set($project_node->nid, $data); + } + + // do the next delete task + devshop_projects_project_delete_queue_next_task($project_node); +} + +/* + * Implementation of hook_post_hosting_TASK_TYPE_task +>>>>>>> 648db064d26aa789328bf198f3b3e2fe45fdf029 + * + * @see devshop_projects_form_alter(). We had to add the submit hadler there. + */ +function hosting_task_devshop_create_form_submit($form, &$form_state) { + + $project = node_load($form_state['values']['nid']); + $platform_name = $form_state['values']['parameters']['platform_name']; + $branch = $form_state['values']['parameters']['branch']; + $servers = hosting_get_servers('http'); + $server = variable_get('devshop_projects_default_web_server', key($servers)); + + // hosting_platform fields + $platform = new stdClass; + $platform->title = $project->title . '_' . $platform_name; + $platform->publish_path = $project->code_path . '/' . $platform_name; + $platform->web_server = $server; + $platform->git_branch = $branch; + $platform->project = $project->title; + $platform->environment = $platform_name; + + watchdog('debug', 'Form state: ' . print_r($form_state['values'],1)); + + watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); + + $platform_node = _devshop_projects_node_create('platform', $platform); + + +} From 1acd25fc63e89c5bbf21969a5acf785aaedc08df Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 12:12:28 -0500 Subject: [PATCH 0228/3476] Revert "merge fix" This reverts commit 59cd1bb8aa02c2d10eddf426d5ef87fb6c0c0b48, reversing changes made to 58d235a9260aa4a1f586209114582d3b4da555ac. --- devshop_projects/inc/tasks.inc | 260 --------------------------------- 1 file changed, 260 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index c23bff3c0..62d78ce95 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -64,266 +64,6 @@ function hosting_task_devshop_create_form($node) { /** * Extra submit function for hosting_task_confirm_form() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_platform_context_options(&$task) { - if (!empty($task->ref->project)){ - $task->context_options['project'] = $task->ref->project; - $task->properties['task properties'] = 'works'; - $task->ref->properties['task ref properties'] = 'works'; - - d()->setProperty('setProperty', 'works'); - } -} - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task - * - * If a new platform has been added to a project and the env - * is not dev, test, or live, auto-create a site.... - * - * - * @TODO: This is used only for creating the new site when a "branch platform" - * is created... we should save something in the task or platform node so we - * don't have to use this logic. - */ -function devshop_projects_post_hosting_verify_task($task, $data) { - - // We only case about platforms. - if ($task->ref->type != 'platform') { - return; - } - - // Get objects - $nid = $task->ref->nid; - $platform = node_load($nid); - - // If this platform isn't in a project, bail. - if (empty($platform->project_nid)){ - return; - } - - // Get the project - $project = node_load($platform->project_nid); - - // If the project doesn't have an install profile chosen yet, bail. - if (empty($project->install_profile)){ - return; - } - - // If the project has a site already - $sites = array_flip($project->project_objects['site']); - if (isset($sites[$platform->project_environment])){ - return; - } - - // So this is a platform which is in a project and it not dev, test, or - // live. Let's create a site based off of this platform. - devshop_projects_create_site($project, $platform, $platform->project_environment); -} - -function watchcat($m, $l) { - $f = fopen("/tmp/{$m}.catlog", "a+"); - $cr = ($l[strlen($l) - 1] == '\n') ? true : false; - fwrite($f, sprintf("%s %s%s", date('Y-m-d H:i:s'), $l, $cr ? "" : "\n")); - fclose($f); -} - - -/* - * This function checks to see if there if there are any more objects - * to disable or delete during the 'project delete' task. The list of - * tasks that need to be done are kept in the project data field. See the - * following two function for more detail on those arrays: - * - * devshop_projects_project_delete_form_submit - * devshop_projects_project_delete_continue - * - * @param $pnode - * The project node that is to be deleted. - * - */ -function devshop_projects_project_delete_queue_next_task($pnode) { - // Assume all done - $updated = FALSE; - $nid = FALSE; - $data = devshop_projects_project_data_get($pnode->nid); - - // Keys to tasks to be performed. Objects must be processed in this - // order due to task dependencies. - $keys = array(array('type' => 'site', - 'task' => 'disable'), - array('type' => 'site', - 'task' => 'delete'), - array('type' => 'platform', - 'task' => 'delete')); - - // Iterate through arrays of tasks => nids and queue one tasks that - // is left to do - - foreach ($keys as $oper) { - $key1 = "project_delete_{$oper['type']}_{$oper['task']}"; - - if (isset($data[$key1])) { - if (count($data[$key1]) > 0) { - // Use the first nid in the list and stop looking for any more work - $nid = $data[$key1][0]; - break; - } - else { - // Array is empty, delete it - unset($data[$key1]); - } - $updated = TRUE; - } - - } - - // If $nid != FALSE, then we need to act on that node. If it is FALSE, - // then we are all done. We need to set the project node status - // field to 0 and delete that row from the hosting_devshop_project - // table - - if ($nid) { - hosting_add_task($nid, $oper['task']); - } - - // If we changed the project data array in any way, we need to save it - if ($updated) { - devshop_projects_project_data_set($pnode->nid, $data); - } - - if (!$nid) { - $pnode->status = 0; - - //$pnode->rid = 0; - node_save($pnode); - - // Check to see if the user wanted the project directory removed. - // If so, make sure the directory path is valid and isn't "/"!! - if ($data['deleting_project_remove_dir'] && - strlen($pnode->code_path) > 1) { - @exec("rm -rf {$pnode->code_path}"); - } - - // delete this project - db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", - $pnode->nid); - } -} - -/* - * This function get called when we are in the process of deleting a - * project and all of its related sites & platforms. It is invoked - * by the one of the hook_post_hosting_TASK_TYPE_task() hook when - * a site disable or site/platform delete tasks complete. - * - * All of the sites that we need to disable and delete and all of the - * platforms we need to delete are kept in the project node's - * $node->data field. There are four elements in that array: - * - * - * project data['deleting_project']: if TRUE, we are in the process - * of delete this project and all sites & platforms. - * project data['project_delete_site_disable']: array of site NID's - * that are to be disabled. - * project data['project_delete_site_delete']: an array of site NID's - * that are to be deleted once they are disabled. - * project data['project_delete_platform_delete']: an array of platform - * NID's that are to be deleted once the sites based on these platforms - * are disabled and deleted. - * - * We first disable all of the sites, then we delete them. Next we delete - * all of the platforms. As soon as a site or platform is deleted, we remove - * the NID from the corresponding array. When everything is deleted, we - * remove the rows from the object table. Hostmaster will remove them from - * the hosting context table during the delete task. - * - * @param $nid - * The NID of the object which the task just completed on. It can be - * the project node (when the actual project is being deleted). The - * site site node (when the site was disabled or deleted), or the - * platform node (when the platform was deleted). - * @param $op - * The operation (task type) that just complted. Will be one of: - * 'project delete', 'site disable', 'site delete', or - * 'platform delete'. - * @param $failed - * If TRUE, this task did not complete succesfully. We know this - * the 'rollback' hook was invoked rather then the 'post task' - * hook. There isn't much we can do but log it and carry on. - */ -function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { - - if (!($node = node_load($nid))) { - watchdog('devshop', "Delete continue: nid $nid not found"); - return; - } - - if ($node->type == 'project') { - // First step complete: project deleted. Now we need to - // disable all of the sites, delete them. And then delete - // all of the platforms. - devshop_projects_project_delete_queue_next_task($node); - return; - } - else if (($node->type != 'site') && - ($node->type != 'platform')) { - // We only care about sites and platforms - return; - } - - - // Does this object belong to a devshop project? If not, - // then just ignore it. - if (!($node->project_nid)) { - return; - } - - // load project node - if (!($project_node = node_load($node->project_nid))) { - watchdog('devshop', - "Delete continue: can't load project nid $node->project_nid"); - return; - } - - $data = devshop_projects_project_data_get($project_node->nid); - - if (!$data['deleting_project']) { - return; - } - - if ($op == 'delete') { - // delete this object if the task just complete was a delete - db_query("DELETE FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d AND object_nid = %d", - $project_node->nid, $node->nid); - } - - // Remove this nid from our array of things to do - $key1 = "project_delete_{$node->type}_{$op}"; - $key2 = array_search($node->nid, $data[$key1]); - - if ($key2 === FALSE) { - watchdog('devshop', "search for nid {$node->nid} failed, key = $key2"); - } - else { - array_shift($data[$key1]); - if (count($data[$key1]) == 0) { - unset($data[$key1]); - } - devshop_projects_project_data_set($project_node->nid, $data); - } - - // do the next delete task - devshop_projects_project_delete_queue_next_task($project_node); -} - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task ->>>>>>> 648db064d26aa789328bf198f3b3e2fe45fdf029 * * @see devshop_projects_form_alter(). We had to add the submit hadler there. */ From 8bf3433580c4c05f5da8ea7448adf83cf072a8c9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 12:30:01 -0500 Subject: [PATCH 0229/3476] slight ui improvement --- devshop_projects/inc/ui.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index a541ad2e2..68dde9185 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -62,12 +62,11 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -8 ); - $url = 'http://' . $node->base_url; + $url = 'http://dev.' . $node->base_url; $node->content['info']['base_url'] = array( '#type' => 'item', - '#title' => t('Base URL'), + '#title' => t('Dev Site'), '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), - '#description' => t('A link to this page. @TODO!'), '#weight' => -10 ); From 7e24cd633d2729abbf68729377f0bcce8d3cf15c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 12:30:22 -0500 Subject: [PATCH 0230/3476] getting rid of delete project task until it works --- devshop_projects/devshop_projects.drush.inc | 1 + devshop_projects/inc/tasks.inc | 3 +++ 2 files changed, 4 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 1ee2b7b61..94e5216e5 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -147,6 +147,7 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->ref->git_tags = $branches['tags']; // Save the project node now that we have branches and tags. + drush_log('[DEBUG] $task->ref = ' . print_r($task->ref, 1)); drush_log('[DEVSHOP] Trying to node save ' . $task->ref->title); node_save($task->ref); diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 62d78ce95..200a64ac6 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -21,12 +21,15 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); + /* + @TODO: Refactor custom forms to work with hosting_confirm_form() $tasks['project']['delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); + */ return $tasks; } From 4e554ea8cf85a5676a5a1892db6ff8541eeccddf Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 14:18:52 -0500 Subject: [PATCH 0231/3476] cleaning up badly merged readme file --- README.txt | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/README.txt b/README.txt index 85884aee2..40f834274 100644 --- a/README.txt +++ b/README.txt @@ -26,11 +26,6 @@ for a fast way to install aegir. There is also a Vagrant project at http://drupal.org/project/aegir_up -<<<<<<< HEAD -Download devshop_provision with drush: - -$ drush dl devshop_provision -======= ------------------------- Install devshop_provision @@ -39,25 +34,11 @@ Much like hosting depends on provision, devshop_hosting depends on devshop_provi Download devshop_provision with drush: $ drush dl devshop_provision ->>>>>>> 68b63fe095343116ce462210c57825971a58b7f1 It will try to download it to /usr/share/drush/commands, but you can put it anywhere Drush command files can live. /var/aegir/.drush/commands is a good place because then you can update drush core without a hassle. -<<<<<<< HEAD - ---------------- -NOTE about Sync ---------------- -Currently the Sync command requires a special folder in the Aegir Backups directory on -BOTH servers: - -$ mkdir ~/backups/devshop-sync - -Unfortunately drush rsync cannot sync single files, as far as I know, so it syncs the -folder. -======= ------------------------- Install devshop_hosting @@ -72,14 +53,10 @@ have the latest commits) enabled devshop_pull $ drush @hostmaster en devshop_pull ->>>>>>> 68b63fe095343116ce462210c57825971a58b7f1 - ------------------------------------- Provision Commands & Hostmaster Tasks ------------------------------------- -<<<<<<< HEAD -======= Once enabled, Aegir Sites will have 3 new Tasks available to them. NOTE: Not all tasks should be run on certain sites. It is up to YOU to decide @@ -88,7 +65,6 @@ staging, testing, or development. Use these commands with caution. All tasks have specific permissions, so you can grant roles individual tasks. ->>>>>>> 68b63fe095343116ce462210c57825971a58b7f1 1. Pull Code | drush @alias provision-devshop-pull | drush @alias pdp This task pulls your code, runs new updates, reverts features, and clears caches. It can be used as a Deployment task, for test sites @@ -126,8 +102,4 @@ All tasks have specific permissions, so you can grant roles individual tasks. - Imports the SQL dump into @destination database. - (optionally) Runs update.php. - (optionally) Runs features-revert-all. -<<<<<<< HEAD - - (optionally) Clears all caches. -======= - (optionally) Clears all caches. ->>>>>>> 68b63fe095343116ce462210c57825971a58b7f1 From c0c77ba4e25ec47dfd509f3349b95961065ff4c1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 10 Dec 2012 14:19:19 -0500 Subject: [PATCH 0232/3476] cleaning up badly merged readme file --- README.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.txt b/README.txt index 40f834274..82c32bf11 100644 --- a/README.txt +++ b/README.txt @@ -1,16 +1,8 @@ -<<<<<<< HEAD -DevShop Provision ------------------------------------- -Tools for Aegir-powered development. - -This module provides the backend commands needed to -======= DevShop Hosting ------------------------------------ Tools for Aegir-powered development. This module provides the front-end commands needed to ->>>>>>> 68b63fe095343116ce462210c57825971a58b7f1 deploy and manage sites using the DevShop git and features based development workflow. From e173678df302976e0ed62895da99bde2184012b3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 10:44:41 -0500 Subject: [PATCH 0233/3476] only override task confirm form for create tasks --- devshop_projects/inc/forms.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 1019295b6..28f5a4e0e 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -23,7 +23,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } // On Hosting Task form - if ($form_id == 'hosting_task_confirm_form'){ + if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-create'){ array_unshift($form['#submit'], 'hosting_task_devshop_create_form_submit'); } } From 2aae584852622e16c0537d4d9712047d9bc5c802 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 10:54:28 -0500 Subject: [PATCH 0234/3476] fail if no remote information --- devshop_projects/devshop_projects.drush.inc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 94e5216e5..4454c670b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -163,11 +163,16 @@ function getBranchesAndTags($git_url = NULL){ $git_url = drush_get_option('git_url'); } $command = "git ls-remote {$git_url}"; - drush_shell_exec($command); - $exec_output = drush_shell_exec_output(); - drush_log('[DEVSHOP] running '.$command, 'ok'); - drush_log('[DEVSHOP] ' . implode("\n", $exec_output), 'ok'); + + if (drush_shell_exec($command)){ + $exec_output = drush_shell_exec_output(); + } else { + $exec_output = drush_shell_exec_output(); + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error retrieving remote information: ') . implode("\n", $exec_output), 'error'); + return; + } + // Check for Permission Denied // @TODO: Provide link to the Public key for the server. From 7644f958b3d555fe4aeffa498a33aa168ebed4f3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 11:03:21 -0500 Subject: [PATCH 0235/3476] handle bad project urls --- devshop_projects/inc/create-wizard.inc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 7b8b3a59a..ab9aa42f1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -200,13 +200,25 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { global $user; $project = &$form_state['project']; - $project->git_url = $form_state['values']['git_url']; - $project->title = $project_name = $form_state['values']['title']; + + // If the project already exists, and name or git url has changed... + // we should delete the old placeholder project and create a new one. + if ($project->project_nid && $project->git_url != $form_state['values']['git_url'] || $project->title != $project_name = $form_state['values']['title']) { + + // Create "delete" task + hosting_add_task($project->project_nid, 'delete'); + + // Reset project nid + $project->project_nid = NULL; + } if (empty($project->project_nid)){ // Create the project node now. We will update it with the chosen path. // This is so we can check for branches and save the hosting_context as soon // as possible. + $project->git_url = $form_state['values']['git_url']; + $project->title = $project_name = $form_state['values']['title']; + $node = new stdClass; $node->title = $project->title; $node->git_url = $project->git_url; From 873d21e505a908e56a548566714ea06da87394e9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 11:45:46 -0500 Subject: [PATCH 0236/3476] if project node verify fails, bring back to git url page. --- devshop_projects/inc/create-wizard.inc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index ab9aa42f1..c2b0818db 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -40,13 +40,24 @@ function devshop_projects_create_wizard($step = NULL){ $form_state['project'] = $project; // Particular overrides - if ($step == 'environments' && isset($project->nid)){ - $project_node = node_load($project->nid); + if ($step == 'environments' && isset($project->project_nid)){ + $project_node = node_load($project->project_nid); if (empty($project_node->git_branches)){ //$form_info['show back'] = FALSE; } } + // If project node is active... make sure it was verified. + $project_node = node_load($project->project_nid); + $tasks = hosting_task_fetch_tasks($project_node->nid); + if ($tasks['verify']['task_status'] === HOSTING_TASK_ERROR){ + drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); + // If not on the first step, go to it. + if ($step != current(array_keys($form_info['order']))){ + drupal_goto('node/add/project'); + } + } + // Generate our ctools form and output $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; From 38f337f19063a32123bf8736d0f1a7acd3199954 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 11:59:47 -0500 Subject: [PATCH 0237/3476] removing redirect for now --- devshop_projects/inc/forms.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 28f5a4e0e..2942687d0 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -175,7 +175,8 @@ function devshop_projects_install_sites_form($form_state, $project_node) { return $form; } elseif ($project_node->project_status == 'preparing-project') { - drupal_goto('node/add/project'); + // @TODO: Not yet, needs more logic + // drupal_goto('node/add/project'); } elseif ($project_node->project_status == 'sites-installed') { return; From c5452a0dc0b4db463b2194a2a708b0a5d428830b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:00:28 -0500 Subject: [PATCH 0238/3476] deleting project on cancel. Preventing changing of project title. --- devshop_projects/inc/create-wizard.inc | 37 +++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index c2b0818db..228f8973b 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -144,12 +144,16 @@ function devshop_projects_create_wizard_finish(&$form_state) { */ function devshop_projects_create_wizard_cancel(&$form_state) { // Update the cache with changes. + $project = &$form_state['project']; ctools_object_cache_clear('project', $form_state['cache name']); + + // Redirect to projects list $form_state['redirect'] = 'hosting/projects'; - // @TODO: If step 1 has been completed, we should run Delete on Project node. - // to get rid of drush alias context, files, everything that this form - // may have created. + // If we have a project node, create a "delete" hosting task + if (!empty($project->project_nid)){ + hosting_add_task($project->project_nid, 'delete'); + } } /** @@ -177,15 +181,30 @@ function devshop_project_create_step_git(&$form, &$form_state) { '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), '#default_value' => $project->git_url, ); + $form['title'] = array( '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository.'), - '#size' => 40, - '#default_value' => $project->title, - '#maxlength' => 255, + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository. NOTE: You cannot change this, but you can delete and start over.Choose wisely.'), + '#size' => 40, + '#default_value' => $project->title, + '#maxlength' => 255, ); + + // If there is already a title, make this an item. + // @TODO: Can we allow "renaming" of projects? (probably only at this stage). + if (!empty($project->title)) { + // Force title + $form['title']['#value'] = $form['title']['#default_value']; + $form['title']['#type'] = 'value'; + $form['title']['#description'] = t('You cannot change the name of your project once it has been created. If you must, click the "Cancel" button to delete this project, then create a new one.'); + + // Be nice and show it. + $form['title_display'] = $form['title']; + $form['title_display']['#type'] = 'item'; + + } } /** From eeaef880691ebcb14c856f57bdbf4e27f0a623b2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:15:20 -0500 Subject: [PATCH 0239/3476] allow updating the repo in the wizard --- devshop_projects/inc/create-wizard.inc | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 228f8973b..33ab83687 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -231,24 +231,23 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project = &$form_state['project']; - // If the project already exists, and name or git url has changed... - // we should delete the old placeholder project and create a new one. - if ($project->project_nid && $project->git_url != $form_state['values']['git_url'] || $project->title != $project_name = $form_state['values']['title']) { - - // Create "delete" task - hosting_add_task($project->project_nid, 'delete'); - - // Reset project nid - $project->project_nid = NULL; + // If the project already exists, and git url has changed... + if ($project->project_nid && $project->git_url != $form_state['values']['git_url']) { + drupal_set_message(t('You have changed the git URL. Please wait while we verify your remote and branches.')); + // Change the git url and save the node. Verification SHOULD run again. + $node = node_load($project->project_nid); + $node->git_url = $form_state['values']['git_url']; + node_save($node); } + // Now that we've compared old and new, set $project properties. + $project->git_url = $form_state['values']['git_url']; + $project->title = $project_name = $form_state['values']['title']; + if (empty($project->project_nid)){ // Create the project node now. We will update it with the chosen path. // This is so we can check for branches and save the hosting_context as soon // as possible. - $project->git_url = $form_state['values']['git_url']; - $project->title = $project_name = $form_state['values']['title']; - $node = new stdClass; $node->title = $project->title; $node->git_url = $project->git_url; From dc302099744f989d8e71ec3655989dc2b54b07d9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:15:44 -0500 Subject: [PATCH 0240/3476] properly trigger verification on project update --- devshop_projects/inc/nodes.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 34603fbdf..972791493 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -121,7 +121,6 @@ function devshop_projects_insert($node) { * */ function devshop_projects_update($node) { - $data = array(); $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; @@ -131,6 +130,10 @@ function devshop_projects_update($node) { "WHERE nid = %d", $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, serialize($data), $node->nid); + + if (!$node->no_verify) { + hosting_add_task($node->nid, 'verify'); + } } /** From 305a13a482434365ce1a3727bdd4cfb948771607 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:25:59 -0500 Subject: [PATCH 0241/3476] use project verification task status to check status --- devshop_projects/inc/create-wizard.inc | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 33ab83687..0ceed52a0 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -36,9 +36,6 @@ function devshop_projects_create_wizard($step = NULL){ ctools_object_cache_set('project', $form_state['cache name'], $project); } - // All forms can access $form_state['project']; - $form_state['project'] = $project; - // Particular overrides if ($step == 'environments' && isset($project->project_nid)){ $project_node = node_load($project->project_nid); @@ -47,10 +44,13 @@ function devshop_projects_create_wizard($step = NULL){ } } - // If project node is active... make sure it was verified. + // Check verification status $project_node = node_load($project->project_nid); $tasks = hosting_task_fetch_tasks($project_node->nid); - if ($tasks['verify']['task_status'] === HOSTING_TASK_ERROR){ + $project->verify_task_status = $tasks['verify']['task_status']; + + // If project verification failed, we need to ask for a new git url. + if ($project->verify_task_status === HOSTING_TASK_ERROR){ drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); // If not on the first step, go to it. if ($step != current(array_keys($form_info['order']))){ @@ -58,6 +58,10 @@ function devshop_projects_create_wizard($step = NULL){ } } + + // All forms can access $form_state['project']; + $form_state['project'] = $project; + // Generate our ctools form and output $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; @@ -233,11 +237,14 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { // If the project already exists, and git url has changed... if ($project->project_nid && $project->git_url != $form_state['values']['git_url']) { - drupal_set_message(t('You have changed the git URL. Please wait while we verify your remote and branches.')); // Change the git url and save the node. Verification SHOULD run again. + drupal_set_message(t('You have changed the git URL. Please wait while we verify your remote and branches.')); $node = node_load($project->project_nid); $node->git_url = $form_state['values']['git_url']; + $node->branches = array(); node_save($node); + + // Also clear the repo related info from $project } // Now that we've compared old and new, set $project properties. @@ -335,11 +342,11 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { */ function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; - $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); - $project_node = node_load($project->project_nid); - if (empty($project_node->git_branches)){ + $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); + + if ($project->verify_task_status = HOSTING_TASK_QUEUED || $project->verify_task_status = HOSTING_TASK_PROCESSING){ // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? From 35239959d621225e7bd402acfba1d0e26e9b4443 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:30:43 -0500 Subject: [PATCH 0242/3476] better logic! :D --- devshop_projects/inc/create-wizard.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 0ceed52a0..d00b19581 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -345,8 +345,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $project_node = node_load($project->project_nid); $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); - - if ($project->verify_task_status = HOSTING_TASK_QUEUED || $project->verify_task_status = HOSTING_TASK_PROCESSING){ + if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING){ // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? From f7c939f00d89a8bf18879bb317f81bb90082c9de Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:33:37 -0500 Subject: [PATCH 0243/3476] adding @TODO about cloning sites instead of installing over and over --- devshop_projects/inc/forms.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 2942687d0..7a6eb33f5 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -301,6 +301,8 @@ function devshop_projects_install_sites_form_submit(&$form, &$form_state){ db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); // Create the site nodes + // @TODO: Can we speed things up here by only running install for the first, + // then "Cloning" to create the rest? foreach ($project_node->project_objects['platform'] as $nid => $env){ devshop_projects_create_site($project_node, node_load($nid), $env); } From 59be782d497f4183f9b51c5b75dd177e81ac5437 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:45:53 -0500 Subject: [PATCH 0244/3476] fixing up form alters for create platform. No longer needs to add "create-platform" task, its just a pseudotask! --- devshop_projects/inc/forms.inc | 2 +- devshop_projects/inc/tasks.inc | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 7a6eb33f5..ad64739c3 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -24,7 +24,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // On Hosting Task form if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-create'){ - array_unshift($form['#submit'], 'hosting_task_devshop_create_form_submit'); + $form['#submit'] = array('hosting_task_devshop_create_form_submit'); } } diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 200a64ac6..89e1eaeef 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -61,7 +61,6 @@ function hosting_task_devshop_create_form($node) { '#default_value' => 1, ); */ - $form['#submit'][] = 'hosting_task_devshop_create_form_submit'; return $form; } @@ -91,7 +90,12 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); + // Create the platform node $platform_node = _devshop_projects_node_create('platform', $platform); - + // We are replacing hosting_confirm_form_submit here, so just do what it does, + // minus the hosting task creation! + $values = $form_state['values']; + $form_state['redirect'] = 'node/' . $values['nid']; + modalframe_close_dialog(); } From 94c7ee6499cb46c52e27e025fcac7290f4171648 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 12:58:51 -0500 Subject: [PATCH 0245/3476] fixing project node edit page and making the environments table nicer --- devshop_projects/inc/forms.inc | 28 ++++++++-------------------- devshop_projects/inc/ui.inc | 13 +++++-------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index ad64739c3..476b770e3 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -33,16 +33,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ */ function devshop_projects_form(&$node) { - $retry = devshop_project_project_create_failed($node->nid, $task); - if ($retry) { - $form['notice'] = array( - '#type' => 'item', - '#title' => t('NOTICE!'), - '#description' => t('Project Create failed! You can view the task log ' . l('here.', "node/$task->nid") . ' Please make any necessary corrections below and resubmit the form to try again.'), - '#weight' => -1, - ); - } - $form['git_url'] = array( '#type' => 'textfield', '#title' => t('Git URL'), @@ -90,18 +80,16 @@ function devshop_projects_form(&$node) { // Don't allow editing if ($node->nid) { - $form['code_path']['#value'] = $form['code_path']['#default_value']; - $form['code_path']['#disabled'] = TRUE; - - $form['title']['#value'] = $form['title']['#default_value']; - $form['title']['#disabled'] = TRUE; - - if (!$retry) { - $form['git_url']['#value'] = $form['git_url']['#default_value']; - $form['git_url']['#disabled'] = TRUE; + $locked = array('title', 'git_url', 'code_path', 'base_url'); + foreach ($locked as $field){ + $form[$field]['#value'] = $form[$field]['#default_value']; + $form[$field]['#type'] = 'value'; + + // Be nice and show it. + $form["{$field}_display"] = $form[$field]; + $form["{$field}_display"]['#type'] = 'item'; } } - return $form; } diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 68dde9185..f2ecd89b7 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -98,21 +98,18 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($site->site_status != -2) { $url = "http://$site->title"; $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); - $row[] = l('Site', "node/$site->nid"); + $row[] = l('Site Dashboard', "node/$site->nid"); } else { // Site if deleted, just show the platform name $row[] = $platform->title; $row[] = ''; } - $row[] = l('Platform', "node/$site->platform"); - if ($site->site_status != -2) { - $row[] = l('Download Database', "$url/admin/status/"); - } - else { - $row[] = ''; + $row[] = l('Platform Dashboard', "node/$site->platform"); + + if (module_exists('devshop_log')){ + $row[] = l('Commit Log', "node/$site->platform/gitlog"); } - $row[] = l('Commit Log', "node/$site->platform/gitlog"); $rows[] = $row; } $header = array(); From cc0ba4b924f1e1da4f61f236d629869615adca7f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 13:12:22 -0500 Subject: [PATCH 0246/3476] cleaning up devshop log --- devshop_log/devshop_log.module | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index b8ce5594e..ddac5be54 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -50,8 +50,8 @@ function devshop_log_menu() { $items['node/%/gitlog'] = array( 'title' => 'Commit Log', 'description' => 'View commit log entries', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('devshop_log_view_form', 1), + 'page callback' => 'devshop_log_page', + 'page arguments' => array(1), 'type' => MENU_LOCAL_TASK, 'access callback' => '_devshop_log_access_callback', 'access arguments' => array(1), @@ -65,22 +65,8 @@ function devshop_log_menu() { /** * View Log Page */ -function devshop_log_view_form($form, $nid) { - $form['devsop_log'] = array( - '#type' => 'item', - '#title' => '', - '#value' => t(''), - '#weight' => 0, - ); - $form['devsop_log']['commit_log'] = array( - '#type' => 'textarea', - '#title' => t('Git Commit Log'), - '#default_value' => t(_devshop_log_load($nid)), - '#attributes' => array('readonly' => 'readonly'), - '#rows' => 25, - ); - - return $form; +function devshop_log_page($nid) { + return check_markup(_devshop_log_load($nid)); } /** @@ -97,7 +83,7 @@ function devshop_git_log_settings() { '#type' => 'textfield', '#maxlength' => 5, '#title' => t('Max log entries to display'), - '#default_value' => variable_get('devshop_git_log_count', 10), + '#default_value' => variable_get('devshop_git_log_count', 10), '#weight' => 1, '#description' => t('Enter the maximum number of log entries to ' . 'display in the Git Commit Log View page. A ' . @@ -110,7 +96,7 @@ function devshop_git_log_settings() { /* * This is a hack which loads the git commit log for the given - * platform node. + * platform node. */ function _devshop_log_load($nid) { @@ -123,7 +109,7 @@ function _devshop_log_load($nid) { // directory before we fetch the log. Technically, I should be able to // use git's --work-tree option and not have to chdir, but for some // reason, it doesn't work. It works when I run the exact same command - // at the shell prompt, but doesn't work when I call git from PHP. + // at the shell prompt, but doesn't work when I call git from PHP. // Something to fix one day. $log_count = variable_get('devshop_git_log_count', 10); From 11e00ca92b14e35ec55481a9470d67bcb071b0be Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 13:14:11 -0500 Subject: [PATCH 0247/3476] cleanup comments --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index aa509fd36..c6c74ca60 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -42,7 +42,6 @@ function devshop_projects_menu() { // hosting tasks ajax pages. - // @TODO: This doesn't work and is making me very angry! foreach (hosting_available_tasks('project') as $task => $info){ $path = 'node/%/project_' . $task; $items[$path] = array( @@ -63,6 +62,7 @@ function devshop_projects_hosting_task_confirm_form_page($nid, $task){ $node = node_load($nid); return drupal_get_form('hosting_task_confirm_form', $node, $task); } + /** * Implementation of hook_menu_alter() * From dbadd1e2810a49f858eae464cb6fa2a3220e667d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 13:41:43 -0500 Subject: [PATCH 0248/3476] drastic improvements to the projects list page! --- devshop_projects/devshop_projects.module | 2 +- devshop_projects/inc/ui.inc | 44 ++++++++++++++++++++---- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index c6c74ca60..9f838b4c0 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -34,7 +34,7 @@ function devshop_projects_menu() { $items['hosting/projects'] = array( 'title' => 'Projects', 'description' => 'Display a list of all projects', - 'page callback' => 'devshop_projects_projects_view', + 'page callback' => 'devshop_projects_projects_page', 'access arguments' => array('view projects'), 'menu_name' => 'primary-links', 'weight' => 1, diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index f2ecd89b7..3fa2ef891 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -10,19 +10,51 @@ * @TODO: Should we make this a list of teasers? Hard to cram everything in * a table. */ -function devshop_projects_projects_view() { - - $header = array('Projects', 'Platforms', 'Sites'); +function devshop_projects_projects_page() { + + $header = array( + 'Name', + 'Profile', + 'Version', + '', + 'Git URL', + 'Dev Site', + ); $r = db_query("SELECT * FROM {hosting_devshop_project}"); $rows = array(); while(($proj = db_fetch_object($r))) { $node = node_load($proj->nid); + if (!empty($node->project_objects['platform'])){ + $platform_node = node_load(key($node->project_objects['platform'])); + } + $row = array(); - $row[] = l($node->title, "node/$proj->nid"); - $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='platform'", $node->nid)); - $row[] = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); + + // Link to Project page + $row[] = '' . l($node->title, "node/$proj->nid") . ''; + + // Install Profile + $row[] = $node->install_profile; + + // Drupal Version + $row[] = $platform_node->release->version; + + // Number of environments + $num = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); + $row[] = format_plural($num, t('1 site'), t('!num sites', array('!num' => $num))); + + + + // Git URL + $row[] = strtr("", array('!url' => $node->git_url)); + + // Link to Dev Site + $dev_site_url = url("http://dev." . $node->base_url, array('absolute' => TRUE)); + $row[] = l($dev_site_url, $dev_site_url); + + $rows[] = $row; } From 53d41b6e785946e1ec3fd0fe674c0320e9f63805 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 13:45:27 -0500 Subject: [PATCH 0249/3476] language and link options --- devshop_projects/inc/tasks.inc | 4 ++-- devshop_projects/inc/ui.inc | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 89e1eaeef..049c81488 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -16,8 +16,8 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', ); $tasks['project']['devshop-create'] = array( - 'title' => t('Create New Platform'), - 'description' => t('Creates a new platform within this project.'), + 'title' => t('Create New Site'), + 'description' => t('Creates a new site & platform within this project.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 3fa2ef891..5075a6902 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -31,6 +31,7 @@ function devshop_projects_projects_page() { } $row = array(); + $link_options = array('attributes' => array('target' => '_blank')); // Link to Project page $row[] = '' . l($node->title, "node/$proj->nid") . ''; @@ -52,7 +53,7 @@ function devshop_projects_projects_page() { // Link to Dev Site $dev_site_url = url("http://dev." . $node->base_url, array('absolute' => TRUE)); - $row[] = l($dev_site_url, $dev_site_url); + $row[] = l($dev_site_url, $dev_site_url, $link_options); $rows[] = $row; From 50e8ea50b9c7a733492d4a503c82449e2ff342ee Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 14:51:53 -0500 Subject: [PATCH 0250/3476] oops rollback --- devshop_projects/inc/forms.inc | 24 ++++++++++++++++-------- devshop_projects/inc/tasks.inc | 4 ++-- devshop_projects/inc/ui.inc | 14 +++++++++++++- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 476b770e3..c9c5de3eb 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -180,7 +180,7 @@ function devshop_projects_install_sites_form($form_state, $project_node) { // Display the platforms $rows = array(); - $header = array(t('Name'), t('Branch'), t('Status')); + $header = array(t('Name'), t('Branch'), t('Status'), t('Installation Profiles')); foreach ($project_node->project_objects['platform'] as $nid => $name){ $platform = node_load($nid); @@ -188,7 +188,6 @@ function devshop_projects_install_sites_form($form_state, $project_node) { $row[] = $name; $row[] = $platform->git_branch; $row[] = $platform->verified? t('Verified'): t('Verification Pending'); - $rows[] = $row; if (!$platform->verified){ $completed = FALSE; @@ -198,6 +197,8 @@ function devshop_projects_install_sites_form($form_state, $project_node) { // This fancy footwork gives us an array with items like 'shortname' => 'Name' $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); + $row[] = implode(', ', $profiles[$name]); + if (!isset($available_profiles)){ $available_profiles = $profiles[$name]; } else { @@ -211,8 +212,17 @@ function devshop_projects_install_sites_form($form_state, $project_node) { $defaults['dev']['dev_modules'] = 1; $defaults['test']['run_tests'] = 1; $defaults['live']['live_modules'] = 1; + + // Store rows for display + $rows[] = $row; } + // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" + $form['platforms'] = array( + '#type' => 'markup', + '#value' => theme('table', $header, $rows), + ); + // Provide something for people while they wait. if (!$completed){ $form['help'] = array( @@ -220,11 +230,14 @@ function devshop_projects_install_sites_form($form_state, $project_node) { '#value' => t('Please wait while we download and verify your drupal code.'), ); + return $form; + } elseif (count($available_profiles) == 0) { $form['error'] = array( '#type' => 'markup', - '#value' => t('WARNING: No profile was found in all of your platforms. Please check your branches and code and try again.'), + '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. This can happen if you chose branches with two different versions of Drupal. Please edit codebase so that your desired install profile is in all branches.'), ); + return $form; } else { // Install Profile // Sensible default? @@ -248,11 +261,6 @@ function devshop_projects_install_sites_form($form_state, $project_node) { ); } - // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" - $form['platforms'] = array( - '#type' => 'markup', - '#value' => theme('table', $header, $rows), - ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Create Sites'), diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 89e1eaeef..049c81488 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -16,8 +16,8 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', ); $tasks['project']['devshop-create'] = array( - 'title' => t('Create New Platform'), - 'description' => t('Creates a new platform within this project.'), + 'title' => t('Create New Site'), + 'description' => t('Creates a new site & platform within this project.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 3fa2ef891..5a50b4e3b 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -31,6 +31,7 @@ function devshop_projects_projects_page() { } $row = array(); + $link_options = array('attributes' => array('target' => '_blank')); // Link to Project page $row[] = '' . l($node->title, "node/$proj->nid") . ''; @@ -52,7 +53,7 @@ function devshop_projects_projects_page() { // Link to Dev Site $dev_site_url = url("http://dev." . $node->base_url, array('absolute' => TRUE)); - $row[] = l($dev_site_url, $dev_site_url); + $row[] = l($dev_site_url, $dev_site_url, $link_options); $rows[] = $row; @@ -109,6 +110,15 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -10 ); + if (!empty($node->git_branches)){ + $node->content['info']['git_branches'] = array( + '#type' => 'item', + '#title' => t('Remote Branches'), + '#value' => theme('item_list', $node->git_branches), + '#weight' => -8 + ); + } + if (!empty($node->install_profile)){ $node->content['info']['install_profile'] = array( '#type' => 'item', @@ -118,6 +128,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } + + $rows = array(); if (isset($node->project_objects['site'])) { foreach($node->project_objects['site'] as $nid => $env) { From 5d5d40a8cddbb54d608b2de0a876780f91b6fc4b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 14:54:28 -0500 Subject: [PATCH 0251/3476] fixing whitespace; --- devshop_projects/inc/nodes.inc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 972791493..71127e465 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -49,16 +49,16 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { if ($op == 'view'){ if (!empty($node->project)){ $node->content['info']['project'] = array( - '#type' => 'item', - '#title' => t('Project'), - '#value' => l($node->project, "node/{$node->project_nid}"), - '#weight' => -12, - ); - $node->content['info']['env'] = array( - '#type' => 'item', - '#title' => t('Environment Type'), - '#value' => $node->environment, - '#weight' => -11, + '#type' => 'item', + '#title' => t('Project'), + '#value' => l($node->project, "node/{$node->project_nid}"), + '#weight' => -12, + ); + $node->content['info']['env'] = array( + '#type' => 'item', + '#title' => t('Environment Type'), + '#value' => $node->environment, + '#weight' => -11, ); } } From b55a7f951f37511e0cc8d72f7848aa5dbbb13bd9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 14:55:13 -0500 Subject: [PATCH 0252/3476] adding branch to platform view --- devshop_projects/inc/nodes.inc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 71127e465..eeb403f92 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -56,10 +56,16 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { ); $node->content['info']['env'] = array( '#type' => 'item', - '#title' => t('Environment Type'), + '#title' => t('Environment'), '#value' => $node->environment, '#weight' => -11, ); + $node->content['info']['branch'] = array( + '#type' => 'item', + '#title' => t('Branch'), + '#value' => $node->git_branch, + '#weight' => -11, + ); } } From 9a7502254535e8673cdeaaee179f69e86e876aae Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 19:17:42 -0500 Subject: [PATCH 0253/3476] Major readme improvements --- README.txt | 108 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 36 deletions(-) diff --git a/README.txt b/README.txt index 82c32bf11..5666c5e73 100644 --- a/README.txt +++ b/README.txt @@ -1,51 +1,85 @@ + DevShop Hosting ------------------------------------- -Tools for Aegir-powered development. +=============== +Drupal development infrastructure made easy. + -This module provides the front-end commands needed to -deploy and manage sites using the DevShop git and features +This module provides the front-end commands needed to +deploy and manage sites using the DevShop git and features based development workflow. -------------------------------------- + Installation -------------------------------------- +------------ -DevShop assumes a base Aegir installation. -See http://community.aegirproject.org/handbook to learn about Aegir. +1. Install Aegir -Visit http://community.aegirproject.org/content/installing/automatic-installation-ubuntu-1104-quickstart -for a fast way to install aegir. + DevShop assumes a base Aegir installation. + See http://community.aegirproject.org/handbook to learn about Aegir. -There is also a Vagrant project at http://drupal.org/project/aegir_up + The most reliable and generally supported way to install aegir is with + the Debian packages. + See http://community.aegirproject.org/installing/debian for instructions on + installing in debian based systems like Ubuntu. Pay close attention to -------------------------- -Install devshop_provision +2. Install provision_git and devshop provision -Much like hosting depends on provision, devshop_hosting depends on devshop_provision. + $ drush dl devshop_provision provision_git + + In a typical debian environment, it will try to download it to + /usr/share/drush/commands, but you can put it anywhere Drush command files can live. + +3. Install devshop_hosting and enable desired modules. + + Download devshop_hosting and ctools into your @hostmaster site with drush: + $ drush @hostmaster dl devshop_hosting ctools + + Enable devshop_projects, devshop_tasks, devshop_log, and optionally, + devshop_tasks and devshop_tests: + + $ drush @hostmaster en devshop_projects devshop_tasks devshop_log devshop_tests + +NOTE: The project creation wizard requires +certain Hosting Tasks to run before later steps can be completed. For this +reason, it really helps to have a very fast Hosting Queue, or to install the Hosting Queue Runner project (http://drupal.org/project/hosting_queue_runner) so that tasks fire as quickly as possible. + +Usage +----- + +DevShop functionality centers around "Projects". Aegir Project nodes store a Git URL, the code path, the "base url", and the branches of the remote repository. + +DevShop allows multiple platforms and sites (for dev, test, or live purposes) +to be created very easily. Platforms can be easily created from existing +branches of your git repositories. + +To create a new project, visit either the Projects page or click "Create Content" > "DevShop Project". + +### Step 1: Git URL and project name. + +Enter your project's Git URL and Project Name. + +NOTE: Your project's git repo must be a complete drupal core file set. It +should match the structure of Drupal core's git repository, and can be a clone +of http://git.drupalcode.org/project/drupal.git + +### Step 2: File Path and Base URL + +Enter the base path to the Project's code. Recommended is /var/aegir/projects + +Enter the base URL for the project. All Project Sites will be on a subdomain of this base URL. + +### Step 3: Choose Platforms + +To complete this step, the Verify Project task must finish. + +On this page, you choose if you want dev, test, or live platforms and what branches each should live on. You can also choose the branches of your git repository you wish to create platforms and sites for. -Download devshop_provision with drush: - $ drush dl devshop_provision -It will try to download it to /usr/share/drush/commands, but you can put it -anywhere Drush command files can live. /var/aegir/.drush/commands is a good -place because then you can update drush core without a hassle. -------------------------- -Install devshop_hosting -Download devshop_hosting into your @hostmaster site with drush: - $ drush @hostmaster dl devshop_hosting - -Enable devshop_tasks with drush. Unless you like visiting your admin pages. - $ drush @hostmaster en devshop_tasks - -If you need a pull queue (ie. this is hosting a shared Dev site that must always -have the latest commits) enabled devshop_pull - $ drush @hostmaster en devshop_pull -------------------------------------- Provision Commands & Hostmaster Tasks ------------------------------------- @@ -55,12 +89,14 @@ NOTE: Not all tasks should be run on certain sites. It is up to YOU to decide where and when to run these tasks. DevShop is NOT aware of which site is live, staging, testing, or development. Use these commands with caution. +@TODO: More constrained tasks are being created as tasks on projects. + All tasks have specific permissions, so you can grant roles individual tasks. 1. Pull Code | drush @alias provision-devshop-pull | drush @alias pdp This task pulls your code, runs new updates, reverts features, and clears caches. It can be used as a Deployment task, for test sites - + - Git Pull the code for your site's platform. - Then, all optionally: - Run update.php. @@ -70,22 +106,22 @@ All tasks have specific permissions, so you can grant roles individual tasks. 2. Commit Features | drush @alias provision-devshop-commit | drush @alias pdc This task integrates with Features.module to make it very easy to recreate and commit your features - + - Calls drush features-update-all - Commits the result, with a part automated and part customized commit message. - (Optionally) pushes the commits. - (Optionally) force-reverts after a commit. - + 3. Sync Content | drush provision-devshop-sync @source @destination | drush pds @source @destination This task pulls content down from a site that has a drush alias on the current system. Currently any alias can be entered when Syncing content. Eventually, the site will store its default "source" site. - + WARNING: This command is built as a backend command. There are NO PROMPTS before the scripts will sql-drop the @destination database. It should NEVER be used on a production site. Once "DevShop Environments" is in place, we can prevent this command from even being called on a "live" environment. - + This task: - (optionally) Pulls code - Drops the @destination database. From d586d6dc7443686472cb66301ec9ac16ab7336f0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Dec 2012 19:23:36 -0500 Subject: [PATCH 0254/3476] adding hidden fields so sites and platforms can be edited --- devshop_projects/inc/forms.inc | 38 +++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c9c5de3eb..4ecb9810d 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -22,10 +22,42 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } } - // On Hosting Task form + // On Hosting Task: Create Project form, do our own submission. if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-create'){ $form['#submit'] = array('hosting_task_devshop_create_form_submit'); } + + // If not a part of a project, bail out. + if (empty($form['#node']->nid)){ + return; + } + + // On Platform node forms, allow changing branch. + if ($form_id == 'platform_node_form'){ + $node = $form['#node']; + $project_node = node_load($node->project_nid); + + $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); + $form['git_branch'] = array( + '#type' => 'select', + '#title' => t('Branch'), + '#default_value' => isset($node->git_branch)? $node->git_branch: NULL, + '#options' => $branch_options, + '#description' => t('Choose the branch that this platform should track.'), + ); + } + + // Save values that need to be saved. + if ($form_id == 'platform_node_form' || $form_id == 'site_node_form'){ + $form['project'] = array( + '#type' => 'value', + '#value' => $node->project, + ); + $form['environment'] = array( + '#type' => 'value', + '#value' => $node->environment, + ); + } } /** @@ -73,10 +105,6 @@ function devshop_projects_form(&$node) { '#maxlength' => 255, '#weight' => 2, ); - $form['retry'] = array( - '#type' => 'value', - '#value' => $retry, - ); // Don't allow editing if ($node->nid) { From aa6c63127286ef3458f96e6561a3a3097b580da9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 12:39:59 -0500 Subject: [PATCH 0255/3476] moving patches and adding readme. --- .../1513678-hosting-task-names-1.9.patch | 0 .../1760962-hosting-task-provision-save-1.9.patch | 0 .../1778400-hosting-tasks-names-hooks-1.9.patch | 0 patches/README.txt | 3 +++ 4 files changed, 3 insertions(+) rename 1513678-hosting-task-names-1.9.patch => patches/1513678-hosting-task-names-1.9.patch (100%) rename 1760962-hosting-task-provision-save-1.9.patch => patches/1760962-hosting-task-provision-save-1.9.patch (100%) rename 1778400-hosting-tasks-names-hooks-1.9.patch => patches/1778400-hosting-tasks-names-hooks-1.9.patch (100%) create mode 100644 patches/README.txt diff --git a/1513678-hosting-task-names-1.9.patch b/patches/1513678-hosting-task-names-1.9.patch similarity index 100% rename from 1513678-hosting-task-names-1.9.patch rename to patches/1513678-hosting-task-names-1.9.patch diff --git a/1760962-hosting-task-provision-save-1.9.patch b/patches/1760962-hosting-task-provision-save-1.9.patch similarity index 100% rename from 1760962-hosting-task-provision-save-1.9.patch rename to patches/1760962-hosting-task-provision-save-1.9.patch diff --git a/1778400-hosting-tasks-names-hooks-1.9.patch b/patches/1778400-hosting-tasks-names-hooks-1.9.patch similarity index 100% rename from 1778400-hosting-tasks-names-hooks-1.9.patch rename to patches/1778400-hosting-tasks-names-hooks-1.9.patch diff --git a/patches/README.txt b/patches/README.txt new file mode 100644 index 000000000..76742c883 --- /dev/null +++ b/patches/README.txt @@ -0,0 +1,3 @@ +These patches are only required for hostmaster-6.x-1.9. + +All of them have been committed to hostmaster-6.x-1.x and will be included in the next release of aegir. From f04024a797c203acd012a3150fbd26f75291866f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 12:40:21 -0500 Subject: [PATCH 0256/3476] removing license --- LICENSE.txt | 339 ---------------------------------------------------- 1 file changed, 339 deletions(-) delete mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index d159169d1..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. From accbb914922a17b56309bcf7199a8c2fde47b690 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 12:43:59 -0500 Subject: [PATCH 0257/3476] creating install text file --- INSTALL.txt | 0 README.txt | 43 ++++++++----------------------------------- 2 files changed, 8 insertions(+), 35 deletions(-) create mode 100644 INSTALL.txt diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 000000000..e69de29bb diff --git a/README.txt b/README.txt index 5666c5e73..521507a80 100644 --- a/README.txt +++ b/README.txt @@ -3,57 +3,30 @@ DevShop Hosting =============== Drupal development infrastructure made easy. - -This module provides the front-end commands needed to +This module provides the front-end interface needed to deploy and manage sites using the DevShop git and features based development workflow. Installation ------------ +For installation instructions, see INSTALL.txt. -1. Install Aegir - - DevShop assumes a base Aegir installation. - See http://community.aegirproject.org/handbook to learn about Aegir. - - The most reliable and generally supported way to install aegir is with - the Debian packages. - - See http://community.aegirproject.org/installing/debian for instructions on - installing in debian based systems like Ubuntu. Pay close attention to - -2. Install provision_git and devshop provision - - $ drush dl devshop_provision provision_git - - In a typical debian environment, it will try to download it to - /usr/share/drush/commands, but you can put it anywhere Drush command files can live. - -3. Install devshop_hosting and enable desired modules. - - Download devshop_hosting and ctools into your @hostmaster site with drush: - $ drush @hostmaster dl devshop_hosting ctools - - Enable devshop_projects, devshop_tasks, devshop_log, and optionally, - devshop_tasks and devshop_tests: - - $ drush @hostmaster en devshop_projects devshop_tasks devshop_log devshop_tests - -NOTE: The project creation wizard requires -certain Hosting Tasks to run before later steps can be completed. For this -reason, it really helps to have a very fast Hosting Queue, or to install the Hosting Queue Runner project (http://drupal.org/project/hosting_queue_runner) so that tasks fire as quickly as possible. Usage ----- -DevShop functionality centers around "Projects". Aegir Project nodes store a Git URL, the code path, the "base url", and the branches of the remote repository. +DevShop functionality centers around "Projects". Aegir Project nodes store a +Git URL, the code path, the "base url", and the branches of the remote +repository. DevShop allows multiple platforms and sites (for dev, test, or live purposes) to be created very easily. Platforms can be easily created from existing branches of your git repositories. -To create a new project, visit either the Projects page or click "Create Content" > "DevShop Project". +To create a new project, visit either the Projects page or click +"Create Content" > "DevShop Project". + ### Step 1: Git URL and project name. From 046b1e22447e5b1f3f2aedd0c6b083cad42fe6f3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 12:54:30 -0500 Subject: [PATCH 0258/3476] adding readme just for projects module --- devshop_projects/README.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/README.txt b/devshop_projects/README.txt index b68ad016b..c8a974dc7 100644 --- a/devshop_projects/README.txt +++ b/devshop_projects/README.txt @@ -1,2 +1,8 @@ -This module requires one tiny patch to hosting_tasks.module... + +DevShop Projects +================ +This module provides the majority of functionality for DevShop. It provides +the node type and data needed for DevShop functionality. + +For this reason, documentation for this project is included in ../README.txt From 89074c9fd010628643569bc146a9e78dc6835b34 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:24:01 -0500 Subject: [PATCH 0259/3476] adding INSTALL.txt and edits --- INSTALL.txt | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.txt | 9 ++++++--- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/INSTALL.txt b/INSTALL.txt index e69de29bb..0c315102f 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -0,0 +1,56 @@ +DevShop Hosting +=============== + +Installation +------------ + +DevShop is built on top of Aegir, and consists of two projects: +devshop_provision (backend) and devshop_hosting (front-end). + +DevShop Provision also requires the provision_git drush module. + + +1. Install Aegir + + DevShop assumes a base Aegir installation. + See http://community.aegirproject.org/handbook to learn about Aegir. + + The most reliable and generally supported way to install aegir is with + the Debian packages. + + See http://community.aegirproject.org/installing/debian for instructions on + installing in debian based systems like Ubuntu. Pay close attention to + +2. Install provision_git and devshop provision + + $ drush dl devshop_provision provision_git + + In a typical debian environment, it will try to download it to + /usr/share/drush/commands, but you can put it anywhere Drush command files can live. + +3. Install devshop_hosting and enable desired modules. + + Download devshop_hosting and ctools into your @hostmaster site with drush: + $ drush @hostmaster dl devshop_hosting ctools + + Enable devshop_projects, devshop_tasks, devshop_log, and optionally, + devshop_tasks and devshop_tests: + + $ drush @hostmaster en devshop_projects devshop_tasks devshop_log devshop_tests + +NOTE: The project creation wizard requires certain Hosting Tasks to run before +later steps can be completed. For this reason, it really helps to have a very +fast Hosting Queue, or to install the Hosting Queue Runner project (http://drupal.org/project/hosting_queue_runner) so that tasks fire as quickly +as possible. + +4. Connect to your private Git repositories. + +DevShop allows you to clone your projects via the web based interface. In order +to do this, you must first make sure your DevShop/aegir server can connect to +your repository. + +Setup SSH keys so that your aegir user can git clone your repository. The first +time you clone an SSH repository, you have to type "Yes" for security purposes. + +Once you can connect and clone, you no longer need to use the command line to +create new platforms on your aegir server. \ No newline at end of file diff --git a/README.txt b/README.txt index 521507a80..2b4a2da3f 100644 --- a/README.txt +++ b/README.txt @@ -13,8 +13,8 @@ Installation For installation instructions, see INSTALL.txt. -Usage ------ +DevShop Projects +---------------- DevShop functionality centers around "Projects". Aegir Project nodes store a Git URL, the code path, the "base url", and the branches of the remote @@ -24,10 +24,13 @@ DevShop allows multiple platforms and sites (for dev, test, or live purposes) to be created very easily. Platforms can be easily created from existing branches of your git repositories. + +Creating Projects +----------------- + To create a new project, visit either the Projects page or click "Create Content" > "DevShop Project". - ### Step 1: Git URL and project name. Enter your project's Git URL and Project Name. From 29e1f4ea650f72eb2528f5ef29701243c16fce29 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:27:25 -0500 Subject: [PATCH 0260/3476] adding goals --- README.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.txt b/README.txt index 2b4a2da3f..dc0259ad2 100644 --- a/README.txt +++ b/README.txt @@ -8,6 +8,16 @@ deploy and manage sites using the DevShop git and features based development workflow. +About +----- + +The goals of DevShop are... + +1. to simplify management of multiple environments for multiple Drupal projects. +2. to provide web-based tools that streamline the Drupal site building workflow. +3. to provide a common, open-source infrastructure for Drupal development shops. + + Installation ------------ For installation instructions, see INSTALL.txt. From 756e8a243f5f2456c4343e907bc058e074c3def4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:30:23 -0500 Subject: [PATCH 0261/3476] readme edits --- INSTALL.txt | 1 + README.txt | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/INSTALL.txt b/INSTALL.txt index 0c315102f..49267e263 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,3 +1,4 @@ + DevShop Hosting =============== diff --git a/README.txt b/README.txt index dc0259ad2..846dfc4dc 100644 --- a/README.txt +++ b/README.txt @@ -7,9 +7,8 @@ This module provides the front-end interface needed to deploy and manage sites using the DevShop git and features based development workflow. - -About ------ +About DevShop +------------- The goals of DevShop are... From 93f3d4cce08ffda03141d86c688eea27b1c50fea Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:32:00 -0500 Subject: [PATCH 0262/3476] killing old ajax file --- devshop_projects/devshop_projects.ajax.inc | 65 ---------------------- 1 file changed, 65 deletions(-) delete mode 100644 devshop_projects/devshop_projects.ajax.inc diff --git a/devshop_projects/devshop_projects.ajax.inc b/devshop_projects/devshop_projects.ajax.inc deleted file mode 100644 index 9b69370da..000000000 --- a/devshop_projects/devshop_projects.ajax.inc +++ /dev/null @@ -1,65 +0,0 @@ - NULL, - 'submitted' => FALSE, - ); - $form_build_id = $_POST['form_build_id']; - - // Get the form from the cache. - $form = form_get_cache($form_build_id, $form_state); - $args = $form['#parameters']; - $form_id = array_shift($args); - - // We will run some of the submit handlers so we need to disable redirecting. - $form['#redirect'] = FALSE; - - // We need to process the form, prepare for that by setting a few internals - // variables. - $form['#post'] = $_POST; - $form['#programmed'] = FALSE; - $form_state['post'] = $_POST; - - // Build, validate and if possible, submit the form. - // drupal_process_form($form_id, $form, $form_state); - - // This call recreates the form relying solely on the form_state that the - // drupal_process_form set up. - $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id); - - //// Render the new output. - //$choice_form = $form['choice_wrapper']['choice']; - //unset($choice_form['#prefix'], $choice_form['#suffix']); // Prevent duplicate wrappers. - //$output = theme('status_messages') . drupal_render($choice_form); - - - // Get refs - $git_url = $form_state['post']['git_url']; - $command = "git-ls-remote $git_url"; - $exec_output = shell_exec($command); - - $lines = explode("\n", $exec_output); - $output .= print_r($lines, 1); - $branches = array(); - foreach ($lines as $line){ - list($commit, $ref_string) = explode(" ", $line); - list($i, $type, $name) = explode('/', $ref_string); - - if ($type == 'heads'){ - $branches[$name] = $name; - } - } - - $form['branch']['#options'] = $branches; - - //$output .= $command.$exec_output; - $output .= theme('status_messages'); - $output .= drupal_render($form['branch']); -// $output .= print_r($form_state, 1); - - drupal_json(array('status' => TRUE, 'data' => $output)); -} \ No newline at end of file From 83468f5444ca2be9e9b4a01e5aaa68990ea4ca60 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:36:23 -0500 Subject: [PATCH 0263/3476] adding perms and tasks from devshop_tasks module --- devshop_projects/devshop_projects.module | 5 ++++- devshop_projects/inc/tasks.inc | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9f838b4c0..9415349bd 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -22,7 +22,10 @@ include_once('inc/ui.inc'); function devshop_projects_perm() { return array( 'view projects', - 'delete project' + 'create devshop-create task', + 'create devshop-commit task', + 'create devshop-pull task', + 'create devshop-sync task', ); } diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 049c81488..87d7c1b98 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -21,6 +21,26 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); + $tasks['site']['devshop-commit'] = array( + 'title' => t('Commit Features'), + 'description' => t('Recreates all Features and commits the result.'), + 'access callback' => 'devshop_hosting_task_menu_access', + 'dialog' => TRUE, + ); + $tasks['site']['devshop-pull'] = array( + 'title' => t('Pull Code'), + 'description' => t('Pull & verify platform code and (optionally) run update.php, clear cache, and revert features.'), + 'access callback' => 'devshop_hosting_task_menu_access', + 'dialog' => TRUE, + ); + $tasks['site']['devshop-sync'] = array( + 'title' => t('Sync Content'), + 'description' => t('Sync content from another site and (optionally) run update.php, clear cache, and revert features.'), + 'access callback' => 'devshop_hosting_task_menu_access', + 'dialog' => TRUE, + ); + + /* @TODO: Refactor custom forms to work with hosting_confirm_form() $tasks['project']['delete'] = array( From 66d1c4f57c242c4633072749395ee537813731af Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:39:33 -0500 Subject: [PATCH 0264/3476] folding devshop_tasks into projects module --- devshop_projects/devshop_projects.module | 11 ++ devshop_projects/inc/tasks.inc | 147 +++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9415349bd..faa2d3e2f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -224,3 +224,14 @@ function _devshop_projects_node_create($type, $node = stdClass){ return $node; } +/** + * Check if a site has features diff enabled. + */ +function _devshop_projects_site_has_module($node, $module) { + $param = array( + 'rid' => $node->nid, + 'p.short_name' => $module, + ); + $package = hosting_package_instance_load($param); + return $package->status; +} \ No newline at end of file diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 87d7c1b98..42b036f0f 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -119,3 +119,150 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $form_state['redirect'] = 'node/' . $values['nid']; modalframe_close_dialog(); } + + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form(). + * + * For "Commit" task. + */ +function hosting_task_devshop_commit_form($node) { + + $descr = 'A message describing this commit. Too see a diff output off all of the features, '; + + if (_devshop_projects_site_has_module($node, 'features_diff')) { + $descr .= 'click ' . l(t('here.'), "http://$node->hosting_name/features/diff/all", array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . ' Be patient. It takes a few moments for the diffs to be generatred.'; + } + else { + $descr .= 'enable the Features Diff module for this site, Verify the site, and select this task again.'; + } + + $form['message'] = array( + '#title' => t('Commit Message'), + '#type' => 'textarea', + '#description' => $descr, + ); + $form['push'] = array( + '#title' => t('Push code after commit?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + $form['revert'] = array( + '#title' => t('Force revert features after commit?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + // @TODO: Provide a DIFF display to give the user an idea of what has changed. + return $form; +} + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form(). + * + * For "Pull Code" task. + */ +function hosting_task_devshop_pull_form($node) { + // @TODO: Once there is some kind of site relationship system, + // determine if the site has a parent site to sync from, then show this checkbox. + $has_features = _devshop_projects_site_has_module($node, 'features'); + /* + $form['sync'] = array( + '#title' => t('Sync content from live before code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + */ + $form['update'] = array( + '#title' => t('Run update.php after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + $form['revert'] = array( + '#title' => t('Revert all features after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + $form['cache'] = array( + '#title' => t('Clear cache after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + return $form; +} + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form(). + * + * For "Sync Content" task. + */ +function hosting_task_devshop_sync_form($node) { +/* Disabled until we add support for custom aliases + $form['source'] = array( + '#title' => t('Source site alias'), + '#type' => 'textfield', + '#description' => t('Enter a site alias to sync from. The alias must exist in the local system.'), + ); + */ + // Get nid + $nid = $node->nid; + $has_features = _devshop_projects_site_has_module($node, 'features'); + + // See if this site is already in a project + $query = db_query("SELECT project_nid " . + "FROM {hosting_devshop_project_object} " . + "WHERE object_nid = %d " . + "AND object_type = 'site'", $nid); + + if($proj = db_fetch_object($query)) { + + // Only make sites in this project a source for content + $query = db_query("SELECT object_nid " . + "FROM {hosting_devshop_project_object} " . + "WHERE project_nid = %d " . + "AND object_type = 'site' " . + "AND object_nid <> %d", $proj->project_nid, $nid); + + $options = array(); + while($s = db_fetch_object($query)) { + $site_node = node_load($s->object_nid); + $options['@' . $site_node->title] = '@' . $site_node->title; + } + + $form['note'] = array( + '#value' => '

'. t('This will DESTROY the database for !site and replace it with the database for the selected Source Site.', array('!site' => l($node->title, "node/$nid"))) . '

', + '#type' => 'markup', + '#weight' => 100, + ); + + $form['source'] = array( + '#type' => 'radios', + '#title' => t('Source Site'), + '#options' => $options, + '#default_value' => key($options), + '#description' => t('Select site to sync from.'), + ); + } + + $form['pull'] = array( + '#title' => t('Pull code before content sync?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + $form['update'] = array( + '#title' => t('Run update.php after content sync?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + $form['revert'] = array( + '#title' => t('Revert all features after content sync?'), + '#type' => 'checkbox', + '#default_value' => $has_features, + '#access' => $has_features, + ); + $form['cache'] = array( + '#title' => t('Clear cache after content sync?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + return $form; +} From 509bfb0f18aaba17ba2db620d8481e85efbaefdc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:40:00 -0500 Subject: [PATCH 0265/3476] removing devshop tasks module --- devshop_tasks/devshop_tasks.info | 4 - devshop_tasks/devshop_tasks.module | 205 ----------------------------- 2 files changed, 209 deletions(-) delete mode 100644 devshop_tasks/devshop_tasks.info delete mode 100644 devshop_tasks/devshop_tasks.module diff --git a/devshop_tasks/devshop_tasks.info b/devshop_tasks/devshop_tasks.info deleted file mode 100644 index 086b9b7bd..000000000 --- a/devshop_tasks/devshop_tasks.info +++ /dev/null @@ -1,4 +0,0 @@ -name = DevShop Tasks -description = Provides common tasks needed to manage Drupal site development. -core = 6.x -package = DevShop \ No newline at end of file diff --git a/devshop_tasks/devshop_tasks.module b/devshop_tasks/devshop_tasks.module deleted file mode 100644 index 213aea816..000000000 --- a/devshop_tasks/devshop_tasks.module +++ /dev/null @@ -1,205 +0,0 @@ - t('Commit Features'), - 'description' => t('Recreates all Features and commits the result.'), - 'dialog' => TRUE, - ); - - $tasks['site']['devshop-pull'] = array( - 'title' => t('Pull Code'), - 'description' => t('Pull & verify platform code and (optionally) run update.php, clear cache, and revert features.'), - 'dialog' => TRUE, - 'task_permitted' => TRUE, - 'access callback' => 'user_access', - 'access arguments' => array('create devshop-pull task'), - ); - - $tasks['site']['devshop-sync'] = array( - 'title' => t('Sync Content'), - 'description' => t('Sync content from another site and (optionally) run update.php, clear cache, and revert features.'), - 'dialog' => TRUE, - ); - return $tasks; -} - -/** - * Implementation of hook_perm() - */ -function devshop_tasks_perm() { - return array( - 'create devshop-commit task', - 'create devshop-pull task', - 'create devshop-sync task', - ); -} - -/** - * Check if a site has features diff enabled. - */ -function _devshop_tasks_site_has_module($node, $module) { - $param = array( - 'rid' => $node->nid, - 'p.short_name' => $module, - ); - $package = hosting_package_instance_load($param); - return $package->status; -} - -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Commit" task. - */ -function hosting_task_devshop_commit_form($node) { - - $descr = 'A message describing this commit. Too see a diff output off all of the features, '; - - if (_devshop_tasks_site_has_module($node, 'features_diff')) { - $descr .= 'click ' . l(t('here.'), "http://$node->hosting_name/features/diff/all", array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . ' Be patient. It takes a few moments for the diffs to be generatred.'; - } - else { - $descr .= 'enable the Features Diff module for this site, Verify the site, and select this task again.'; - } - - $form['message'] = array( - '#title' => t('Commit Message'), - '#type' => 'textarea', - '#description' => $descr, - ); - $form['push'] = array( - '#title' => t('Push code after commit?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - $form['revert'] = array( - '#title' => t('Force revert features after commit?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - // @TODO: Provide a DIFF display to give the user an idea of what has changed. - return $form; -} - -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Pull Code" task. - */ -function hosting_task_devshop_pull_form($node) { - // @TODO: Once there is some kind of site relationship system, - // determine if the site has a parent site to sync from, then show this checkbox. - $has_features = _devshop_tasks_site_has_module($node, 'features'); - /* - $form['sync'] = array( - '#title' => t('Sync content from live before code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - */ - $form['update'] = array( - '#title' => t('Run update.php after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - $form['revert'] = array( - '#title' => t('Revert all features after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - $form['cache'] = array( - '#title' => t('Clear cache after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - return $form; -} - -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Sync Content" task. - */ -function hosting_task_devshop_sync_form($node) { -/* Disabled until we add support for custom aliases - $form['source'] = array( - '#title' => t('Source site alias'), - '#type' => 'textfield', - '#description' => t('Enter a site alias to sync from. The alias must exist in the local system.'), - ); - */ - // Get nid - $nid = $node->nid; - $has_features = _devshop_tasks_site_has_module($node, 'features'); - - // See if this site is already in a project - $query = db_query("SELECT project_nid " . - "FROM {hosting_devshop_project_object} " . - "WHERE object_nid = %d " . - "AND object_type = 'site'", $nid); - - if($proj = db_fetch_object($query)) { - - // Only make sites in this project a source for content - $query = db_query("SELECT object_nid " . - "FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d " . - "AND object_type = 'site' " . - "AND object_nid <> %d", $proj->project_nid, $nid); - - $options = array(); - while($s = db_fetch_object($query)) { - $site_node = node_load($s->object_nid); - $options['@' . $site_node->title] = '@' . $site_node->title; - } - - $form['note'] = array( - '#value' => '

'. t('This will DESTROY the database for !site and replace it with the database for the selected Source Site.', array('!site' => l($node->title, "node/$nid"))) . '

', - '#type' => 'markup', - '#weight' => 100, - ); - - $form['source'] = array( - '#type' => 'radios', - '#title' => t('Source Site'), - '#options' => $options, - '#default_value' => key($options), - '#description' => t('Select site to sync from.'), - ); - } - - $form['pull'] = array( - '#title' => t('Pull code before content sync?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - $form['update'] = array( - '#title' => t('Run update.php after content sync?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - $form['revert'] = array( - '#title' => t('Revert all features after content sync?'), - '#type' => 'checkbox', - '#default_value' => $has_features, - '#access' => $has_features, - ); - $form['cache'] = array( - '#title' => t('Clear cache after content sync?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - return $form; -} - From 78888f897db17d8036683ddd03d147b11cd73361 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 13:50:15 -0500 Subject: [PATCH 0266/3476] moving hosting drush code to devshop_projects --- devshop_projects/devshop_projects.drush.inc | 129 ++++++++++++++------ devshop_projects/inc/tasks.inc | 6 +- 2 files changed, 96 insertions(+), 39 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 4454c670b..a01ed471e 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -5,7 +5,6 @@ * will go here. */ - /** * Implements drush_HOOK_pre_COMMAND() * @@ -19,57 +18,115 @@ */ function drush_devshop_projects_pre_hosting_task() { - // Verify Platform $task =& drush_get_context('HOSTING_TASK'); - // Verify Platform // For our platforms, we have to clone it if it has a git_remote // If it has a git branch, we should checkout as well. if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->git_url)) { + drush_devshop_provision_pre_hosting_task_platform_verify(); + } + + // Pull + if ($task->ref->type == 'platform' && $task->task_type == 'devshop-pull') { + $task->options['no-update'] = !$task->task_args['update']; + $task->options['no-revert'] = !$task->task_args['revert']; + $task->options['no-cache'] = !$task->task_args['cache']; + $task->options['sites'] = explode('|', $task->task_args['sites']); + } - $platform = $task->ref; - $root = $platform->publish_path; - $git_remote = $platform->git_url; - $git_branch = $platform->git_branch; + // Commit + if ($task->ref->type == 'site' && $task->task_type == 'devshop-commit') { + $task->options['message'] = $task->task_args['message']; + $task->options['push'] = $task->task_args['push']; + $task->options['revert'] = $task->task_args['revert']; + } - // Check if a repo exists - if (!is_dir($root) && !drush_shell_cd_and_exec($root, 'git status')){ - drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); + // Sync + if ($task->ref->type == 'site' && $task->task_type == 'devshop-sync') { + $task->options['source'] = $task->task_args['source']; + $task->options['pull'] = $task->task_args['pull']; + $task->options['no-update'] = !$task->task_args['update']; + $task->options['no-revert'] = !$task->task_args['revert']; + $task->options['no-cache'] = !$task->task_args['cache']; + } + + // Platform Create + if ($task->ref->type == 'project' && $task->task_type == 'devshop-create') { + $task->options['platform-name'] = $task->task_args['platform_name']; + $task->options['branch'] = $task->task_args['branch']; + $task->options['pull'] = $task->task_args['pull']; + } + + // Download + if ($task->ref->type == 'site' && $task->task_type == 'devshop-dl') { + $task->options['modules'] = $task->task_args['modules']; + } - // Build the command string - $command = "git clone $git_remote $root"; - if ($git_branch) { - $command .= " --branch $git_branch"; + // Run tests + if ($task->ref->type == 'site' && $task->task_type == 'devshop-test') { + $tests = array(); + + foreach(explode("\n", $task->task_args['tests_to_run']) as $test) { + $test = trim($test); + if (strlen($test) > 0) { + $tests[] = $test; } } - // If the platform has been verified and has a branch and git url - else { - $root = $platform->publish_path; - $git_remote = $platform->git_url; - $git_branch = $platform->git_branch; + $task->options['tests-to-run'] = implode($tests, ","); + $task->options['sync-from-live'] = $task->task_args['sync']; + } +} - // Build the command string - $command = "git checkout $git_branch"; +/** + * Pre hosting task hook for "verify platform" task. + * - Clones the repository on first run, checks out the selected after that. + */ +function drush_devshop_provision_pre_hosting_task_platform_verify(){ + // Verify Platform + $task =& drush_get_context('HOSTING_TASK'); + + $platform = $task->ref; + $root = $platform->publish_path; + $git_remote = $platform->git_url; + $git_branch = $platform->git_branch; + + // Check if a repo exists + if (!is_dir($root) && !drush_shell_cd_and_exec($root, 'git status')){ + drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); + + // Build the command string + $command = "git clone $git_remote $root"; + if ($git_branch) { + $command .= " --branch $git_branch"; } + } + // If the platform has been verified and has a branch and git url + else { + $root = $platform->publish_path; + $git_remote = $platform->git_url; + $git_branch = $platform->git_branch; - // Execute - if (!empty($command)){ - drush_log('[DEVSHOP] Running: ' . $command); + // Build the command string + $command = "git checkout $git_branch"; + } - // @TODO: Create a d()->server->shell_cd_and_exec() function - // server->shell_exec() uses escapeshellcmd(), so we cannot cd_and_exec! - // So instead, this is the code from d()->server->shell_exec - // d()->server->shell_exec($cmd); - if (provision_is_local_host(d()->server->remote_host)) { - return drush_shell_cd_and_exec($root, escapeshellcmd($command)); - } - else { - return drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); - } + // Execute + if (!empty($command)){ + drush_log('[DEVSHOP] Running: ' . $command); - $output = drush_shell_exec_output(); - drush_log('Shell Output: ' . implode("\n", $output) , 'notice'); + // @TODO: Create a d()->server->shell_cd_and_exec() function + // server->shell_exec() uses escapeshellcmd(), so we cannot cd_and_exec! + // So instead, this is the code from d()->server->shell_exec + // d()->server->shell_exec($cmd); + if (provision_is_local_host(d()->server->remote_host)) { + return drush_shell_cd_and_exec($root, escapeshellcmd($command)); + } + else { + return drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); } + + $output = drush_shell_exec_output(); + drush_log('Shell Output: ' . implode("\n", $output) , 'notice'); } } diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 42b036f0f..5036b5841 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -21,19 +21,19 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); - $tasks['site']['devshop-commit'] = array( + $tasks['project']['devshop-commit'] = array( 'title' => t('Commit Features'), 'description' => t('Recreates all Features and commits the result.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); - $tasks['site']['devshop-pull'] = array( + $tasks['project']['devshop-pull'] = array( 'title' => t('Pull Code'), 'description' => t('Pull & verify platform code and (optionally) run update.php, clear cache, and revert features.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); - $tasks['site']['devshop-sync'] = array( + $tasks['project']['devshop-sync'] = array( 'title' => t('Sync Content'), 'description' => t('Sync content from another site and (optionally) run update.php, clear cache, and revert features.'), 'access callback' => 'devshop_hosting_task_menu_access', From efd393c856ac39788b6c9353a8c5e353172caaa1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 14:32:37 -0500 Subject: [PATCH 0267/3476] fixing option setting --- devshop_projects/devshop_projects.drush.inc | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index a01ed471e..9296145a5 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -28,9 +28,9 @@ function drush_devshop_projects_pre_hosting_task() { // Pull if ($task->ref->type == 'platform' && $task->task_type == 'devshop-pull') { - $task->options['no-update'] = !$task->task_args['update']; - $task->options['no-revert'] = !$task->task_args['revert']; - $task->options['no-cache'] = !$task->task_args['cache']; + $task->options['update'] = $task->task_args['update']; + $task->options['revert'] = $task->task_args['revert']; + $task->options['cache'] = $task->task_args['cache']; $task->options['sites'] = explode('|', $task->task_args['sites']); } @@ -45,16 +45,9 @@ function drush_devshop_projects_pre_hosting_task() { if ($task->ref->type == 'site' && $task->task_type == 'devshop-sync') { $task->options['source'] = $task->task_args['source']; $task->options['pull'] = $task->task_args['pull']; - $task->options['no-update'] = !$task->task_args['update']; - $task->options['no-revert'] = !$task->task_args['revert']; - $task->options['no-cache'] = !$task->task_args['cache']; - } - - // Platform Create - if ($task->ref->type == 'project' && $task->task_type == 'devshop-create') { - $task->options['platform-name'] = $task->task_args['platform_name']; - $task->options['branch'] = $task->task_args['branch']; - $task->options['pull'] = $task->task_args['pull']; + $task->options['update'] = $task->task_args['update']; + $task->options['revert'] = $task->task_args['revert']; + $task->options['cache'] = $task->task_args['cache']; } // Download From 84313bd6076dc21dc21f1702061fe35739b00aae Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 14:52:50 -0500 Subject: [PATCH 0268/3476] converting pull code task to work on project nodes. --- devshop_projects/devshop_projects.drush.inc | 3 +- devshop_projects/inc/tasks.inc | 39 +++++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 9296145a5..2ba3e3734 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -27,7 +27,8 @@ function drush_devshop_projects_pre_hosting_task() { } // Pull - if ($task->ref->type == 'platform' && $task->task_type == 'devshop-pull') { + if ($task->ref->type == 'project' && $task->task_type == 'devshop-pull') { + $task->args['environment'] = $task->task_args['environment']; $task->options['update'] = $task->task_args['update']; $task->options['revert'] = $task->task_args['revert']; $task->options['cache'] = $task->task_args['cache']; diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 5036b5841..5c82d8d61 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -53,6 +53,14 @@ function devshop_projects_hosting_tasks() { return $tasks; } + +/** + * Helper to provide a select list of environments for this project + */ +function devshop_projects_environment_options($node){ + return array_combine($node->project_objects['site'], $node->project_objects['site']); +} + /** * Implementation of hook_hosting_task_TASK_TYPE_form(). * @@ -162,26 +170,29 @@ function hosting_task_devshop_commit_form($node) { * For "Pull Code" task. */ function hosting_task_devshop_pull_form($node) { - // @TODO: Once there is some kind of site relationship system, - // determine if the site has a parent site to sync from, then show this checkbox. - $has_features = _devshop_projects_site_has_module($node, 'features'); - /* - $form['sync'] = array( - '#title' => t('Sync content from live before code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, + $env_options = devshop_projects_environment_options($node); + $form['environment'] = array( + '#type' => 'radios', + '#title' => t('Environment'), + '#options' => $env_options, + '#default_value' => key($env_options), + '#description' => t('Select platform to Pull Code to.'), ); - */ + $form['update'] = array( '#title' => t('Run update.php after code pull?'), '#type' => 'checkbox', '#default_value' => 1, ); - $form['revert'] = array( - '#title' => t('Revert all features after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); + + $has_features = _devshop_projects_site_has_module($node, 'features'); + if ($has_features){ + $form['revert'] = array( + '#title' => t('Revert all features after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + } $form['cache'] = array( '#title' => t('Clear cache after code pull?'), '#type' => 'checkbox', From 7042414090b84378350865b4866094690f8d57a7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 15:04:29 -0500 Subject: [PATCH 0269/3476] creating standardized "choose environment" form. --- devshop_projects/inc/tasks.inc | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 5c82d8d61..14713da5b 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -57,8 +57,16 @@ function devshop_projects_hosting_tasks() { /** * Helper to provide a select list of environments for this project */ -function devshop_projects_environment_options($node){ - return array_combine($node->project_objects['site'], $node->project_objects['site']); +function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description) { + // @TODO: Add a check here. Sometimes we want to limit this list. + $options = array_combine($node->project_objects['site'], $node->project_objects['site']); + $form['environment'] = array( + '#type' => 'radios', + '#title' => t('Environment'), + '#options' => $options, + '#default_value' => key($options), + '#description' => $description, + ); } /** @@ -136,6 +144,9 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { */ function hosting_task_devshop_commit_form($node) { + $form = array(); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to commit features from.')); + $descr = 'A message describing this commit. Too see a diff output off all of the features, '; if (_devshop_projects_site_has_module($node, 'features_diff')) { @@ -170,15 +181,10 @@ function hosting_task_devshop_commit_form($node) { * For "Pull Code" task. */ function hosting_task_devshop_pull_form($node) { - $env_options = devshop_projects_environment_options($node); - $form['environment'] = array( - '#type' => 'radios', - '#title' => t('Environment'), - '#options' => $env_options, - '#default_value' => key($env_options), - '#description' => t('Select platform to Pull Code to.'), - ); - + + $form = array(); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to pull code to.')); + $form['update'] = array( '#title' => t('Run update.php after code pull?'), '#type' => 'checkbox', From 0fe88af6aab503c590807f4317c4ddca787cdd31 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 15:06:13 -0500 Subject: [PATCH 0270/3476] making reusable environments selector --- devshop_projects/inc/tasks.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 14713da5b..1d1720c0e 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -57,10 +57,10 @@ function devshop_projects_hosting_tasks() { /** * Helper to provide a select list of environments for this project */ -function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description) { +function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description, $key = 'environment') { // @TODO: Add a check here. Sometimes we want to limit this list. $options = array_combine($node->project_objects['site'], $node->project_objects['site']); - $form['environment'] = array( + $form[$key] = array( '#type' => 'radios', '#title' => t('Environment'), '#options' => $options, From d3045a92235638e22989d77eeb87c4188060ca1d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 15:11:20 -0500 Subject: [PATCH 0271/3476] adding more details to sync form messages --- devshop_projects/inc/tasks.inc | 68 +++++++++++----------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 1d1720c0e..3eb50c462 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -57,12 +57,12 @@ function devshop_projects_hosting_tasks() { /** * Helper to provide a select list of environments for this project */ -function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description, $key = 'environment') { +function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description, $key = 'environment', $title = 'Environment') { // @TODO: Add a check here. Sometimes we want to limit this list. $options = array_combine($node->project_objects['site'], $node->project_objects['site']); $form[$key] = array( '#type' => 'radios', - '#title' => t('Environment'), + '#title' => t($title), '#options' => $options, '#default_value' => key($options), '#description' => $description, @@ -222,62 +222,36 @@ function hosting_task_devshop_sync_form($node) { */ // Get nid $nid = $node->nid; - $has_features = _devshop_projects_site_has_module($node, 'features'); - // See if this site is already in a project - $query = db_query("SELECT project_nid " . - "FROM {hosting_devshop_project_object} " . - "WHERE object_nid = %d " . - "AND object_type = 'site'", $nid); - - if($proj = db_fetch_object($query)) { - - // Only make sites in this project a source for content - $query = db_query("SELECT object_nid " . - "FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d " . - "AND object_type = 'site' " . - "AND object_nid <> %d", $proj->project_nid, $nid); - - $options = array(); - while($s = db_fetch_object($query)) { - $site_node = node_load($s->object_nid); - $options['@' . $site_node->title] = '@' . $site_node->title; - } + $form = array(); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment.'), 'destination', 'Destination'); - $form['note'] = array( - '#value' => '

'. t('This will DESTROY the database for !site and replace it with the database for the selected Source Site.', array('!site' => l($node->title, "node/$nid"))) . '

', - '#type' => 'markup', - '#weight' => 100, - ); - - $form['source'] = array( - '#type' => 'radios', - '#title' => t('Source Site'), - '#options' => $options, - '#default_value' => key($options), - '#description' => t('Select site to sync from.'), - ); - } - + $form['note'] = array( + '#value' => '

'. t('This will DESTROY the database for Destination and replace it with the database for the selected Source.', array('!site' => l($node->title, "node/$nid"))) . '

', + '#type' => 'markup', + '#weight' => 100, + ); $form['pull'] = array( - '#title' => t('Pull code before content sync?'), + '#title' => t('Pull code on Destination before content sync?'), '#type' => 'checkbox', '#default_value' => 1, ); $form['update'] = array( - '#title' => t('Run update.php after content sync?'), + '#title' => t('Run update.php on Destination after content sync?'), '#type' => 'checkbox', '#default_value' => 1, ); - $form['revert'] = array( - '#title' => t('Revert all features after content sync?'), - '#type' => 'checkbox', - '#default_value' => $has_features, - '#access' => $has_features, - ); + if (_devshop_projects_site_has_module($node, 'features')){ + $form['revert'] = array( + '#title' => t('Revert all features on Destination after content sync?'), + '#type' => 'checkbox', + '#default_value' => $has_features, + '#access' => $has_features, + ); + } $form['cache'] = array( - '#title' => t('Clear cache after content sync?'), + '#title' => t('Clear cache on Destination after content sync?'), '#type' => 'checkbox', '#default_value' => 1, ); From 0b81ba07b53cd7d69ea2cc76e253fd0c7d664f27 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 15:13:12 -0500 Subject: [PATCH 0272/3476] adding task args --- devshop_projects/devshop_projects.drush.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 2ba3e3734..c6d8a1c34 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -37,6 +37,7 @@ function drush_devshop_projects_pre_hosting_task() { // Commit if ($task->ref->type == 'site' && $task->task_type == 'devshop-commit') { + $task->args['environment'] = $task->task_args['environment']; $task->options['message'] = $task->task_args['message']; $task->options['push'] = $task->task_args['push']; $task->options['revert'] = $task->task_args['revert']; @@ -44,7 +45,8 @@ function drush_devshop_projects_pre_hosting_task() { // Sync if ($task->ref->type == 'site' && $task->task_type == 'devshop-sync') { - $task->options['source'] = $task->task_args['source']; + $task->args['source'] = $task->task_args['source']; + $task->args['destination'] = $task->task_args['destination']; $task->options['pull'] = $task->task_args['pull']; $task->options['update'] = $task->task_args['update']; $task->options['revert'] = $task->task_args['revert']; @@ -66,6 +68,7 @@ function drush_devshop_projects_pre_hosting_task() { $tests[] = $test; } } + $task->args['environment'] = $task->task_args['environment']; $task->options['tests-to-run'] = implode($tests, ","); $task->options['sync-from-live'] = $task->task_args['sync']; } From 96cbef92c48bd0f26360d752ec2f486561e687db Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 15:14:01 -0500 Subject: [PATCH 0273/3476] fixing if() --- devshop_projects/devshop_projects.drush.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index c6d8a1c34..29d560562 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -36,7 +36,7 @@ function drush_devshop_projects_pre_hosting_task() { } // Commit - if ($task->ref->type == 'site' && $task->task_type == 'devshop-commit') { + if ($task->ref->type == 'project' && $task->task_type == 'devshop-commit') { $task->args['environment'] = $task->task_args['environment']; $task->options['message'] = $task->task_args['message']; $task->options['push'] = $task->task_args['push']; @@ -44,7 +44,7 @@ function drush_devshop_projects_pre_hosting_task() { } // Sync - if ($task->ref->type == 'site' && $task->task_type == 'devshop-sync') { + if ($task->ref->type == 'project' && $task->task_type == 'devshop-sync') { $task->args['source'] = $task->task_args['source']; $task->args['destination'] = $task->task_args['destination']; $task->options['pull'] = $task->task_args['pull']; @@ -54,12 +54,12 @@ function drush_devshop_projects_pre_hosting_task() { } // Download - if ($task->ref->type == 'site' && $task->task_type == 'devshop-dl') { + if ($task->ref->type == 'project' && $task->task_type == 'devshop-dl') { $task->options['modules'] = $task->task_args['modules']; } // Run tests - if ($task->ref->type == 'site' && $task->task_type == 'devshop-test') { + if ($task->ref->type == 'project' && $task->task_type == 'devshop-test') { $tests = array(); foreach(explode("\n", $task->task_args['tests_to_run']) as $test) { From f8f96b118099ca526a65afdbbe1c58b54a31d6fc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 15:27:53 -0500 Subject: [PATCH 0274/3476] properly checking for features --- devshop_projects/devshop_projects.module | 11 ++++++++++- devshop_projects/inc/tasks.inc | 5 ++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index faa2d3e2f..065e0fc0c 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -234,4 +234,13 @@ function _devshop_projects_site_has_module($node, $module) { ); $package = hosting_package_instance_load($param); return $package->status; -} \ No newline at end of file +} + +/** + * Check if a site has features diff enabled. + */ +function _devshop_projects_project_has_module($node, $module) { + $sites = array_flip($node->project_objects['site']); + return _devshop_projects_site_has_module(node_load($sites['dev']), $module); +} + \ No newline at end of file diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 3eb50c462..288162a4a 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -191,8 +191,7 @@ function hosting_task_devshop_pull_form($node) { '#default_value' => 1, ); - $has_features = _devshop_projects_site_has_module($node, 'features'); - if ($has_features){ + if (_devshop_projects_project_has_module($node, 'features')){ $form['revert'] = array( '#title' => t('Revert all features after code pull?'), '#type' => 'checkbox', @@ -242,7 +241,7 @@ function hosting_task_devshop_sync_form($node) { '#type' => 'checkbox', '#default_value' => 1, ); - if (_devshop_projects_site_has_module($node, 'features')){ + if (_devshop_projects_project_has_module($node, 'features')){ $form['revert'] = array( '#title' => t('Revert all features on Destination after content sync?'), '#type' => 'checkbox', From 74b685d87997ffc0936ae4af5242e952c088e4b4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 23:49:15 -0500 Subject: [PATCH 0275/3476] cleaning up devshop_hosting tasks documentation --- README.txt | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/README.txt b/README.txt index 846dfc4dc..97dbc1771 100644 --- a/README.txt +++ b/README.txt @@ -78,9 +78,13 @@ staging, testing, or development. Use these commands with caution. All tasks have specific permissions, so you can grant roles individual tasks. -1. Pull Code | drush @alias provision-devshop-pull | drush @alias pdp - This task pulls your code, runs new updates, reverts features, and clears - caches. It can be used as a Deployment task, for test sites +1. Pull Code + $ drush @project_NAME provision-devshop-pull ENVIRONMENT + + This task runs on the dev platform for this project. It runs git pull, and + optionally runs new updates, reverts features, and clears caches. It is used + to keep the dev server up to date on every commit via the devshop_pull module, + and can be used as the deployment task. - Git Pull the code for your site's platform. - Then, all optionally: @@ -88,24 +92,24 @@ All tasks have specific permissions, so you can grant roles individual tasks. - Revert all Features modules - Clear caches -2. Commit Features | drush @alias provision-devshop-commit | drush @alias pdc - This task integrates with Features.module to make it very easy to recreate and - commit your features +2. Commit Features + $ drush @project_NAME provision-devshop-commit ENVIRONMENT --message="My Commit" + + This task integrates with Features.module to make it very easy to commit + your changes to your features. - Calls drush features-update-all - Commits the result, with a part automated and part customized commit message. - (Optionally) pushes the commits. - (Optionally) force-reverts after a commit. -3. Sync Content | drush provision-devshop-sync @source @destination | drush pds @source @destination - This task pulls content down from a site that has a drush alias on the current - system. Currently any alias can be entered when Syncing content. Eventually, - the site will store its default "source" site. +3. Sync Content + $ drush @project_NAME provision-devshop-sync SOURCE_ENVIRONMENT DESTINATION_ENVIRONMENT + + This task makes it easy to syncronize the database and filesdown from other + environments within the project. - WARNING: This command is built as a backend command. There are NO PROMPTS - before the scripts will sql-drop the @destination database. It should NEVER - be used on a production site. Once "DevShop Environments" is in place, we can - prevent this command from even being called on a "live" environment. + WARNING: This will DESTROY the destination site's database! This task: - (optionally) Pulls code From 2114722dbb45870e77c8d4f195a1e16ab2627d79 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Dec 2012 23:52:03 -0500 Subject: [PATCH 0276/3476] cleaning up hosting readme even more. --- README.txt | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/README.txt b/README.txt index 97dbc1771..c1e5188fc 100644 --- a/README.txt +++ b/README.txt @@ -9,7 +9,6 @@ based development workflow. About DevShop ------------- - The goals of DevShop are... 1. to simplify management of multiple environments for multiple Drupal projects. @@ -24,7 +23,6 @@ For installation instructions, see INSTALL.txt. DevShop Projects ---------------- - DevShop functionality centers around "Projects". Aegir Project nodes store a Git URL, the code path, the "base url", and the branches of the remote repository. @@ -61,22 +59,12 @@ To complete this step, the Verify Project task must finish. On this page, you choose if you want dev, test, or live platforms and what branches each should live on. You can also choose the branches of your git repository you wish to create platforms and sites for. - - - - Provision Commands & Hostmaster Tasks ------------------------------------- +DevShop contains a set of features that make Drupal site building within a +version-controlled code workflow quick and easy. -Once enabled, Aegir Sites will have 3 new Tasks available to them. - -NOTE: Not all tasks should be run on certain sites. It is up to YOU to decide -where and when to run these tasks. DevShop is NOT aware of which site is live, -staging, testing, or development. Use these commands with caution. - -@TODO: More constrained tasks are being created as tasks on projects. - -All tasks have specific permissions, so you can grant roles individual tasks. +All tasks are run on project nodes, and all tasks have specific permissions, so you can grant roles the permission to fire specific tasks. 1. Pull Code $ drush @project_NAME provision-devshop-pull ENVIRONMENT From 1b3263fbf860b7711f28392ead0a100894d79c2f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Dec 2012 00:03:31 -0500 Subject: [PATCH 0277/3476] adding validation to prevent attempts to sync to self. --- devshop_projects/inc/tasks.inc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 288162a4a..abcab43c8 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -254,5 +254,16 @@ function hosting_task_devshop_sync_form($node) { '#type' => 'checkbox', '#default_value' => 1, ); + $form['#validate'][] = 'hosting_task_devshop_sync_form_validate'; return $form; } + +/** + * Validation for hosting_task_devshop_sync_form() + */ +function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ + // Can't sync to self + if ($form_state['values']['parameters']['source'] == $form_state['values']['parameters']['destination']){ + form_set_error('source', t('The source cannot be the same as the destination.')); + } +} \ No newline at end of file From b8f07ae667b4931c87535bd81e88957076994098 Mon Sep 17 00:00:00 2001 From: Herman van Rink Date: Mon, 17 Dec 2012 10:45:44 +0100 Subject: [PATCH 0278/3476] Issue #1868284 by helmo: Fixed sql updates for changed column. --- devshop_projects_testing/devshop_projects_testing.module | 2 +- devshop_pull/devshop_pull.install | 2 +- devshop_pull/devshop_pull.module | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_projects_testing/devshop_projects_testing.module index d098bab8e..b100b2bd0 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_projects_testing/devshop_projects_testing.module @@ -113,7 +113,7 @@ function devshop_projects_testing_access($node, $type) { return FALSE; } - if ($obj->env_type == 'live' || $obj->env_type == 'dev') { + if ($obj->environment == 'live' || $obj->environment == 'dev') { return FALSE; } diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index dd85f04ac..40954c00c 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -65,7 +65,7 @@ function devshop_pull_install() { drupal_install_schema('devshop_pull'); //Enable pull for dev platform - $result = db_query("SELECT DISTINCT object_nid FROM {hosting_devshop_project_object} WHERE env_type='dev' AND object_type='platform'"); + $result = db_query("SELECT DISTINCT object_nid FROM {hosting_devshop_project_object} WHERE environment='dev' AND object_type='platform'"); while ($item = db_fetch_object($result)) { db_query("INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, pull_enabled, pull_reset) VALUES(%d, 1, 0)", $result->object_nid); diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 586d6ff52..e9add82af 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -180,7 +180,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { } } elseif ($node->type == 'platform') { - $data_project = db_fetch_array(db_query('SELECT project_nid, env_type AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); + $data_project = db_fetch_array(db_query('SELECT project_nid, environment AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); if (isset($data_project['project_nid'])) { $data = db_fetch_array(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); return $data; From 31258fe7a2076b462c0a99faa282f8d29db96fde Mon Sep 17 00:00:00 2001 From: helmo Date: Mon, 17 Dec 2012 12:01:21 +0100 Subject: [PATCH 0279/3476] Issue #1790374 by helmo: Fixed Some code style things. --- devshop_log/devshop_log.module | 3 ++ devshop_pull/devshop_pull.inc | 14 +++++----- devshop_pull/devshop_pull.module | 47 ++++++++++++++++---------------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index ddac5be54..20436398c 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -122,10 +122,13 @@ function _devshop_log_load($nid) { // Get our current working directory $cwd = getcwd(); + // Switch to the repo directory chdir($node->publish_path); + // Fetch the log $output = shell_exec("$git_path log $log_count_opt 2>&1"); + // Change back to our original work directory chdir($cwd); diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 2299b369c..7d3b943a3 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -5,7 +5,7 @@ * Menu callback that is invoked by GitHub WebHook facility to create * a code pull task. * - * @TODO: Save "Last Pull" and "Last Pull Status" when git pull task is complete. + * @TODO: Save "Last Pull" and "Last Pull Status" when git pull task is complete. ... DONE?? seems to be in place */ function devshop_pull_callback($project, $hash) { @@ -66,10 +66,10 @@ function devshop_pull_callback($project, $hash) { return; } - //Search platforms with pull enable of this project + // Search platforms with pull enable of this project $platforms = $pnode->project_objects['platform']; - //Put timestamp + // Put timestamp db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); foreach ($platforms as $platform_nid => $site) { @@ -89,10 +89,10 @@ function devshop_pull_callback($project, $hash) { * and given to the GitHub WebHook to invoke a pull after a commit. */ function _devshop_pull_callback_url($node) { - return url(DEVSHOP_PULL_CALLBACK_URL . - '/' . $node->hosting_name . - '/' . _devshop_pull_hash_create($node), - array('absolute' => TRUE)); + return url(DEVSHOP_PULL_CALLBACK_URL + . '/' . $node->hosting_name + . '/' . _devshop_pull_hash_create($node), + array('absolute' => TRUE)); } /** diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index e9add82af..dd0f72fcb 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -59,29 +59,28 @@ function devshop_pull_hosting_queues() { /** * Implements hook_form_alter(). */ -function devshop_pull_form_alter(&$form, &$form_state, $form_id){ - if ($form_id == 'project_node_form'){ +function devshop_pull_form_alter(&$form, &$form_state, $form_id) { + if ($form_id == 'project_node_form') { // Get node $node = $form['#node']; // Get default value - if (is_null($node->pull_method)){ + if (is_null($node->pull_method)) { $node->pull_method = DEVSHOP_PULL_DISABLED; } $form['pull_method'] = array( '#title' => 'Automatic Git Pull Method', '#type' => 'radios', - '#description' => t('Choose the method of regularly calling "Pull Code". See !link to configure the queue. See !link2 to configure URL Callback.', array( + '#description' => t('Choose the method of regularly calling "Pull Code". See !link to configure the queue. See !link2 to configure URL Callback.', array( '!link' => l(t('Hosting > Queues'), 'admin/hosting/queues'), '!link2' => l(t('Hosting > DevShop Pull Settings'), 'admin/hosting/devshop_pull') )), '#default_value' => $node->pull_method, '#options' => array( - DEVSHOP_PULL_DISABLED => t('Pull disabled.'), - DEVSHOP_PULL_QUEUE => t('Pull on queue (every minute).'), - DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), - + DEVSHOP_PULL_DISABLED => t('Pull disabled.'), + DEVSHOP_PULL_QUEUE => t('Pull on queue (every minute).'), + DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), ), ); } @@ -131,13 +130,13 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { ($pnode->pull_method == DEVSHOP_PULL_QUEUE? t('Hosting Queue'): (t('Disabled')))); - if ($pnode->pull_method == DEVSHOP_PULL_CALLBACK){ + if ($pnode->pull_method == DEVSHOP_PULL_CALLBACK) { module_load_include('inc', 'devshop_pull'); $url = _devshop_pull_callback_url($pnode); $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); } - if (isset($pnode) && $pnode->pull_method != DEVSHOP_PULL_DISABLED){ + if (isset($pnode) && $pnode->pull_method != DEVSHOP_PULL_DISABLED) { $node->content['devshop_pull'] = array( '#type' => 'fieldset', '#title' => t('Pull Configuration'), @@ -153,7 +152,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { '#type' => 'item', '#title' => t('Reset on Pull'), '#weight' => 31, - '#value' => $node->pull_reset? t('Enabled'): t('Disabled'), + '#value' => $node->pull_reset ? t('Enabled') : t('Disabled'), ); $node->content['devshop_pull']['last_pull'] = array( '#type' => 'item', @@ -165,19 +164,19 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { '#type' => 'item', '#title' => t('Last pull status'), '#weight' => 33, - '#value' => $pnode->last_pull_status == 1? t('OK'): ($pnode->last_pull? t('Failed'): t('Never Pulled')), + '#value' => $pnode->last_pull_status == 1 ? t('OK') : ($pnode->last_pull ? t('Failed') : t('Never Pulled')), ); } } break; case 'load': if ($node->type == 'project') { - $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); - if (!empty($data->project_nid)){ - $node->pull_method = $data->pull_method; - $node->last_pull = $data->last_pull; - $node->last_pull_status = $data->last_pull_status; - } + $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); + if (!empty($data->project_nid)) { + $node->pull_method = $data->pull_method; + $node->last_pull = $data->last_pull; + $node->last_pull_status = $data->last_pull_status; + } } elseif ($node->type == 'platform') { $data_project = db_fetch_array(db_query('SELECT project_nid, environment AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); @@ -190,8 +189,8 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { case 'insert': case 'update': if ($node->type == 'project') { - db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); + db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); } elseif ($node->type == 'platform') { db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); @@ -265,16 +264,16 @@ function devshop_pull_get_all_sites($nid_platform) { /** * Helper for creating the pull code tasks */ -function devshop_pull_task($nid){ +function devshop_pull_task($nid) { $node = node_load($nid); $nids = array(); $args = array(); - if ($node->type == 'platform'){ + if ($node->type == 'platform') { $sites = devshop_pull_get_all_sites($node->nid); print_r($sites); - foreach ($sites as $nid => $site){ + foreach ($sites as $nid => $site) { $nids[] = $nid; // When doing the pull task, don't do a db update or revert. @@ -309,7 +308,7 @@ print_r($sites); } - foreach ($nids as $nid){ + foreach ($nids as $nid) { hosting_add_task($nid, 'devshop-pull', $args[$nid]); } } From 82f2471129dbb924d8b98e98c7add72ed729ea37 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 10:45:06 -0500 Subject: [PATCH 0280/3476] fixing "preparing-project" status problem. --- devshop_projects/devshop_projects.module | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 065e0fc0c..ab2d8ccbc 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -121,7 +121,7 @@ function devshop_project_status($node){ $sites_installing = FALSE; // PROJECT STATUS - if (empty($node->project_objects) || empty($node->git_branches)){ + if (empty($node->project_objects) && empty($node->git_branches)){ return 'preparing-project'; } @@ -149,6 +149,9 @@ function devshop_project_status($node){ } else { $sites_ready = FALSE; } + + + // @TODO: This is so rough! We can do better. return $sites_ready? 'sites_ready': ( $sites_installing? 'sites_installing': ( $platforms_ready? 'platforms_ready': 'platforms_verifying' From 234836b06d379f5e82624d6a9244062b78421f18 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 11:07:19 -0500 Subject: [PATCH 0281/3476] removing devshop message watchdog constant --- devshop_pull/devshop_pull.inc | 14 +++++++------- devshop_pull/devshop_pull.module | 25 +++++++++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 7d3b943a3..cb274836f 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -20,19 +20,19 @@ function devshop_pull_callback($project, $hash) { // Make sure the client's IP address is on the list if (!in_array(ip_address(), $acl)) { $message = ip_address() . " is not authorized to invoke a Pull Code request."; - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); + watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; return; } $message = "GitHub post-receive hook invoked by " . ip_address(); - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); + watchdog('devshop', $message, array(), WATCHDOG_INFO); print "$message
"; if (strlen($project) < 1 || strlen($hash) != 32) { $message = "Invalid/missing parameters in URL!"; - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); + watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; return; } @@ -45,7 +45,7 @@ function devshop_pull_callback($project, $hash) { // Load the entire node if (!$pnode = node_load($pnid)) { $message = "Unable to load project node!"; - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); + watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; return; } @@ -53,7 +53,7 @@ function devshop_pull_callback($project, $hash) { // Make sure the security code is valid if (_devshop_pull_hash_create($pnode) != $hash) { $message = "Security code $hash is not valid!"; - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); + watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; return; } @@ -61,7 +61,7 @@ function devshop_pull_callback($project, $hash) { // Make sure the platform has pull callback enabled if ($pnode->pull_method != DEVSHOP_PULL_CALLBACK){ $message = "Project is NOT configured to use Pull Code URL callback!"; - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_ERROR); + watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; return; } @@ -77,7 +77,7 @@ function devshop_pull_callback($project, $hash) { $enabled = db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); if ($enabled) { $message = "devshop_pull_task($platform_nid)"; - watchdog(DSGH_WD_TYPE, $message, array(), WATCHDOG_INFO); + watchdog('devshop', $message, array(), WATCHDOG_INFO); print "$message
"; devshop_pull_task($platform_nid); } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index dd0f72fcb..103454afe 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -1,20 +1,26 @@ array('administer hosting settings'), 'file' => 'devshop_pull.settings.inc', ); - $items[DEVSHOP_PULL_CALLBACK_URL] = array( + $items['devshop/pull'] = array( 'page callback' => 'devshop_pull_callback', - 'access arguments' => array('access devshop pull callback'), 'file' => 'devshop_pull.inc', 'type' => MENU_CALLBACK, ); @@ -60,6 +65,8 @@ function devshop_pull_hosting_queues() { * Implements hook_form_alter(). */ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { + + // On Projects, add "Choose Pull Method" if ($form_id == 'project_node_form') { // Get node $node = $form['#node']; @@ -84,6 +91,8 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { ), ); } + // On Projects, choose if Pull on Commit is enabled, and if "reset --hard" + // should be used to keep the codebase clean. if ($form_id == 'platform_node_form') { $platform = $form['#node']; if (isset($platform->project_nid)) { From de8dd5a52d2c02f4ca15ad86cae4a05379fc2dfc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 11:15:16 -0500 Subject: [PATCH 0282/3476] better messaging, comments, and putting back constant for callback URL --- devshop_pull/devshop_pull.inc | 5 ++++- devshop_pull/devshop_pull.module | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index cb274836f..7fc3163f8 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -25,7 +25,8 @@ function devshop_pull_callback($project, $hash) { return; } - $message = "GitHub post-receive hook invoked by " . ip_address(); + // Log It + $message = "[DEVSHOP] Commit Received! invoked by " . ip_address(); watchdog('devshop', $message, array(), WATCHDOG_INFO); print "$message
"; @@ -72,6 +73,8 @@ function devshop_pull_callback($project, $hash) { // Put timestamp db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); + // @TODO: Lets provide much better messaging here. + // Go through each platform that is pull enabled and add a "pull code" task. foreach ($platforms as $platform_nid => $site) { //If pull enabled then add to task $enabled = db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 103454afe..81bc98588 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -16,6 +16,9 @@ define('DEVSHOP_PULL_DISABLED', 0); define('DEVSHOP_PULL_QUEUE', 1); define('DEVSHOP_PULL_CALLBACK', 2); +// The base URL to use for the Post Commit callback. +define('DEVSHOP_PULL_CALLBACK_URL', 'devshop/pull'); + /** * Implementation of hook_perm() */ @@ -37,7 +40,7 @@ function devshop_pull_menu() { 'access arguments' => array('administer hosting settings'), 'file' => 'devshop_pull.settings.inc', ); - $items['devshop/pull'] = array( + $items[DEVSHOP_PULL_CALLBACK_URL] = array( 'page callback' => 'devshop_pull_callback', 'file' => 'devshop_pull.inc', 'type' => MENU_CALLBACK, From 63ee12adeab703db5fc220abadf77d4beae02cd9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 11:16:28 -0500 Subject: [PATCH 0283/3476] saving status and changing placement of sql UPDATE --- devshop_pull/devshop_pull.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 7fc3163f8..cb0c17022 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -70,9 +70,6 @@ function devshop_pull_callback($project, $hash) { // Search platforms with pull enable of this project $platforms = $pnode->project_objects['platform']; - // Put timestamp - db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $pnid); - // @TODO: Lets provide much better messaging here. // Go through each platform that is pull enabled and add a "pull code" task. foreach ($platforms as $platform_nid => $site) { @@ -85,6 +82,9 @@ function devshop_pull_callback($project, $hash) { devshop_pull_task($platform_nid); } } + + // Put timestamp + db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $pnid); } /** From aaf912b249930438a05e1ea82889cec65c9415fa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 11:20:18 -0500 Subject: [PATCH 0284/3476] Better message --- devshop_pull/devshop_pull.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index cb0c17022..fe7f704b8 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -76,7 +76,7 @@ function devshop_pull_callback($project, $hash) { //If pull enabled then add to task $enabled = db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); if ($enabled) { - $message = "devshop_pull_task($platform_nid)"; + $message = "Platform $site Pull on Commit is enabled. Creating Pull Code task. (NID: $platform_nid)"; watchdog('devshop', $message, array(), WATCHDOG_INFO); print "$message
"; devshop_pull_task($platform_nid); From 96871dd5feab535289fdf3dbf2ef345200165518 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 11:20:37 -0500 Subject: [PATCH 0285/3476] making callback access callback TRUE. the function handles access control --- devshop_pull/devshop_pull.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 81bc98588..bcbb5236e 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -42,6 +42,7 @@ function devshop_pull_menu() { ); $items[DEVSHOP_PULL_CALLBACK_URL] = array( 'page callback' => 'devshop_pull_callback', + 'access callback' => TRUE, 'file' => 'devshop_pull.inc', 'type' => MENU_CALLBACK, ); From 395cbecb820e97267f7e5b99f84b4f7b1b41c79a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 11:28:13 -0500 Subject: [PATCH 0286/3476] adding comments, @todos --- devshop_pull/devshop_pull.inc | 5 +++++ devshop_pull/devshop_pull.module | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index fe7f704b8..97894f45a 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -71,6 +71,11 @@ function devshop_pull_callback($project, $hash) { $platforms = $pnode->project_objects['platform']; // @TODO: Lets provide much better messaging here. + // @TODO: Check for existing Pull Code tasks. We don't want to fire a task + // if one is already running. (Is this true? What if the second commit + // doesn't get pulled by the first Pull Code Task? server might get out of + // date.) + // Go through each platform that is pull enabled and add a "pull code" task. foreach ($platforms as $platform_nid => $site) { //If pull enabled then add to task diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index bcbb5236e..9fb56ab4d 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -275,7 +275,14 @@ function devshop_pull_get_all_sites($nid_platform) { } /** - * Helper for creating the pull code tasks + * Helper for creating the pull code tasks. + * + * We have to set options here for automated Pull Code tasks. + * + * @TODO: Clarify this code... are we calling it on projects or platforms? + * + * @param $nid + * A project OR platform node. */ function devshop_pull_task($nid) { @@ -285,7 +292,6 @@ function devshop_pull_task($nid) { $args = array(); if ($node->type == 'platform') { $sites = devshop_pull_get_all_sites($node->nid); -print_r($sites); foreach ($sites as $nid => $site) { $nids[] = $nid; From 1f4a57d4c2494dec9f4148e2a0f6bcc024eba67e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 12:18:44 -0500 Subject: [PATCH 0287/3476] major cleanup/refactor. one function, one parameter. --- devshop_pull/devshop_pull.inc | 32 +++---------- devshop_pull/devshop_pull.module | 79 +++++++------------------------- 2 files changed, 23 insertions(+), 88 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 97894f45a..b0bd49a08 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -40,11 +40,11 @@ function devshop_pull_callback($project, $hash) { // Based on the project name, get the node ID for the project node. // Is need remove the prefix for find the nid. - $pnid = db_result(db_query( + $project_nid = db_result(db_query( "SELECT nid FROM {hosting_context} WHERE name = '%s'", str_replace('project_', '', $project))); // Load the entire node - if (!$pnode = node_load($pnid)) { + if (!$project_node = node_load($project_nid)) { $message = "Unable to load project node!"; watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; @@ -52,7 +52,7 @@ function devshop_pull_callback($project, $hash) { } // Make sure the security code is valid - if (_devshop_pull_hash_create($pnode) != $hash) { + if (_devshop_pull_hash_create($project_node) != $hash) { $message = "Security code $hash is not valid!"; watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; @@ -60,36 +60,16 @@ function devshop_pull_callback($project, $hash) { } // Make sure the platform has pull callback enabled - if ($pnode->pull_method != DEVSHOP_PULL_CALLBACK){ + if ($project_node->pull_method != DEVSHOP_PULL_CALLBACK){ $message = "Project is NOT configured to use Pull Code URL callback!"; watchdog('devshop', $message, array(), WATCHDOG_ERROR); print "$message
"; return; } - - // Search platforms with pull enable of this project - $platforms = $pnode->project_objects['platform']; - - // @TODO: Lets provide much better messaging here. - // @TODO: Check for existing Pull Code tasks. We don't want to fire a task - // if one is already running. (Is this true? What if the second commit - // doesn't get pulled by the first Pull Code Task? server might get out of - // date.) - // Go through each platform that is pull enabled and add a "pull code" task. - foreach ($platforms as $platform_nid => $site) { - //If pull enabled then add to task - $enabled = db_result(db_query("SELECT pull_enabled FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); - if ($enabled) { - $message = "Platform $site Pull on Commit is enabled. Creating Pull Code task. (NID: $platform_nid)"; - watchdog('devshop', $message, array(), WATCHDOG_INFO); - print "$message
"; - devshop_pull_task($platform_nid); - } - } + // This does the hard work of figuring out what tasks to create. + devshop_pull_project($project_node->nid); - // Put timestamp - db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $pnid); } /** diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 9fb56ab4d..734387a00 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -226,7 +226,6 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { */ function devshop_pull_get_platforms($limit = 5) { $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); - return db_result($result); } @@ -236,7 +235,7 @@ function devshop_pull_get_platforms($limit = 5) { function devshop_pull_hosting_devshop_pull_task_rollback($task, $data) { $project = node_load($task->ref->project_nid); watchdog('devshop', 'Pull FAILED on project: @alias', array('@alias' => $project->title)); - db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $task->ref->project_nid); + db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $task->ref->project_nid); } /** @@ -256,78 +255,34 @@ function hosting_pull_queue($count) { while ($project = db_fetch_object($result)) { - devshop_pull_task($project->project_nid); + devshop_pull_project($project->project_nid); module_invoke_all('devshop_pull', $project); } } - -/** - * Helper for get all sites of one platform - */ -function devshop_pull_get_all_sites($nid_platform) { - $names = array(); - $result = db_query("SELECT title, hs.nid FROM {hosting_site} hs LEFT JOIN {node} n ON hs.nid = n.nid WHERE platform=%d", $nid_platform); - while ($node = db_fetch_object($result)) { - $names[$node->nid] = $node->title; - } - return $names; -} - /** - * Helper for creating the pull code tasks. - * - * We have to set options here for automated Pull Code tasks. - * - * @TODO: Clarify this code... are we calling it on projects or platforms? + * Prepares a "Pull Code" task for a project. + * + * @param $project_nid + * A project nid. * - * @param $nid - * A project OR platform node. + * Platforms in a project must be enabled to have this command run on them. */ -function devshop_pull_task($nid) { +function devshop_pull_project($project_nid) { + // Search platforms with pull enabled for this project + $results = db_queryd("SELECT pull_reset, environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND project_nid = %d", $project_nid); - $node = node_load($nid); - - $nids = array(); - $args = array(); - if ($node->type == 'platform') { - $sites = devshop_pull_get_all_sites($node->nid); - foreach ($sites as $nid => $site) { - $nids[] = $nid; - - // When doing the pull task, don't do a db update or revert. - // Just clear all of the caches - $args[$nid] = array( - 'reset' => $node->pull_reset, + while ($info = db_fetch_object($results)){ + $args = array( + 'environment' => $info->environment, + 'reset' => $info->pull_reset, 'update' => 0, 'revert' => 0, 'cache' => 1, ); - } - } - else { - //Search platform with pull enabled of this project - $platforms = $node->project_objects['platform']; - - - foreach ($platforms as $platform_nid => $platform) { - //If pull enabled then add to task - $data = db_fetch_object(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_platforms} WHERE platform_nid=%d", $platform_nid)); - if ($data->pull_enabled) { - $nids[] = $platform_nid; - $args[$platform_nid] = array( - 'reset' => $data->pull_reset, - 'update' => 0, - 'revert' => 0, - 'cache' => 1, - 'sites' => devshop_pull_get_all_sites($platform_nid), - ); - } - } + hosting_add_task($project_nid, 'devshop-pull', $args); } - - foreach ($nids as $nid) { - hosting_add_task($nid, 'devshop-pull', $args[$nid]); - } + // Put timestamp + db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $project_nid); } From f37aa0566012629ad1bce7e84a3c1ef703a8dbb2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 14:28:28 -0500 Subject: [PATCH 0288/3476] major refactoring to allow multiple platforms to be pulled per single git pull task --- devshop_projects/devshop_projects.drush.inc | 5 ++-- devshop_projects/inc/tasks.inc | 32 ++++++++++++++++++--- devshop_pull/devshop_pull.module | 22 ++++++++------ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 29d560562..683c83015 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -28,11 +28,10 @@ function drush_devshop_projects_pre_hosting_task() { // Pull if ($task->ref->type == 'project' && $task->task_type == 'devshop-pull') { - $task->args['environment'] = $task->task_args['environment']; + $task->args['environments'] = $task->task_args['environments']; $task->options['update'] = $task->task_args['update']; - $task->options['revert'] = $task->task_args['revert']; + $task->options['revert'] = !empty($task->task_args['revert']); $task->options['cache'] = $task->task_args['cache']; - $task->options['sites'] = explode('|', $task->task_args['sites']); } // Commit diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index abcab43c8..9c46be6d0 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -57,14 +57,15 @@ function devshop_projects_hosting_tasks() { /** * Helper to provide a select list of environments for this project */ -function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description, $key = 'environment', $title = 'Environment') { +function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description, $key = 'environment', $title = 'Environment', $type = 'radios') { // @TODO: Add a check here. Sometimes we want to limit this list. $options = array_combine($node->project_objects['site'], $node->project_objects['site']); $form[$key] = array( - '#type' => 'radios', + '#type' => $type, '#title' => t($title), '#options' => $options, - '#default_value' => key($options), + '#default_value' => $type == 'radios'? key($options): array(key($options)), + '#required' => $type == 'checkboxes', '#description' => $description, ); } @@ -183,7 +184,16 @@ function hosting_task_devshop_commit_form($node) { function hosting_task_devshop_pull_form($node) { $form = array(); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to pull code to.')); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environments to pull code to.'), 'environments', 'Environments', 'checkboxes'); + + /* + *$form['environments'] = array( + '#title' => t('list of environments'), + '#type' => 'textfield', + '#default_value' => 'dev test', + ); + + */ $form['update'] = array( '#title' => t('Run update.php after code pull?'), @@ -203,9 +213,23 @@ function hosting_task_devshop_pull_form($node) { '#type' => 'checkbox', '#default_value' => 1, ); + + // Add validator for environments + //$form['#validate'] = array('hosting_task_devshop_pull_form'); return $form; } +/** + * Extra submit function for hosting_task_confirm_form() + * + * @see devshop_projects_form_alter(). We had to add the submit hadler there. + */ +function hosting_task_devshop_pull_form_validate($form, &$form_state) { + $value = implode(' ', array_filter($form_state['values']['parameters']['environments'])); + form_set_value($form['parameters']['environments'], $value, $form_state); +} + + /** * Implementation of hook_hosting_task_TASK_TYPE_form(). * diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 734387a00..30e73a443 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -270,18 +270,22 @@ function hosting_pull_queue($count) { */ function devshop_pull_project($project_nid) { // Search platforms with pull enabled for this project - $results = db_queryd("SELECT pull_reset, environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND project_nid = %d", $project_nid); + $results = db_query("SELECT environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND project_nid = %d", $project_nid); + + $args = array('environments' => ''); while ($info = db_fetch_object($results)){ - $args = array( - 'environment' => $info->environment, - 'reset' => $info->pull_reset, - 'update' => 0, - 'revert' => 0, - 'cache' => 1, - ); - hosting_add_task($project_nid, 'devshop-pull', $args); + $args['environments'] .= $info->environment .', '; } + //$args += array( + // 'update' => 0, + // 'revert' => 0, + // 'cache' => 1, + //); + + print "[DEBUG] Adding pull task with args:"; + print_r($args); + hosting_add_task($project_nid, 'devshop-pull', $args); // Put timestamp db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $project_nid); From be5311fb5efc294c5acbc6b1cc0c9c5c9634bef9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 14:29:31 -0500 Subject: [PATCH 0289/3476] cleanup --- devshop_pull/devshop_pull.module | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 30e73a443..87d297d5f 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -275,16 +275,8 @@ function devshop_pull_project($project_nid) { $args = array('environments' => ''); while ($info = db_fetch_object($results)){ - $args['environments'] .= $info->environment .', '; + $args['environments'] .= $info->environment .' '; } - //$args += array( - // 'update' => 0, - // 'revert' => 0, - // 'cache' => 1, - //); - - print "[DEBUG] Adding pull task with args:"; - print_r($args); hosting_add_task($project_nid, 'devshop-pull', $args); // Put timestamp From 819bf572f689a737555c1628678a3340ec109422 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 14:30:15 -0500 Subject: [PATCH 0290/3476] removing debuggers --- devshop_projects/devshop_projects.drush.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 683c83015..bcbb726de 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -200,8 +200,6 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->ref->git_tags = $branches['tags']; // Save the project node now that we have branches and tags. - drush_log('[DEBUG] $task->ref = ' . print_r($task->ref, 1)); - drush_log('[DEVSHOP] Trying to node save ' . $task->ref->title); node_save($task->ref); $task->context_options['git_branches'] = $branches['branches']; From 387e382b46b9bcd121d857088ddd19eb10f07fa6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 14:48:50 -0500 Subject: [PATCH 0291/3476] fixing branch changes --- devshop_projects/devshop_projects.drush.inc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index bcbb726de..f9dc6b0cc 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -98,6 +98,8 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ } // If the platform has been verified and has a branch and git url else { + drush_log(dt("[DEVSHOP] Existing Repo found at !root. Checking out branch !branch", array('!branch' => $platform->git_branch, '!root' => $root))); + $root = $platform->publish_path; $git_remote = $platform->git_url; $git_branch = $platform->git_branch; @@ -115,14 +117,15 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ // So instead, this is the code from d()->server->shell_exec // d()->server->shell_exec($cmd); if (provision_is_local_host(d()->server->remote_host)) { - return drush_shell_cd_and_exec($root, escapeshellcmd($command)); + drush_shell_cd_and_exec($root, escapeshellcmd($command)); } else { - return drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); + drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); } - $output = drush_shell_exec_output(); - drush_log('Shell Output: ' . implode("\n", $output) , 'notice'); + $output = drush_shell_exec_output(); + drush_log('Shell Output: ' . implode("\n", $output) , 'warning'); + return $output; } } From 35c85184cadf016a95a19c8920f4d15930e357d3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 15:02:52 -0500 Subject: [PATCH 0292/3476] removing some notices --- devshop_projects/inc/create-wizard.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index d00b19581..8f4e3e839 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -28,6 +28,7 @@ function devshop_projects_create_wizard($step = NULL){ $step = current(array_keys($form_info['order'])); $project = new stdClass(); $project->git_url = ''; + $project->project_nid = NULL; $project->title = ''; $project->default_platforms = array(); $project->branch_platforms = array(); @@ -46,8 +47,10 @@ function devshop_projects_create_wizard($step = NULL){ // Check verification status $project_node = node_load($project->project_nid); - $tasks = hosting_task_fetch_tasks($project_node->nid); - $project->verify_task_status = $tasks['verify']['task_status']; + if (!empty($project_node->nid)){ + $tasks = hosting_task_fetch_tasks($project_node->nid); + } + $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; // If project verification failed, we need to ask for a new git url. if ($project->verify_task_status === HOSTING_TASK_ERROR){ From bc3efa59f65d49f718c7f29a63ea8066804a304c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 15:26:34 -0500 Subject: [PATCH 0293/3476] files sync almost working --- devshop_projects/devshop_projects.drush.inc | 1 + devshop_projects/inc/tasks.inc | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index f9dc6b0cc..20b1f7408 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -46,6 +46,7 @@ function drush_devshop_projects_pre_hosting_task() { if ($task->ref->type == 'project' && $task->task_type == 'devshop-sync') { $task->args['source'] = $task->task_args['source']; $task->args['destination'] = $task->task_args['destination']; + $task->options['files'] = $task->task_args['files']; $task->options['pull'] = $task->task_args['pull']; $task->options['update'] = $task->task_args['update']; $task->options['revert'] = $task->task_args['revert']; diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 9c46be6d0..9dedb8ce4 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -236,13 +236,6 @@ function hosting_task_devshop_pull_form_validate($form, &$form_state) { * For "Sync Content" task. */ function hosting_task_devshop_sync_form($node) { -/* Disabled until we add support for custom aliases - $form['source'] = array( - '#title' => t('Source site alias'), - '#type' => 'textfield', - '#description' => t('Enter a site alias to sync from. The alias must exist in the local system.'), - ); - */ // Get nid $nid = $node->nid; @@ -250,6 +243,12 @@ function hosting_task_devshop_sync_form($node) { devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment.'), 'destination', 'Destination'); + $form['files'] = array( + '#title' => t('Sync files folder from destination to source.'), + '#type' => 'checkbox', + '#default_value' => 0, + ); + $form['note'] = array( '#value' => '

'. t('This will DESTROY the database for Destination and replace it with the database for the selected Source.', array('!site' => l($node->title, "node/$nid"))) . '

', '#type' => 'markup', From 69be184177652de200493328c133584dca9109f7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Dec 2012 16:13:25 -0500 Subject: [PATCH 0294/3476] handling bad argument sorting in drush_hosting_tasks --- devshop_projects/devshop_projects.drush.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 20b1f7408..7f90d8735 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -44,8 +44,8 @@ function drush_devshop_projects_pre_hosting_task() { // Sync if ($task->ref->type == 'project' && $task->task_type == 'devshop-sync') { - $task->args['source'] = $task->task_args['source']; - $task->args['destination'] = $task->task_args['destination']; + $task->args[] = $task->task_args['source']; + $task->args[] = $task->task_args['destination']; $task->options['files'] = $task->task_args['files']; $task->options['pull'] = $task->task_args['pull']; $task->options['update'] = $task->task_args['update']; From 421a8bf25d0488d0531d3c85d818d053fc3536e8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 14:55:08 -0500 Subject: [PATCH 0295/3476] better project node page when sites aren't ready yet. --- devshop_projects/inc/ui.inc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 5a50b4e3b..560e8c97a 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -73,10 +73,20 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // If not ready yet: if ($node->project_status != 'sites_ready'){ + + // @TODO: Once we have a smarter status, we can redirect them back the + // create wizard. + // Site install form - $node->content['devshop']['install'] = array( - '#value' => drupal_get_form('devshop_projects_install_sites_form', $node), + $node->content['devshop'] = array( + '#value' => '

' . t('Your project is still being configured... Sites are not ready yet.') . '

', '#type' => 'markup', + '#weight' => 1, + ); + $node->content['create'] = array( + '#value' => '

' . l(t('Go back to Create Project Wizard.'), 'node/add/project/settings') . '

', + '#type' => 'markup', + '#weight' => 2, ); return $node; } From 268cd44b8f868e0e15f00732908daad128002032 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 15:06:24 -0500 Subject: [PATCH 0296/3476] Moving Install Sites form to Step 4 of wizard --- devshop_projects/inc/create-wizard.inc | 162 ++++++++++++++++++++++++- devshop_projects/inc/forms.inc | 159 ------------------------ 2 files changed, 157 insertions(+), 164 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 8f4e3e839..f3f0bf6ac 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -86,9 +86,10 @@ function devshop_projects_create_wizard_info(){ 'finish callback' => 'devshop_projects_create_wizard_finish', 'cancel callback' => 'devshop_projects_create_wizard_cancel', 'order' => array( - 'git_url' => t('Step 1: Git'), + 'git_url' => t('Step 1: Source'), 'settings' => t('Step 2: Settings'), 'environments' => t('Step 3: Platforms'), + 'sites' => t('Step 4: Sites'), ), 'forms' => array( 'git_url' => array( @@ -100,6 +101,9 @@ function devshop_projects_create_wizard_info(){ 'environments' => array( 'form id' => 'devshop_project_create_step_environments' ), + 'sites' => array( + 'form id' => 'devshop_project_create_step_sites' + ), ), ); } @@ -130,15 +134,20 @@ function devshop_projects_create_wizard_next(&$form_state) { function devshop_projects_create_wizard_finish(&$form_state) { $project = &$form_state['project']; - // Save the extra options to the project + // Save the extra options to the project node. $node = node_load($project->project_nid); $node->code_path = $project->code_path; $node->base_url = $project->base_url; - - // Everything else is done on the Node Page. We will fake the last step on - // the node page. + $node->install_profile = $project->install_profile; node_save($node); + // Create the site nodes + // @TODO: Can we speed things up here by only running install for the first, + // then "Cloning" to create the rest? + foreach ($node->project_objects['platform'] as $nid => $env){ + devshop_projects_create_site($node, node_load($nid), $env); + } + ctools_object_cache_clear('project', $form_state['cache name']); $form_state['redirect'] = 'node/' . $node->nid; } @@ -479,3 +488,146 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } } } + +/********** + * STEP 4 + * Project Settings + *********/ + +/** + * STEP 4: Form + */ +function devshop_project_create_step_sites(&$form, &$form_state) { + $project = &$form_state['project']; + $project_node = node_load($project->project_nid); + + // OUTPUT + if ($project_node->project_status == 'sites_installing') { + $form['note'] = array( + '#type' => 'item', + '#title' => t('Sites Installing'), + '#value' => t('Your sites are being installed!'), + ); + return $form; + } + elseif ($project_node->project_status == 'preparing-project') { + // @TODO: Not yet, needs more logic + // drupal_goto('node/add/project'); + } + elseif ($project_node->project_status == 'sites-installed') { + return; + } + + $profiles = array(); + $completed = TRUE; + + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + + // Display the platforms + $rows = array(); + $header = array(t('Name'), t('Branch'), t('Status'), t('Installation Profiles')); + foreach ($project_node->project_objects['platform'] as $nid => $name){ + + $platform = node_load($nid); + $row = array(); + $row[] = $name; + $row[] = $platform->git_branch; + $row[] = $platform->verified? t('Verified'): t('Verification Pending'); + + if (!$platform->verified){ + $completed = FALSE; + } + + // Collect install profiles + // This fancy footwork gives us an array with items like 'shortname' => 'Name' + $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); + + $row[] = implode(', ', $profiles[$name]); + + if (!isset($available_profiles)){ + $available_profiles = $profiles[$name]; + } else { + // @TODO: This joins profiles by name, not profile name. + $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + } + + // Build site options + $defaults = array(); + $defaults['dev']['pull'] = 1; + $defaults['dev']['dev_modules'] = 1; + $defaults['test']['run_tests'] = 1; + $defaults['live']['live_modules'] = 1; + + // Store rows for display + $rows[] = $row; + } + + // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" + $form['platforms'] = array( + '#type' => 'markup', + '#value' => theme('table', $header, $rows), + ); + + // Provide something for people while they wait. + if (!$completed){ + $form['help'] = array( + '#type' => 'markup', + '#value' => t('Please wait while we download and verify your drupal code.'), + ); + + return $form; + + } elseif (count($available_profiles) == 0) { + $form['error'] = array( + '#type' => 'markup', + '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. This can happen if you chose branches with two different versions of Drupal. Please edit codebase so that your desired install profile is in all branches.'), + ); + return $form; + } else { + // Install Profile + // Sensible default? + // Lets go with standard for now... we can update later. + if (isset($available_profiles['standard'])) { + $default_profile = 'standard'; + } + // If 'drupal' profile exists, it is likely drupal6! + elseif (isset($available_profiles['drupal'])) { + $default_profile = 'drupal'; + } + + // @TODO: Handle no available profiles + $form['install_profile'] = array( + '#type' => 'radios', + '#options' => $available_profiles, + '#title' => t('Project Install Profile'), + '#required' => 1, + '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), + '#default_value' => $default_profile, + ); + } +} + +/** + * STEP 4: Validate + */ +function devshop_project_create_step_sites_validate(&$from, &$form_state) { + $project = &$form_state['project']; + + if (empty($form_state['values']['install_profile'])){ + form_set_error('install_profile', t('You must choose an install profile')); + } +} + +/** + * STEP 4: Submit + * + * Save install profile to the project object (project and site node creation + * happens on wizard finish.) + */ +function devshop_project_create_step_sites_submit(&$from, &$form_state) { + $project = &$form_state['project']; + $project->install_profile = $form_state['values']['install_profile']; +} diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 4ecb9810d..e1f9080f0 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -173,165 +173,6 @@ function devshop_projects_validate($node, &$form) { } } -/** - * Form for site installation - * - * Displayed on a PROJECT node page. This is only used once per project, - * during the "new project" process. - */ -function devshop_projects_install_sites_form($form_state, $project_node) { - - // OUTPUT - if ($project_node->project_status == 'sites_installing') { - $form['note'] = array( - '#type' => 'item', - '#title' => t('Sites Installing'), - '#value' => t('Your sites are being installed!'), - ); - return $form; - } - elseif ($project_node->project_status == 'preparing-project') { - // @TODO: Not yet, needs more logic - // drupal_goto('node/add/project'); - } - elseif ($project_node->project_status == 'sites-installed') { - return; - } - - $profiles = array(); - $completed = TRUE; - - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - - // Display the platforms - $rows = array(); - $header = array(t('Name'), t('Branch'), t('Status'), t('Installation Profiles')); - foreach ($project_node->project_objects['platform'] as $nid => $name){ - - $platform = node_load($nid); - $row = array(); - $row[] = $name; - $row[] = $platform->git_branch; - $row[] = $platform->verified? t('Verified'): t('Verification Pending'); - - if (!$platform->verified){ - $completed = FALSE; - } - - // Collect install profiles - // This fancy footwork gives us an array with items like 'shortname' => 'Name' - $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); - - $row[] = implode(', ', $profiles[$name]); - - if (!isset($available_profiles)){ - $available_profiles = $profiles[$name]; - } else { - // @TODO: This joins profiles by name, not profile name. - $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); - } - - // Build site options - $defaults = array(); - $defaults['dev']['pull'] = 1; - $defaults['dev']['dev_modules'] = 1; - $defaults['test']['run_tests'] = 1; - $defaults['live']['live_modules'] = 1; - - // Store rows for display - $rows[] = $row; - } - - // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" - $form['platforms'] = array( - '#type' => 'markup', - '#value' => theme('table', $header, $rows), - ); - - // Provide something for people while they wait. - if (!$completed){ - $form['help'] = array( - '#type' => 'markup', - '#value' => t('Please wait while we download and verify your drupal code.'), - ); - - return $form; - - } elseif (count($available_profiles) == 0) { - $form['error'] = array( - '#type' => 'markup', - '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. This can happen if you chose branches with two different versions of Drupal. Please edit codebase so that your desired install profile is in all branches.'), - ); - return $form; - } else { - // Install Profile - // Sensible default? - // Lets go with standard for now... we can update later. - if (isset($available_profiles['standard'])) { - $default_profile = 'standard'; - } - // If 'drupal' profile exists, it is likely drupal6! - elseif (isset($available_profiles['drupal'])) { - $default_profile = 'drupal'; - } - - // @TODO: Handle no available profiles - $form['install_profile'] = array( - '#type' => 'radios', - '#options' => $available_profiles, - '#title' => t('Project Install Profile'), - '#required' => 1, - '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), - '#default_value' => $default_profile, - ); - } - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Create Sites'), - ); - return $form; -} - -/** - * Validates for site installation - */ -function devshop_projects_install_sites_form_validate(&$form, &$form_state){ - if (empty($form_state['values']['install_profile'])){ - form_set_error('install_profile', t('You must choose an install profile')); - } -} - -/** - * Submit for site installation - * - * I believe, based on the fact that provision has to create - * the site aliases before it runs provision-install, we have - * to have a new hostmaster task here. - * - * If we save the install_profile in the project context, - * the task could easily create the new sites. - */ -function devshop_projects_install_sites_form_submit(&$form, &$form_state){ - - global $user; - $project_node = node_load($form_state['values']['nid']); - $project_node->install_profile = $form_state['values']['install_profile']; - - // Save project installation profile to database - db_query('UPDATE {hosting_devshop_project} SET install_profile = "%s" WHERE nid = %d', $form_state['values']['install_profile'], $project_node->nid); - - // Create the site nodes - // @TODO: Can we speed things up here by only running install for the first, - // then "Cloning" to create the rest? - foreach ($project_node->project_objects['platform'] as $nid => $env){ - devshop_projects_create_site($project_node, node_load($nid), $env); - } -} - /** * Form for platform creation */ From add098257dca53e1436dd7fe3dc6a3d600e7d2be Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 15:10:10 -0500 Subject: [PATCH 0297/3476] adding session var for last step --- devshop_projects/inc/create-wizard.inc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f3f0bf6ac..52df38f69 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -61,10 +61,12 @@ function devshop_projects_create_wizard($step = NULL){ } } - // All forms can access $form_state['project']; $form_state['project'] = $project; + // Saving the last visited step for redirects from node + $_SESSION['last_step'] = $step; + // Generate our ctools form and output $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; @@ -150,6 +152,9 @@ function devshop_projects_create_wizard_finish(&$form_state) { ctools_object_cache_clear('project', $form_state['cache name']); $form_state['redirect'] = 'node/' . $node->nid; + + // Removing last step session variable. + unset($_SESSION['last_step']); } @@ -170,6 +175,10 @@ function devshop_projects_create_wizard_cancel(&$form_state) { if (!empty($project->project_nid)){ hosting_add_task($project->project_nid, 'delete'); } + + // Removing last step session variable. + unset($_SESSION['last_step']); + } /** From d7bd4ae61c23b6bc4b4fdebc50c8eba562e3b69a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 15:30:10 -0500 Subject: [PATCH 0298/3476] adding validation to settings form --- devshop_projects/inc/create-wizard.inc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 52df38f69..0a24b8eb3 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -337,6 +337,15 @@ function devshop_project_create_step_settings(&$form, &$form_state) { */ function devshop_project_create_step_settings_validate(&$from, &$form_state) { $project = &$form_state['project']; + + // Code path and domain must be unique + if (db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s";', $form_state['values']['code_path']))){ + form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); + } + if (db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE base_url = "%s";', $form_state['values']['base_url']))){ + form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); + } + } /** From 997977c3d4efe84116b5b7b11bfeba02bca60f99 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 15:33:48 -0500 Subject: [PATCH 0299/3476] better descriptions --- devshop_projects/inc/create-wizard.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 0a24b8eb3..0f8f385ef 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -313,7 +313,7 @@ function devshop_project_create_step_settings(&$form, &$form_state) { $form['code_path'] = array( '#type' => 'textfield', '#title' => t('Code path'), - '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), + '#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project.'), '#required' => TRUE, '#size' => 40, '#default_value' => $project->code_path, @@ -322,14 +322,12 @@ function devshop_project_create_step_settings(&$form, &$form_state) { $form['base_url'] = array( '#type' => 'textfield', '#title' => t('Primary Domain'), - '#description' => t('The domain name all sites will be built under.'), + '#description' => t('All sites will be under a subdomain of this domain.'), '#required' => TRUE, '#size' => 40, '#default_value' => $project->base_url, '#maxlength' => 255, ); - - // @TODO: Put Pull Code settings here } /** From f3bb6c3e3d709b0476dbc3ffba080ac6c8fb2651 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 16:11:02 -0500 Subject: [PATCH 0300/3476] environments step javascript --- devshop_projects/inc/create-wizard.inc | 9 +++++ devshop_projects/inc/environments.js | 49 ++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 devshop_projects/inc/environments.js diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 0f8f385ef..06626deb6 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -385,9 +385,18 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#type' => 'value', '#value' => TRUE, ); + + // Add JS that reloads the page when tasks finish. + $path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; + drupal_add_js($path); return; } + + // Add JS that reloads the page when tasks finish. + $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; + drupal_add_js($path); + $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); $form['default_platforms'] = array( diff --git a/devshop_projects/inc/environments.js b/devshop_projects/inc/environments.js new file mode 100644 index 000000000..47f05743a --- /dev/null +++ b/devshop_projects/inc/environments.js @@ -0,0 +1,49 @@ + + +Drupal.behaviors.devshopPlatforms = function() { + + // DEV + // Hide if not checked + if (!$('#edit-default-platforms-dev-enabled').attr('checked')) { + $('#edit-default-platforms-dev-branch-wrapper').hide(); + } + + // ONCLICK: + $('#edit-default-platforms-dev-enabled').change(function(e){ + if ($(this).attr('checked')) { + $('#edit-default-platforms-dev-branch-wrapper').show(); + } else { + $('#edit-default-platforms-dev-branch-wrapper').hide(); + } + }); + + // TEST + // Hide if not checked + if (!$('#edit-default-platforms-test-enabled').attr('checked')) { + $('#edit-default-platforms-test-branch-wrapper').hide(); + } + + // ONCLICK: + $('#edit-default-platforms-test-enabled').change(function(e){ + if ($(this).attr('checked')) { + $('#edit-default-platforms-test-branch-wrapper').show(); + } else { + $('#edit-default-platforms-test-branch-wrapper').hide(); + } + }); + + // LIVE + // Hide if not checked + if (!$('#edit-default-platforms-live-enabled').attr('checked')) { + $('#edit-default-platforms-live-branch-wrapper').hide(); + } + + // ONCLICK: + $('#edit-default-platforms-live-enabled').change(function(e){ + if ($(this).attr('checked')) { + $('#edit-default-platforms-live-branch-wrapper').show(); + } else { + $('#edit-default-platforms-live-branch-wrapper').hide(); + } + }); +} \ No newline at end of file From 19696d1b7a2ef480b58fc26ae6231af7e2c175e3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 16:34:55 -0500 Subject: [PATCH 0301/3476] improving step 4 display --- devshop_projects/inc/create-wizard.inc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 06626deb6..32b22f9f3 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -555,12 +555,17 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $rows = array(); $header = array(t('Name'), t('Branch'), t('Status'), t('Installation Profiles')); foreach ($project_node->project_objects['platform'] as $nid => $name){ + + // Figure out status + + $task = hosting_get_most_recent_task($nid, 'verify'); + $status = _hosting_parse_error_code($task->task_status); $platform = node_load($nid); $row = array(); $row[] = $name; $row[] = $platform->git_branch; - $row[] = $platform->verified? t('Verified'): t('Verification Pending'); + $row[] = _hosting_parse_error_code($task->task_status); if (!$platform->verified){ $completed = FALSE; @@ -570,8 +575,12 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // This fancy footwork gives us an array with items like 'shortname' => 'Name' $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); - $row[] = implode(', ', $profiles[$name]); - + if ($task->task_status == HOSTING_TASK_ERROR) { + $row[] = t('Platform verification failed! Check the task log for more details.'); + } else { + $row[] = implode(', ', $profiles[$name]); + } + if (!isset($available_profiles)){ $available_profiles = $profiles[$name]; } else { From 25b0541b1fb8f6b68551076854a4f472a30f8999 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 16:44:05 -0500 Subject: [PATCH 0302/3476] re-saving platforms so you can go back and choose new branches! --- devshop_projects/inc/create-wizard.inc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 32b22f9f3..c3427b4e4 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -511,6 +511,13 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform_node = _devshop_projects_node_create('platform', $platform); $project->platforms[$platform_name] = $platform_node->nid; } + // If the platform already exists, change the platform node branch and save it. + // @TODO: only re-save the node if the chosen branch changed. + else { + $platform_node = node_load($project->platforms[$platform_name]); + $platform_node->git_branch = $branch; + node_save($platform_node); + } } } From 338fd93c343f36a43bb9f11ae4428a2646358fe6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 16:48:29 -0500 Subject: [PATCH 0303/3476] better text --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index c3427b4e4..28b4ee61d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -583,7 +583,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); if ($task->task_status == HOSTING_TASK_ERROR) { - $row[] = t('Platform verification failed! Check the task log for more details.'); + $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); } else { $row[] = implode(', ', $profiles[$name]); } @@ -624,7 +624,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } elseif (count($available_profiles) == 0) { $form['error'] = array( '#type' => 'markup', - '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. This can happen if you chose branches with two different versions of Drupal. Please edit codebase so that your desired install profile is in all branches.'), + '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'node/add/project/environments'))) ); return $form; } else { From 0121507d049331391f87b86a3694fdfc2e5ee16d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 16:55:52 -0500 Subject: [PATCH 0304/3476] adding drupal version. --- devshop_projects/inc/create-wizard.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 28b4ee61d..d338efe5f 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -560,11 +560,10 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Display the platforms $rows = array(); - $header = array(t('Name'), t('Branch'), t('Status'), t('Installation Profiles')); + $header = array(t('Name'), t('Branch'), t('Version'), t('Verification Status'), t('Installation Profiles')); foreach ($project_node->project_objects['platform'] as $nid => $name){ // Figure out status - $task = hosting_get_most_recent_task($nid, 'verify'); $status = _hosting_parse_error_code($task->task_status); @@ -572,14 +571,16 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $row = array(); $row[] = $name; $row[] = $platform->git_branch; + $row[] = $platform->release->version; $row[] = _hosting_parse_error_code($task->task_status); - if (!$platform->verified){ + if (!$platform->verified || $task->task_status != HOSTING_TASK_SUCCESS) { $completed = FALSE; } // Collect install profiles // This fancy footwork gives us an array with items like 'shortname' => 'Name' + // @TODO: If a branch is changed, the old profiles still exist! Make sure the form $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); if ($task->task_status == HOSTING_TASK_ERROR) { From 8e0a397d4692e34db7af7b1ab2a4fe198b1d91bb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 16:58:12 -0500 Subject: [PATCH 0305/3476] removing the removal of the project node page if the status is not "sites-ready" --- devshop_projects/inc/ui.inc | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 560e8c97a..0e7fef4ed 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -73,22 +73,21 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // If not ready yet: if ($node->project_status != 'sites_ready'){ - - // @TODO: Once we have a smarter status, we can redirect them back the - // create wizard. - - // Site install form - $node->content['devshop'] = array( - '#value' => '

' . t('Your project is still being configured... Sites are not ready yet.') . '

', - '#type' => 'markup', - '#weight' => 1, - ); - $node->content['create'] = array( - '#value' => '

' . l(t('Go back to Create Project Wizard.'), 'node/add/project/settings') . '

', - '#type' => 'markup', - '#weight' => 2, - ); - return $node; + //// @TODO: Once we have a smarter status, we can redirect them back the + //// create wizard. + // + //// Site install form + //$node->content['devshop'] = array( + // '#value' => '

' . t('Your project is still being configured... Sites are not ready yet.') . '

', + // '#type' => 'markup', + // '#weight' => 1, + //); + //$node->content['create'] = array( + // '#value' => '

' . l(t('Go back to Create Project Wizard.'), 'node/add/project/settings') . '

', + // '#type' => 'markup', + // '#weight' => 2, + //); + //return $node; } modalframe_parent_js(); From a8a8eb1e3dc542eac00bfa2870ade3b81b161a8d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Dec 2012 17:34:23 -0500 Subject: [PATCH 0306/3476] code cleanup --- devshop_projects/devshop_projects.drush.inc | 81 ++++++++++----------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 7f90d8735..619a04bed 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -133,54 +133,47 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ /** * Implementation of hook_post_hosting_TASK_TYPE_task * - * If this is a new platform created by a project, we will create a site here. - * - * @TODO: This is used only for creating the new site when a "branch platform" - * is created... we should save something in the task or platform node so we - * don't have to use this logic. + * */ function devshop_projects_post_hosting_verify_task($task, $data) { - // We only case about platforms. - if ($task->ref->type != 'platform') { - return; - } - - // Get objects - $nid = $task->ref->nid; - $platform = node_load($nid); - - // If this platform isn't in a project, bail. - if (empty($platform->project_nid)){ - drush_log('[DEVSHOP] No project found for this platform.', 'notice'); - return; - } - - // Get the project - $project = node_load($platform->project_nid); - - // If the project doesn't have an install profile chosen yet, bail. - if (empty($project->install_profile)){ - drush_log('[DEVSHOP] No install profile found for this platform\'s project.', 'notice'); - return; - } - - // If the project has a site already, bail. - $sites = array_flip($project->project_objects['site']); - $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); - if (!empty($sites)){ - drush_log('[DEVSHOP] Platform already has a site.', 'notice'); - return; + // If this is a new platform created by a project, we will create a site here. + if ($task->ref->type == 'platform') { + // Get objects + $nid = $task->ref->nid; + $platform = node_load($nid); + + // If this platform isn't in a project, bail. + if (empty($platform->project_nid)){ + drush_log('[DEVSHOP] No project found for this platform.', 'notice'); + return; + } + + // Get the project + $project = node_load($platform->project_nid); + + // If the project doesn't have an install profile chosen yet, bail. + if (empty($project->install_profile)){ + drush_log('[DEVSHOP] No install profile found for this platform\'s project.', 'notice'); + return; + } + + // If the project has a site already, bail. + $sites = array_flip($project->project_objects['site']); + $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); + if (!empty($sites)){ + drush_log('[DEVSHOP] Platform already has a site.', 'notice'); + return; + } + + // live. Let's create a site based off of this platform. + drush_log('[DEVSHOP] Platform verified. Creating your site.'); + + + $site_node = devshop_projects_create_site($project, $platform, $platform->environment); + + drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); } - - // live. Let's create a site based off of this platform. - drush_log('[DEVSHOP] Platform verified. Creating your site.'); - - - $site_node = devshop_projects_create_site($project, $platform, $platform->environment); - - drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); - } /** From c46147d6411ec7501e101b29b8c577849325ea5c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 11:28:32 -0500 Subject: [PATCH 0307/3476] adding css and js files --- devshop_projects/inc/environments.css | 4 ++++ devshop_projects/inc/reload.js | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 devshop_projects/inc/environments.css create mode 100644 devshop_projects/inc/reload.js diff --git a/devshop_projects/inc/environments.css b/devshop_projects/inc/environments.css new file mode 100644 index 000000000..7e3d2a076 --- /dev/null +++ b/devshop_projects/inc/environments.css @@ -0,0 +1,4 @@ +#edit-default-platforms-dev-enabled-wrapper, #edit-default-platforms-dev-branch-wrapper { + float: left; + width: 50%; +} \ No newline at end of file diff --git a/devshop_projects/inc/reload.js b/devshop_projects/inc/reload.js new file mode 100644 index 000000000..5ffc3c13f --- /dev/null +++ b/devshop_projects/inc/reload.js @@ -0,0 +1,5 @@ + + +Drupal.behaviors.devshopReload = function() { + +} \ No newline at end of file From ed21ae89bee5cb5d3d752cbe9941508117f90cf8 Mon Sep 17 00:00:00 2001 From: helmo Date: Wed, 19 Dec 2012 16:20:30 +0100 Subject: [PATCH 0308/3476] create-wizard: Dont complain on step 1 about the git url --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index d338efe5f..2adb9009c 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -53,7 +53,7 @@ function devshop_projects_create_wizard($step = NULL){ $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; // If project verification failed, we need to ask for a new git url. - if ($project->verify_task_status === HOSTING_TASK_ERROR){ + if ($project->verify_task_status === HOSTING_TASK_ERROR && !empty($project_node->nid)){ drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); // If not on the first step, go to it. if ($step != current(array_keys($form_info['order']))){ From ee3763a23e5079aae1573372043ebe5cf248fa82 Mon Sep 17 00:00:00 2001 From: helmo Date: Wed, 19 Dec 2012 17:03:43 +0100 Subject: [PATCH 0309/3476] create-wizard: Handle an empty profile list in step 4 --- devshop_projects/inc/create-wizard.inc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 2adb9009c..c44312177 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -579,14 +579,18 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } // Collect install profiles + $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); + // This fancy footwork gives us an array with items like 'shortname' => 'Name' // @TODO: If a branch is changed, the old profiles still exist! Make sure the form - $profiles[$name] = array_combine((array) hosting_get_profiles($platform->nid, 'short_name'), (array) hosting_get_profiles($platform->nid)); + if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { + $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); - if ($task->task_status == HOSTING_TASK_ERROR) { - $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); - } else { - $row[] = implode(', ', $profiles[$name]); + if ($task->task_status == HOSTING_TASK_ERROR) { + $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); + } else { + $row[] = implode(', ', $profiles[$name]); + } } if (!isset($available_profiles)){ From f286986d06a637be7d518c2d2feba8650cbcdf00 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:01:26 -0500 Subject: [PATCH 0310/3476] Cleaning up code logic layout, better comments, and removing of submit buttons if not ready. (use $project->no_submit = TRUE if you need to hide it) --- devshop_projects/inc/create-wizard.inc | 72 ++++++++++++-------------- devshop_projects/inc/forms.inc | 7 +++ 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index c44312177..f0e442474 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -552,7 +552,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $profiles = array(); $completed = TRUE; - + $form['nid'] = array( '#type' => 'value', '#value' => $project_node->nid, @@ -563,55 +563,49 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $header = array(t('Name'), t('Branch'), t('Version'), t('Verification Status'), t('Installation Profiles')); foreach ($project_node->project_objects['platform'] as $nid => $name){ - // Figure out status + // Get verification task for this platform. $task = hosting_get_most_recent_task($nid, 'verify'); - $status = _hosting_parse_error_code($task->task_status); - $platform = node_load($nid); + $row = array(); $row[] = $name; $row[] = $platform->git_branch; $row[] = $platform->release->version; $row[] = _hosting_parse_error_code($task->task_status); + + // If platform verified successfully: + if ($task->task_status == HOSTING_TASK_SUCCESS) { - if (!$platform->verified || $task->task_status != HOSTING_TASK_SUCCESS) { - $completed = FALSE; - } - - // Collect install profiles - $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); - - // This fancy footwork gives us an array with items like 'shortname' => 'Name' - // @TODO: If a branch is changed, the old profiles still exist! Make sure the form - if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { - $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); - - if ($task->task_status == HOSTING_TASK_ERROR) { - $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); - } else { + // Collect install profiles + $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); + if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { + $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); $row[] = implode(', ', $profiles[$name]); } + + if (!isset($available_profiles)){ + $available_profiles = $profiles[$name]; + } else { + // @TODO: This joins profiles by name, not profile name. + $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + } } + // If platform verification failed: + elseif ($task->task_status == HOSTING_TASK_ERROR) { + $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); + } - if (!isset($available_profiles)){ - $available_profiles = $profiles[$name]; - } else { - // @TODO: This joins profiles by name, not profile name. - $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + // If platform has NOT verified successfully, mark process not complete. + else { + $completed = FALSE; + $row[] = t('Pending'); } - // Build site options - $defaults = array(); - $defaults['dev']['pull'] = 1; - $defaults['dev']['dev_modules'] = 1; - $defaults['test']['run_tests'] = 1; - $defaults['live']['live_modules'] = 1; - // Store rows for display $rows[] = $row; - } - - // @TODO: Make a table with checkboxes for each platform for "Pull on Commit", "Allow Commit Features", "Allow Run Tests", "Allow Sync" + } // end foreach platform + + // Output our table. $form['platforms'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), @@ -619,14 +613,16 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Provide something for people while they wait. if (!$completed){ + $project->no_submit = TRUE; $form['help'] = array( '#type' => 'markup', - '#value' => t('Please wait while we download and verify your drupal code.'), + '#value' => t('Please wait while we download and verify your drupal code. NOTE: You will have to manually reload the page after the tasks complete to continue.'), ); - return $form; - - } elseif (count($available_profiles) == 0) { + } + // If no available profiles: + elseif (count($available_profiles) == 0) { + $project->no_submit = TRUE; $form['error'] = array( '#type' => 'markup', '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'node/add/project/environments'))) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e1f9080f0..de7fa90ce 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -22,6 +22,13 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } } + // Create Project Wizard + if ($form_id == 'devshop_project_create_step_sites'){ + if ($form_state['project']->no_submit){ + unset($form['buttons']['return']); + } + } + // On Hosting Task: Create Project form, do our own submission. if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-create'){ $form['#submit'] = array('hosting_task_devshop_create_form_submit'); From cf9576a8ca70083a6ed66b9e1f3a7c8e97370756 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:02:53 -0500 Subject: [PATCH 0311/3476] removing unused code --- devshop_projects/inc/create-wizard.inc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f0e442474..fee9a6461 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -37,14 +37,6 @@ function devshop_projects_create_wizard($step = NULL){ ctools_object_cache_set('project', $form_state['cache name'], $project); } - // Particular overrides - if ($step == 'environments' && isset($project->project_nid)){ - $project_node = node_load($project->project_nid); - if (empty($project_node->git_branches)){ - //$form_info['show back'] = FALSE; - } - } - // Check verification status $project_node = node_load($project->project_nid); if (!empty($project_node->nid)){ From 17a5422265f11cf2ecc7966d6d547e8c27c0f7d0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:36:19 -0500 Subject: [PATCH 0312/3476] handle backing up and removing platforms in the wizard. --- devshop_projects/inc/create-wizard.inc | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index fee9a6461..fce729584 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -476,7 +476,10 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Get project and reset properties.. $project = &$form_state['project']; $values = $form_state['values']; - + + // Get last platforms, in case this form is re-submitted + $old_platforms = array_merge($project->default_platforms, $project->branch_platforms); + // Get platforms from form values and save into our object $project->default_platforms = array_filter($values['default_platforms']); $project->branch_platforms = array_filter($values['branch_platforms']); @@ -511,6 +514,21 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { node_save($platform_node); } } + + // For all removed platforms, trigger a delete task + $removed_platforms = array_diff_key($old_platforms, $all_platforms); + foreach ($removed_platforms as $platform_name => $branch) { + + // @TODO: Determine what to do here based on task status... + // if verify task hasn't even run yet (and has never run) we can just delete + // the platform node. + + // Create 'delete' task for this project + hosting_add_task($project->platforms[$platform_name], 'delete'); + + // Remove from project object + unset($project->platforms[$platform_name]); + } } /********** @@ -555,6 +573,12 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $header = array(t('Name'), t('Branch'), t('Version'), t('Verification Status'), t('Installation Profiles')); foreach ($project_node->project_objects['platform'] as $nid => $name){ + // If this platform is no longer in the $project object, we shouldn't show + // it here. + if (!isset($project->platforms[$name])){ + continue; + } + // Get verification task for this platform. $task = hosting_get_most_recent_task($nid, 'verify'); $platform = node_load($nid); From 78ae22c4191e1ce73b7bdf9b015aab5f6b85d028 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:36:34 -0500 Subject: [PATCH 0313/3476] Do not load project_objects if they are disabled. --- devshop_projects/inc/nodes.inc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index eeb403f92..f4cfaa41e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -176,7 +176,13 @@ function devshop_projects_load($node) { $objects = array(); while($project_object = db_fetch_object($query)) { - $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; + + // Only load if site or platform is enabled. + // FYI HOSTING_SITE_ENABLED == HOSTING_PLATFORM_ENABLED + $node = node_load($project_object->object_nid); + if ($node->{$project_object->object_type .'_status'} == HOSTING_SITE_ENABLED) { + $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; + } } $additions['project_objects'] = $objects; From 805288a0ef7425fbac24256c5d5e5fdc1948f796 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:41:23 -0500 Subject: [PATCH 0314/3476] fixing up "no_next" and "no_finish" flags. --- devshop_projects/inc/create-wizard.inc | 8 ++++++-- devshop_projects/inc/forms.inc | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index fce729584..85e4ee2e0 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -381,6 +381,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // Add JS that reloads the page when tasks finish. $path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; drupal_add_js($path); + + $project->no_next = TRUE; return; } @@ -629,7 +631,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Provide something for people while they wait. if (!$completed){ - $project->no_submit = TRUE; + $project->no_finish = TRUE; $form['help'] = array( '#type' => 'markup', '#value' => t('Please wait while we download and verify your drupal code. NOTE: You will have to manually reload the page after the tasks complete to continue.'), @@ -638,13 +640,15 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } // If no available profiles: elseif (count($available_profiles) == 0) { - $project->no_submit = TRUE; + $project->no_finish = TRUE; $form['error'] = array( '#type' => 'markup', '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'node/add/project/environments'))) ); return $form; } else { + $project->no_finish = FALSE; + // Install Profile // Sensible default? // Lets go with standard for now... we can update later. diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index de7fa90ce..c06da64c8 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -23,10 +23,13 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } // Create Project Wizard - if ($form_id == 'devshop_project_create_step_sites'){ - if ($form_state['project']->no_submit){ + if ($form_id == 'devshop_project_create_step_sites' || $form_id == 'devshop_project_create_step_settings'){ + if ($form_state['project']->no_finish){ unset($form['buttons']['return']); } + if ($form_state['project']->no_next){ + unset($form['buttons']['next']); + } } // On Hosting Task: Create Project form, do our own submission. From 9a9bcbe21cef19de8ec53fe8ede5a71b16e681b6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:46:04 -0500 Subject: [PATCH 0315/3476] projects should load platforms that are queued to be enabled --- devshop_projects/inc/create-wizard.inc | 6 +++++- devshop_projects/inc/nodes.inc | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 85e4ee2e0..a991078dc 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -611,7 +611,11 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); - } + } + // If platform verified successfully: + elseif ($task->task_status == HOSTING_TASK_PROCESSING) { + $row[] = t('Platform download & verification in process.'); + } // If platform has NOT verified successfully, mark process not complete. else { diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index f4cfaa41e..8d7036149 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -180,7 +180,7 @@ function devshop_projects_load($node) { // Only load if site or platform is enabled. // FYI HOSTING_SITE_ENABLED == HOSTING_PLATFORM_ENABLED $node = node_load($project_object->object_nid); - if ($node->{$project_object->object_type .'_status'} == HOSTING_SITE_ENABLED) { + if ($node->{$project_object->object_type .'_status'} == HOSTING_SITE_ENABLED || $node->{$project_object->object_type .'_status'} == HOSTING_SITE_QUEUED) { $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; } } From 3876e83d65b81f5e90bb8101803074b5758ae984 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:47:55 -0500 Subject: [PATCH 0316/3476] making sure completed=false flag is saved if task status is anything but successful --- devshop_projects/inc/create-wizard.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index a991078dc..ed5037cd0 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -610,10 +610,12 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { + $completed = FALSE; $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); } // If platform verified successfully: elseif ($task->task_status == HOSTING_TASK_PROCESSING) { + $completed = FALSE; $row[] = t('Platform download & verification in process.'); } From 45d7ac7300498adf1492c96e77411c3d6f23378d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 12:52:31 -0500 Subject: [PATCH 0317/3476] fixing some logic... handles the split second when there might be successful verify task nodes but the platforms information is still coming in. --- devshop_projects/inc/create-wizard.inc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index ed5037cd0..25f53d0d1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -563,6 +563,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } $profiles = array(); + $available_profiles = array(); $completed = TRUE; $form['nid'] = array( @@ -599,14 +600,19 @@ function devshop_project_create_step_sites(&$form, &$form_state) { if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); $row[] = implode(', ', $profiles[$name]); + } else { + $profiles[$name] = array(); } - if (!isset($available_profiles)){ + // If no available profiles found yet, this becomes the available list. + if (empty($available_profiles)){ $available_profiles = $profiles[$name]; - } else { + } + // If we have available profiles, only keep the ones we found again. + else { // @TODO: This joins profiles by name, not profile name. $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); - } + } } // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { From 591f1ebf9fa812554f77ee0f0210e9ce3dcd3435 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 13:04:12 -0500 Subject: [PATCH 0318/3476] check for task status before showing version --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 25f53d0d1..4cc968110 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -589,7 +589,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $row = array(); $row[] = $name; $row[] = $platform->git_branch; - $row[] = $platform->release->version; + $row[] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('Pending'); $row[] = _hosting_parse_error_code($task->task_status); // If platform verified successfully: From a71b31db4073024800e12113bc69c1d4493e88a2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 13:17:48 -0500 Subject: [PATCH 0319/3476] adding project name validation. --- devshop_projects/inc/create-wizard.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 4cc968110..cd7e45a6a 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -237,7 +237,12 @@ function devshop_project_create_step_git(&$form, &$form_state) { */ function devshop_project_create_step_git_validate(&$from, &$form_state) { $project = &$form_state['project']; - // @TODO: Check for duplicate project name here. + + // Check for duplicate project name here. + $node = hosting_context_load($form_state['values']['title']); + if ($node->nid != $project->project_nid){ + form_set_error('title', t('That name is in use. Please try again.')); + } } /** From c22844b930f85a45b2feef0c1652dfb7a28bf9d9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 13:19:49 -0500 Subject: [PATCH 0320/3476] validating project name with hostmaster _hosting_valid_fqdn() --- devshop_projects/inc/create-wizard.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index cd7e45a6a..87603b82f 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -238,6 +238,12 @@ function devshop_project_create_step_git(&$form, &$form_state) { function devshop_project_create_step_git_validate(&$from, &$form_state) { $project = &$form_state['project']; + // No spaces or special characters allowed. + $url = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive + if (!_hosting_valid_fqdn($url)) { + form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); + } + // Check for duplicate project name here. $node = hosting_context_load($form_state['values']['title']); if ($node->nid != $project->project_nid){ From 350faa60e20ec2535e75a34a2fe334f0e9ceea1e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 19 Dec 2012 13:22:32 -0500 Subject: [PATCH 0321/3476] making sure next button is hidden if you cannot continue --- devshop_projects/inc/create-wizard.inc | 2 +- devshop_projects/inc/forms.inc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 87603b82f..67e6e5668 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -382,7 +382,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // @TODO: Pretty this up, figure out how to maybe, display the task in the body? $form['note'] = array( '#type' => 'markup', - '#value' => t('Please wait while we connect to your repository and determine any branches. For now you must reload the page to continue.'), + '#value' => t('Please wait while we connect to your repository and determine any branches. NOTE: You must reload the page when the tasks complete to continue.'), ); $form['not_ready'] = array( '#type' => 'value', diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c06da64c8..641c1df40 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -23,7 +23,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } // Create Project Wizard - if ($form_id == 'devshop_project_create_step_sites' || $form_id == 'devshop_project_create_step_settings'){ + if ($form_id == 'devshop_project_create_step_sites' || $form_id == 'devshop_project_create_step_settings' || $form_id == 'devshop_project_create_step_environments'){ if ($form_state['project']->no_finish){ unset($form['buttons']['return']); } From 90be466d2993f4cd90636667ced0ae4bad557502 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Dec 2012 11:59:02 -0500 Subject: [PATCH 0322/3476] moving log url underneath hosting_logs tab. If hosting_logs isnt there, it falls back to a node/x/tab! --- devshop_log/devshop_log.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index ddac5be54..910dfcc9d 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -47,7 +47,7 @@ function devshop_log_menu() { 'access arguments' => array('administer hosting settings'), ); - $items['node/%/gitlog'] = array( + $items['node/%/logs/commits'] = array( 'title' => 'Commit Log', 'description' => 'View commit log entries', 'page callback' => 'devshop_log_page', From f1c7e4c054ee9901576195a363959703c50ba5bc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 7 Jan 2013 17:42:36 -0500 Subject: [PATCH 0323/3476] Extending git branch length --- devshop_projects/devshop_projects.install | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 76ceb6f5a..b2d2ebe07 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -78,7 +78,7 @@ function devshop_projects_schema() { ), 'git_branch' => array( 'type' => 'varchar', - 'length' => 16, + 'length' => 128, 'not null' => FALSE, 'description' => 'The current branch of this site or platform.', ), @@ -153,3 +153,12 @@ function devshop_projects_update_4() { db_change_field($ret, 'hosting_devshop_project_object', 'env_type', 'environment', $spec); return $ret; } + +/** + * Makes "git_branch" field larger. + */ +function devshop_projects_update_5() { + $ret = array(); + $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE git_branch git_branch VARCHAR(128) NULL"); + return $ret; +} \ No newline at end of file From c748f03f1b80c7ae8d2657b7c3d08d2bcf22fbe1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Jan 2013 13:15:19 +0000 Subject: [PATCH 0324/3476] using SERVER_NAME instead of "hostmaster" name --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 67e6e5668..401f18308 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -298,8 +298,8 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); // Setup project url. - $hostmaster_site = hosting_context_load('hostmaster'); - $server = variable_get('devshop_project_default_base_url', $hostmaster_site->title); + $base_url = $_SERVER['SERVER_NAME']; + $server = variable_get('devshop_project_default_base_url', $base_url); $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => $project_name, '@server' => $server)); } From 5cd8d04224d1518f878fb854c3870efde7926c8a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Jan 2013 13:20:55 +0000 Subject: [PATCH 0325/3476] moving commit log underneath logs tab --- devshop_log/devshop_log.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 20436398c..76e832525 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -47,7 +47,7 @@ function devshop_log_menu() { 'access arguments' => array('administer hosting settings'), ); - $items['node/%/gitlog'] = array( + $items['node/%/logs/commits'] = array( 'title' => 'Commit Log', 'description' => 'View commit log entries', 'page callback' => 'devshop_log_page', @@ -55,7 +55,7 @@ function devshop_log_menu() { 'type' => MENU_LOCAL_TASK, 'access callback' => '_devshop_log_access_callback', 'access arguments' => array(1), - 'weight' => 4, + 'weight' => -4, ); return ($items); From aa2fb8769d3288890ac80edc56b18ef6e8c552cd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Jan 2013 15:14:13 +0000 Subject: [PATCH 0326/3476] fixing data loss editing site or platform --- devshop_projects/inc/forms.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 641c1df40..2628700f0 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -59,6 +59,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Save values that need to be saved. if ($form_id == 'platform_node_form' || $form_id == 'site_node_form'){ + $node = $form['#node']; $form['project'] = array( '#type' => 'value', '#value' => $node->project, @@ -67,6 +68,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#type' => 'value', '#value' => $node->environment, ); + dsm($form); } } From 5e4adb9831203dc0b76f88b87edbc88f055505e7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Jan 2013 10:35:09 -0500 Subject: [PATCH 0327/3476] fixing project update and insert: save install profile --- devshop_projects/inc/nodes.inc | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 8d7036149..e6888571f 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -108,10 +108,14 @@ function devshop_projects_insert($node) { if (!isset($node->no_verify)) { hosting_add_task($node->nid, 'verify'); } + + $data = array(); + $data['git_branches'] = $node->git_branches; + $data['git_tags'] = $node->git_tags; - db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url) ". - "VALUES (%d, '%s', '%s', '%s')", - $node->nid, $node->git_url, hosting_path_normalize($node->code_path), $node->base_url); + db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url, install_profile, data) + VALUES (%d, '%s', '%s', '%s', '%s', '%s')", + $node->nid, $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, $node->install_profile, serialize($data)); // Save hosting context if ((!$node->old_vid)) { @@ -127,19 +131,20 @@ function devshop_projects_insert($node) { * */ function devshop_projects_update($node) { + + if (!$node->no_verify) { + hosting_add_task($node->nid, 'verify'); + } + $data = array(); $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; - db_query("UPDATE {hosting_devshop_project} " . - "SET git_url = '%s', code_path = '%s', base_url = '%s', data='%s' " . - "WHERE nid = %d", - $node->git_url, hosting_path_normalize($node->code_path), - $node->base_url, serialize($data), $node->nid); + db_query("UPDATE {hosting_devshop_project} + SET git_url = '%s', code_path = '%s', base_url = '%s', install_profile='%s', data='%s' + WHERE nid = %d", + $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, $node->install_profile, serialize($data), $node->nid); - if (!$node->no_verify) { - hosting_add_task($node->nid, 'verify'); - } } /** From 861df7e8225a9a60fee0743ffc0614c597cf00f5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Jan 2013 15:55:54 +0000 Subject: [PATCH 0328/3476] fixing commit log link, adding error log and files browser --- devshop_projects/inc/ui.inc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 0e7fef4ed..8b6e66277 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -161,7 +161,13 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = l('Platform Dashboard', "node/$site->platform"); if (module_exists('devshop_log')){ - $row[] = l('Commit Log', "node/$site->platform/gitlog"); + $row[] = l('Commit Log', "node/$site->nid/logs/commits"); + } + if (module_exists('hosting_logs')){ + $row[] = l('Error Log', "node/$site->nid/logs/errors"); + } + if (module_exists('hosting_filemanager')){ + $row[] = l('Files', "node/$site->nid/files/platform"); } $rows[] = $row; } From 6dcc0984ae6be8f4511990d0a03b9f7a49ae7c28 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Jan 2013 16:03:37 +0000 Subject: [PATCH 0329/3476] removing dsm --- devshop_projects/inc/forms.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 2628700f0..26de6e98a 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -68,7 +68,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#type' => 'value', '#value' => $node->environment, ); - dsm($form); } } From 9b597270e9cd9b3956a0dca9312c39200550f404 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Jan 2013 16:03:47 +0000 Subject: [PATCH 0330/3476] some css --- devshop_projects/inc/environments.css | 5 ++++- devshop_projects/inc/ui.inc | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/environments.css b/devshop_projects/inc/environments.css index 7e3d2a076..1cef96df6 100644 --- a/devshop_projects/inc/environments.css +++ b/devshop_projects/inc/environments.css @@ -1,4 +1,7 @@ #edit-default-platforms-dev-enabled-wrapper, #edit-default-platforms-dev-branch-wrapper { float: left; width: 50%; -} \ No newline at end of file +} +body.aegir #page div.node div.content fieldset.project-environments div.form-item { + padding: 5px; +} diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 8b6e66277..abdbc2cc9 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -178,6 +178,9 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#type' => 'fieldset', '#title' => t('Project Environments'), '#weight' => 12, + '#attributes' => array( + 'class' => 'project-environments', + ), ); $node->content['sites']['table'] = array( '#type' => 'item', @@ -198,8 +201,10 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'nid' => $node->nid, 'changed' => $node->changed, ); + drupal_add_js($settings, 'setting'); drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/environments.css'); // MAIN DISPLAY $node->content['devshop'] = array( From 7dbff3bab6c1f7a6670ed505ebe63bae8ed06ada Mon Sep 17 00:00:00 2001 From: kjl Date: Thu, 10 Jan 2013 15:24:08 -0500 Subject: [PATCH 0331/3476] add dependency on devshop_projects to devshop_pull --- devshop_pull/devshop_pull.info | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_pull/devshop_pull.info b/devshop_pull/devshop_pull.info index db0e4c677..a0b33e230 100644 --- a/devshop_pull/devshop_pull.info +++ b/devshop_pull/devshop_pull.info @@ -2,3 +2,4 @@ name = DevShop Pull description = Provides a variety of methods to trigger a Pull Code task on DevShop projects. core = 6.x package = DevShop +dependencies[] = devshop_projects From 1c18551e64e55aa3e8b53d71f5e00b1426e18a5c Mon Sep 17 00:00:00 2001 From: kjl Date: Fri, 11 Jan 2013 13:52:59 -0500 Subject: [PATCH 0332/3476] fix clearing on environments fieldset --- devshop_projects/inc/environments.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/inc/environments.css b/devshop_projects/inc/environments.css index 1cef96df6..687b32fa8 100644 --- a/devshop_projects/inc/environments.css +++ b/devshop_projects/inc/environments.css @@ -5,3 +5,7 @@ body.aegir #page div.node div.content fieldset.project-environments div.form-item { padding: 5px; } + +fieldset.project-environments { + clear: both; +} \ No newline at end of file From 9db3e9fa822007b7242db4c6cd66662c9867a063 Mon Sep 17 00:00:00 2001 From: kjl Date: Fri, 11 Jan 2013 14:02:15 -0500 Subject: [PATCH 0333/3476] Adding 'environments' text to the project wizard --- devshop_projects/inc/create-wizard.inc | 58 +++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 401f18308..b9f31849a 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -58,7 +58,7 @@ function devshop_projects_create_wizard($step = NULL){ // Saving the last visited step for redirects from node $_SESSION['last_step'] = $step; - + // Generate our ctools form and output $output = ctools_wizard_multistep_form($form_info, $step, $form_state); return $output; @@ -141,10 +141,10 @@ function devshop_projects_create_wizard_finish(&$form_state) { foreach ($node->project_objects['platform'] as $nid => $env){ devshop_projects_create_site($node, node_load($nid), $env); } - + ctools_object_cache_clear('project', $form_state['cache name']); $form_state['redirect'] = 'node/' . $node->nid; - + // Removing last step session variable. unset($_SESSION['last_step']); } @@ -167,10 +167,10 @@ function devshop_projects_create_wizard_cancel(&$form_state) { if (!empty($project->project_nid)){ hosting_add_task($project->project_nid, 'delete'); } - + // Removing last step session variable. unset($_SESSION['last_step']); - + } /** @@ -338,7 +338,7 @@ function devshop_project_create_step_settings(&$form, &$form_state) { */ function devshop_project_create_step_settings_validate(&$from, &$form_state) { $project = &$form_state['project']; - + // Code path and domain must be unique if (db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s";', $form_state['values']['code_path']))){ form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); @@ -346,7 +346,7 @@ function devshop_project_create_step_settings_validate(&$from, &$form_state) { if (db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE base_url = "%s";', $form_state['values']['base_url']))){ form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); } - + } /** @@ -388,25 +388,25 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#type' => 'value', '#value' => TRUE, ); - + // Add JS that reloads the page when tasks finish. - $path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; + $path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; drupal_add_js($path); - + $project->no_next = TRUE; return; } // Add JS that reloads the page when tasks finish. - $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; + $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; drupal_add_js($path); - + $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); $form['default_platforms'] = array( '#type' => 'fieldset', - '#title' => t('Default Platforms'), + '#title' => t('Default Platform Environments'), '#theme' => 'devshop_project_create_platforms_table', '#tree' => TRUE, ); @@ -429,7 +429,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $form['branch_platforms'] = array( '#type' => 'checkboxes', '#multiple' => 'true', - '#title' => t('Branch Platforms'), + '#title' => t('Branch Platform Environments'), '#description' => t('Check the branches you wish to create platforms for.'), '#default_value' => $project->branch_platforms, '#options' => $branch_options @@ -489,10 +489,10 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Get project and reset properties.. $project = &$form_state['project']; $values = $form_state['values']; - + // Get last platforms, in case this form is re-submitted $old_platforms = array_merge($project->default_platforms, $project->branch_platforms); - + // Get platforms from form values and save into our object $project->default_platforms = array_filter($values['default_platforms']); $project->branch_platforms = array_filter($values['branch_platforms']); @@ -527,18 +527,18 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { node_save($platform_node); } } - + // For all removed platforms, trigger a delete task $removed_platforms = array_diff_key($old_platforms, $all_platforms); foreach ($removed_platforms as $platform_name => $branch) { - + // @TODO: Determine what to do here based on task status... // if verify task hasn't even run yet (and has never run) we can just delete // the platform node. - + // Create 'delete' task for this project hosting_add_task($project->platforms[$platform_name], 'delete'); - + // Remove from project object unset($project->platforms[$platform_name]); } @@ -576,7 +576,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $profiles = array(); $available_profiles = array(); $completed = TRUE; - + $form['nid'] = array( '#type' => 'value', '#value' => $project_node->nid, @@ -586,23 +586,23 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $rows = array(); $header = array(t('Name'), t('Branch'), t('Version'), t('Verification Status'), t('Installation Profiles')); foreach ($project_node->project_objects['platform'] as $nid => $name){ - + // If this platform is no longer in the $project object, we shouldn't show // it here. if (!isset($project->platforms[$name])){ continue; } - + // Get verification task for this platform. $task = hosting_get_most_recent_task($nid, 'verify'); $platform = node_load($nid); - + $row = array(); $row[] = $name; $row[] = $platform->git_branch; $row[] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('Pending'); $row[] = _hosting_parse_error_code($task->task_status); - + // If platform verified successfully: if ($task->task_status == HOSTING_TASK_SUCCESS) { @@ -614,7 +614,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } else { $profiles[$name] = array(); } - + // If no available profiles found yet, this becomes the available list. if (empty($available_profiles)){ $available_profiles = $profiles[$name]; @@ -635,7 +635,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $completed = FALSE; $row[] = t('Platform download & verification in process.'); } - + // If platform has NOT verified successfully, mark process not complete. else { $completed = FALSE; @@ -645,7 +645,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Store rows for display $rows[] = $row; } // end foreach platform - + // Output our table. $form['platforms'] = array( '#type' => 'markup', @@ -700,7 +700,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { */ function devshop_project_create_step_sites_validate(&$from, &$form_state) { $project = &$form_state['project']; - + if (empty($form_state['values']['install_profile'])){ form_set_error('install_profile', t('You must choose an install profile')); } From 8c0d7cf56ffed68013c9696a85bfb00542e12fb7 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 22 Jan 2013 14:56:08 +0100 Subject: [PATCH 0334/3476] Jacinto. #1882620. Added a new field live --- devshop_projects/devshop_projects.install | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index b2d2ebe07..2aca36648 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -139,7 +139,7 @@ function devshop_projects_update_2() { */ function devshop_projects_update_3() { $ret = array(); - $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} ADD COLUMN git_branch VARCHAR(16) NULL"); + db_add_field($ret, 'hosting_devshop_project_object', 'git_branch', array('type' => 'varchar', 'length' => 16, 'not null' => FALSE)); return $ret; } @@ -161,4 +161,15 @@ function devshop_projects_update_5() { $ret = array(); $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE git_branch git_branch VARCHAR(128) NULL"); return $ret; -} \ No newline at end of file +} + +/** + * Adds live_domain colum to {hosting_devshop_project} + */ +function devshop_projects_update_6() { + $ret = array(); + + db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); + + return $ret; +} From d52f9c926bddcb75679f0a244125af6c6a352fc4 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 22 Jan 2013 15:01:16 +0100 Subject: [PATCH 0335/3476] Jacinto. #1882620 Created a new module devshop_live --- devshop_live/devshop_live.info | 7 +++++++ devshop_live/devshop_live.module | 2 ++ 2 files changed, 9 insertions(+) create mode 100644 devshop_live/devshop_live.info create mode 100644 devshop_live/devshop_live.module diff --git a/devshop_live/devshop_live.info b/devshop_live/devshop_live.info new file mode 100644 index 000000000..ef68e0e8c --- /dev/null +++ b/devshop_live/devshop_live.info @@ -0,0 +1,7 @@ +name = DevShop Live +description = A DevShop module to Going Live. +core = 6.x +package = DevShop + +dependencies[] = devshop_projects +dependencies[] = hosting_alias diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module new file mode 100644 index 000000000..a4abe2daf --- /dev/null +++ b/devshop_live/devshop_live.module @@ -0,0 +1,2 @@ + Date: Tue, 22 Jan 2013 21:47:21 +0000 Subject: [PATCH 0336/3476] adding perms --- devshop_projects/devshop_projects.module | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index ab2d8ccbc..9df148a27 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -26,6 +26,11 @@ function devshop_projects_perm() { 'create devshop-commit task', 'create devshop-pull task', 'create devshop-sync task', + 'create project', + 'view project', + 'edit project', + 'delete projects', + 'administer projects', ); } @@ -246,4 +251,4 @@ function _devshop_projects_project_has_module($node, $module) { $sites = array_flip($node->project_objects['site']); return _devshop_projects_site_has_module(node_load($sites['dev']), $module); } - \ No newline at end of file + From 36af2390a2c93011aaeff81988a3a12538ecaa4a Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Thu, 24 Jan 2013 15:04:42 +0100 Subject: [PATCH 0337/3476] Jacinto. Issue #1882620. Added live domain to projects --- devshop_live/devshop_live.module | 19 +++++++++ devshop_projects/devshop_projects.install | 12 +++++- devshop_projects/inc/create-wizard.inc | 14 ++++++ devshop_projects/inc/forms.inc | 12 ++++++ devshop_projects/inc/nodes.inc | 52 +++++++++++++++++++---- 5 files changed, 100 insertions(+), 9 deletions(-) diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index a4abe2daf..bd03a61d1 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -1,2 +1,21 @@ type == 'site') { + if (array_search($domain, $node->aliases) === FALSE) { + $node->aliases[] = $domain; + node_save($node); + return TRUE; + } + } + + return FALSE; +} diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 2aca36648..50c009cc5 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -43,6 +43,11 @@ function devshop_projects_schema() { 'size' => 'big', 'description' => 'A serialized array of name value pairs for this project.', ), + 'live_domain' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + ), ), 'primary key' => array('nid'), ); @@ -169,7 +174,12 @@ function devshop_projects_update_5() { function devshop_projects_update_6() { $ret = array(); - db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); + //Check if field was created + $schema = drupal_get_schema('hosting_devshop_project'); + + if (!isset($schema['fields']['live_domain'])) { + db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); + } return $ret; } diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index b9f31849a..96a4488a5 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -32,6 +32,7 @@ function devshop_projects_create_wizard($step = NULL){ $project->title = ''; $project->default_platforms = array(); $project->branch_platforms = array(); + $project->live_domain = ''; // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); @@ -133,6 +134,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->code_path = $project->code_path; $node->base_url = $project->base_url; $node->install_profile = $project->install_profile; + $node->live_domain = $project->live_domain; node_save($node); // Create the site nodes @@ -331,6 +333,17 @@ function devshop_project_create_step_settings(&$form, &$form_state) { '#default_value' => $project->base_url, '#maxlength' => 255, ); + + if(module_exists('devshop_live')) { + $form['live_domain'] = array( + '#type' => 'textfield', + '#title' => t('Live domain'), + '#description' => t('The live domain. If you still do not know you can input it later.'), + '#size' => 40, + '#default_value' => $project->live_domain, + '#maxlenght' => 255, + ); + } } /** @@ -360,6 +373,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { // verification. $project->code_path = $form_state['values']['code_path']; $project->base_url = $form_state['values']['base_url']; + $project->live_domain = $form_state['values']['live_domain']; } diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 26de6e98a..4b962b346 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -117,6 +117,18 @@ function devshop_projects_form(&$node) { '#weight' => 2, ); + if(module_exists('devshop_live')) { + $form['live_domain'] = array( + '#type' => 'textfield', + '#title' => t('Live domain'), + '#description' => t('The live domain. If you still do not know you can input it later.'), + '#size' => 40, + '#default_value' => $node->live_domain, + '#maxlenght' => 255, + '#weight' => 3, + ); + } + // Don't allow editing if ($node->nid) { $locked = array('title', 'git_url', 'code_path', 'base_url'); diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index e6888571f..6312487ac 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -113,14 +113,33 @@ function devshop_projects_insert($node) { $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; - db_query("INSERT INTO {hosting_devshop_project} (nid, git_url, code_path, base_url, install_profile, data) - VALUES (%d, '%s', '%s', '%s', '%s', '%s')", - $node->nid, $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, $node->install_profile, serialize($data)); + $info = new stdClass(); + $info->nid = $node->nid; + $info->git_url = $node->git_url; + $info->code_path = hosting_path_normalize($node->code_path); + $info->base_url = $node->base_url; + $info->install_profile = $node->install_profile; + $info->live_domain = $node->live_domain; + $info->data = serialize($data); + + drupal_write_record('hosting_devshop_project', $info); // Save hosting context if ((!$node->old_vid)) { hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); } + + //Get all data of node + $node = node_load($node->nid); + + if (module_exists('devshop_live') && $info->live_domain) { + $live_nid = array_search('live', $node->project_objects['site']); + + if ($live_nid) { + $live_node = node_load($live_nid); + devshop_live_add_domain($live_node, $info->live_domain); + } + } } /** @@ -140,11 +159,28 @@ function devshop_projects_update($node) { $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; - db_query("UPDATE {hosting_devshop_project} - SET git_url = '%s', code_path = '%s', base_url = '%s', install_profile='%s', data='%s' - WHERE nid = %d", - $node->git_url, hosting_path_normalize($node->code_path), $node->base_url, $node->install_profile, serialize($data), $node->nid); + $info = new stdClass(); + $info->nid = $node->nid; + $info->git_url = $node->git_url; + $info->code_path = hosting_path_normalize($node->code_path); + $info->base_url = $node->base_url; + $info->install_profile = $node->install_profile; + $info->live_domain = $node->live_domain; + $info->data = serialize($data); + drupal_write_record('hosting_devshop_project', $info, 'nid'); + + //get more information node + $node = node_load($node->nid); + + if (module_exists('devshop_live') && $info->live_domain) { + $live_nid = array_search('live', $node->project_objects['site']); + + if ($live_nid) { + $live_node = node_load($live_nid); + devshop_live_add_domain($live_node, $info->live_domain); + } + } } /** @@ -165,7 +201,7 @@ function devshop_projects_delete($node) { * Node object */ function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile, data ' . + $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile, live_domain, data ' . 'FROM {hosting_devshop_project} ' . 'WHERE nid = %d', $node->nid)); $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); From c8be7cefaebc05bd2ab606ddd7b4c6ed5e0444e8 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Thu, 24 Jan 2013 16:15:26 +0100 Subject: [PATCH 0338/3476] Jacinto. Issue #1882620. Move code to devshop live --- devshop_live/devshop_live.install | 44 +++++++++++++++++++++ devshop_live/devshop_live.module | 10 ++++- devshop_live/inc/forms.inc | 48 +++++++++++++++++++++++ devshop_live/inc/nodes.inc | 27 +++++++++++++ devshop_projects/devshop_projects.install | 21 ---------- devshop_projects/inc/create-wizard.inc | 14 +------ devshop_projects/inc/forms.inc | 12 ------ devshop_projects/inc/nodes.inc | 26 +----------- 8 files changed, 130 insertions(+), 72 deletions(-) create mode 100644 devshop_live/devshop_live.install create mode 100644 devshop_live/inc/forms.inc create mode 100644 devshop_live/inc/nodes.inc diff --git a/devshop_live/devshop_live.install b/devshop_live/devshop_live.install new file mode 100644 index 000000000..aaf8a0f04 --- /dev/null +++ b/devshop_live/devshop_live.install @@ -0,0 +1,44 @@ + 'varchar', + 'length' => 128, + 'not null' => TRUE, + ); +} + +/** + * Implementation of hook_enable(). + */ +function devshop_live_enable() { + $ret = array(); + + //Check if field was created + $schema = drupal_get_schema('hosting_devshop_project'); + + if (!isset($schema['fields']['live_domain'])) { + db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); + } + + return $ret; +} + +/** + * Implementation of hook_disable(). + */ +function devshop_live_disable() { + $ret = array(); + + db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); + + return $ret; +} diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index bd03a61d1..8e538bb08 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -1,9 +1,17 @@ 'textfield', + '#title' => t('Live domain'), + '#description' => t('The live domain. If you still do not know you can input it later.'), + '#size' => 40, + '#default_value' => $node->live_domain, + '#maxlenght' => 255, + '#weight' => 3, + ); + } + + if ($form_id == 'devshop_project_create_step_settings') { + $project = $form_state['project']; + + $form['live_domain'] = array( + '#type' => 'textfield', + '#title' => t('Live domain'), + '#description' => t('The live domain. If you still do not know you can input it later.'), + '#size' => 40, + '#default_value' => $project->live_domain, + '#maxlenght' => 255, + ); + + $form['#submit'][] = 'devshop_live_create_form_submit'; + } +} + +function devshop_live_create_form_submit($form, &$form_state) { + $project = &$form_state['project']; + + $project->live_domain = $form_state['values']['live_domain']; +} diff --git a/devshop_live/inc/nodes.inc b/devshop_live/inc/nodes.inc new file mode 100644 index 000000000..418f0f2bb --- /dev/null +++ b/devshop_live/inc/nodes.inc @@ -0,0 +1,27 @@ +type == 'project' && in_array($op, array('insert', 'update'))) { + //Load all information + $node_p = node_load($node->nid); + + if ($node->live_domain && !isset($node_p->project_objects['site'])) { + $live_nid = array_search('live', $node_p->project_objects['site']); + + if ($live_nid) { + $live_node = node_load($live_nid); + devshop_live_add_domain($live_node, $node->live_domain); + } + } + } +} diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 50c009cc5..fa62c9db3 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -43,11 +43,6 @@ function devshop_projects_schema() { 'size' => 'big', 'description' => 'A serialized array of name value pairs for this project.', ), - 'live_domain' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - ), ), 'primary key' => array('nid'), ); @@ -167,19 +162,3 @@ function devshop_projects_update_5() { $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE git_branch git_branch VARCHAR(128) NULL"); return $ret; } - -/** - * Adds live_domain colum to {hosting_devshop_project} - */ -function devshop_projects_update_6() { - $ret = array(); - - //Check if field was created - $schema = drupal_get_schema('hosting_devshop_project'); - - if (!isset($schema['fields']['live_domain'])) { - db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); - } - - return $ret; -} diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 96a4488a5..9e04c321a 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -315,6 +315,7 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { */ function devshop_project_create_step_settings(&$form, &$form_state) { $project = &$form_state['project']; + $form['code_path'] = array( '#type' => 'textfield', '#title' => t('Code path'), @@ -333,17 +334,6 @@ function devshop_project_create_step_settings(&$form, &$form_state) { '#default_value' => $project->base_url, '#maxlength' => 255, ); - - if(module_exists('devshop_live')) { - $form['live_domain'] = array( - '#type' => 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain. If you still do not know you can input it later.'), - '#size' => 40, - '#default_value' => $project->live_domain, - '#maxlenght' => 255, - ); - } } /** @@ -366,14 +356,12 @@ function devshop_project_create_step_settings_validate(&$from, &$form_state) { * STEP 2: Submit */ function devshop_project_create_step_settings_submit(&$from, &$form_state) { - global $user; $project = &$form_state['project']; // For this step, we just save code path and base url for later saving and // verification. $project->code_path = $form_state['values']['code_path']; $project->base_url = $form_state['values']['base_url']; - $project->live_domain = $form_state['values']['live_domain']; } diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 4b962b346..26de6e98a 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -117,18 +117,6 @@ function devshop_projects_form(&$node) { '#weight' => 2, ); - if(module_exists('devshop_live')) { - $form['live_domain'] = array( - '#type' => 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain. If you still do not know you can input it later.'), - '#size' => 40, - '#default_value' => $node->live_domain, - '#maxlenght' => 255, - '#weight' => 3, - ); - } - // Don't allow editing if ($node->nid) { $locked = array('title', 'git_url', 'code_path', 'base_url'); diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 6312487ac..96e80c13d 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -128,18 +128,6 @@ function devshop_projects_insert($node) { if ((!$node->old_vid)) { hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); } - - //Get all data of node - $node = node_load($node->nid); - - if (module_exists('devshop_live') && $info->live_domain) { - $live_nid = array_search('live', $node->project_objects['site']); - - if ($live_nid) { - $live_node = node_load($live_nid); - devshop_live_add_domain($live_node, $info->live_domain); - } - } } /** @@ -169,18 +157,6 @@ function devshop_projects_update($node) { $info->data = serialize($data); drupal_write_record('hosting_devshop_project', $info, 'nid'); - - //get more information node - $node = node_load($node->nid); - - if (module_exists('devshop_live') && $info->live_domain) { - $live_nid = array_search('live', $node->project_objects['site']); - - if ($live_nid) { - $live_node = node_load($live_nid); - devshop_live_add_domain($live_node, $info->live_domain); - } - } } /** @@ -201,7 +177,7 @@ function devshop_projects_delete($node) { * Node object */ function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT git_url, code_path, base_url, install_profile, live_domain, data ' . + $additions = db_fetch_array(db_query('SELECT * ' . 'FROM {hosting_devshop_project} ' . 'WHERE nid = %d', $node->nid)); $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); From 211ac85944cd7fb35733d9a80a00a86d95eadf4c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 24 Jan 2013 18:24:31 -0500 Subject: [PATCH 0339/3476] no need to drush_shell_cd_and_exec() if $root isn't a directory --- devshop_projects/devshop_projects.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 619a04bed..64ad7e52c 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -88,7 +88,7 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $git_branch = $platform->git_branch; // Check if a repo exists - if (!is_dir($root) && !drush_shell_cd_and_exec($root, 'git status')){ + if (!is_dir($root) || !drush_shell_cd_and_exec($root, 'git status')){ drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); // Build the command string From f962fb3efe0f3155e5ad833d16f193fb2e813a4e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 09:34:28 -0500 Subject: [PATCH 0340/3476] changing wording to be "environment" --- devshop_projects/inc/tasks.inc | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 9dedb8ce4..045280a47 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -16,7 +16,7 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', ); $tasks['project']['devshop-create'] = array( - 'title' => t('Create New Site'), + 'title' => t('Create New Environment'), 'description' => t('Creates a new site & platform within this project.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, @@ -86,18 +86,11 @@ function hosting_task_devshop_create_form($node) { ); // @TODO: Add "create branch" functionality. $form['platform_name'] = array( - '#title' => t('Platform Name'), + '#title' => t('Environment Name'), '#type' => 'textfield', - '#description' => t('Enter the system name of your platform. For consistency, you should make this match the branch name.'), + '#description' => t('Enter a system name for your environment. For consistency, you should make this match the branch name.'), '#required' => TRUE, ); - /* @TODO: Let devshop_pull handle this. - $form['pull'] = array( - '#title' => t('Enable Pull on Commit'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - */ return $form; } From 8ed5a01d1e3dbe1dabeebdfe9ebfcd9b9ad86f68 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 09:58:27 -0500 Subject: [PATCH 0341/3476] adding validation to environment creation form. --- devshop_projects/inc/tasks.inc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 045280a47..04fc55971 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -91,9 +91,32 @@ function hosting_task_devshop_create_form($node) { '#description' => t('Enter a system name for your environment. For consistency, you should make this match the branch name.'), '#required' => TRUE, ); + dsm($node); + $form['project_nid'] = array( + '#type' => 'value', + '#value' => $node->nid, + ); return $form; } +/** + * Validation hook for hosting_task_devshop_create_form() + */ +function hosting_task_devshop_create_form_validate($form, &$form_state){ + $params = $form_state['values']['parameters']; + + // Check for existing environments for this project. + $object_nid = db_result(db_queryd('SELECT object_nid FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type = "%s" AND environment = "%s"', $params['project_nid'], 'platform', $params['platform_name'])); + if ($object_nid) { + form_set_error('platform_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['platform_name']))); + } + + // Check for illegal chars + if (!preg_match('!^[a-z0-9_]+$!', $params['platform_name'])) { + form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); + } +} + /** * Extra submit function for hosting_task_confirm_form() * From 3a7173f847c6f3b1d79e157574b6a29e9705b6c0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 10:01:27 -0500 Subject: [PATCH 0342/3476] adding a comment --- devshop_projects/devshop_projects.module | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index ab2d8ccbc..d61a56430 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -61,6 +61,11 @@ function devshop_projects_menu() { return ($items); } +/** + * Replacement for hosting_task_confirm_form() + * + * @TODO: Remove once http://drupal.org/node/1861898 is committed. + */ function devshop_projects_hosting_task_confirm_form_page($nid, $task){ $node = node_load($nid); return drupal_get_form('hosting_task_confirm_form', $node, $task); From 4b86a6398bd234678bd1fc630ff726df4a57ff25 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 10:10:21 -0500 Subject: [PATCH 0343/3476] getting rid of automatic enabling of pull on dev platforms --- devshop_pull/devshop_pull.install | 7 ------- 1 file changed, 7 deletions(-) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index 40954c00c..b469c6d98 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -63,13 +63,6 @@ function devshop_pull_schema() { function devshop_pull_install() { // Create tables. drupal_install_schema('devshop_pull'); - - //Enable pull for dev platform - $result = db_query("SELECT DISTINCT object_nid FROM {hosting_devshop_project_object} WHERE environment='dev' AND object_type='platform'"); - - while ($item = db_fetch_object($result)) { - db_query("INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, pull_enabled, pull_reset) VALUES(%d, 1, 0)", $result->object_nid); - } } /** From e9a859f64be14ef9afd36881066665de88854b78 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 10:14:30 -0500 Subject: [PATCH 0344/3476] lost DSM --- devshop_projects/inc/tasks.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 04fc55971..7a694c393 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -91,7 +91,6 @@ function hosting_task_devshop_create_form($node) { '#description' => t('Enter a system name for your environment. For consistency, you should make this match the branch name.'), '#required' => TRUE, ); - dsm($node); $form['project_nid'] = array( '#type' => 'value', '#value' => $node->nid, From c32c3afc6ec140ede7addf7a72cc40689ee7c7a2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 10:14:50 -0500 Subject: [PATCH 0345/3476] changing the node tabs to read "dashboard" and "Settings" --- devshop_projects/devshop_projects.module | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index d61a56430..b49319f4f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -81,8 +81,30 @@ function devshop_projects_menu_alter(&$items) { $items['node/add/project']['page arguments'] = array(3); $items['node/add/project']['file'] = 'create-wizard.inc'; $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects') . '/inc'; + + // Make project node pages more user-friendly. + $items['node/%node/view']['title callback'] = 'devshop_hosting_project_tab_title'; + $items['node/%node/view']['title arguments'] = array('View', 1); + + $items['node/%node/edit']['title callback'] = 'devshop_hosting_project_tab_title'; + $items['node/%node/edit']['title arguments'] = array('Edit', 1); + } +/** + * Tab title replacer + */ +function devshop_hosting_project_tab_title($default, $node){ + if ($default == 'View' && $node->type == 'project'){ + return t('Dashboard'); + } + if ($default == 'Edit' && $node->type == 'project'){ + return t('Settings'); + } + + // Otherwise, just return the page text + return t($default); +} /** * Task access controls From 202af4ecbe2ca1e4539b1135a0f04acd7c01d340 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 10:16:25 -0500 Subject: [PATCH 0346/3476] lost dbquery d --- devshop_projects/inc/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 7a694c393..87e26a301 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -105,7 +105,7 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ $params = $form_state['values']['parameters']; // Check for existing environments for this project. - $object_nid = db_result(db_queryd('SELECT object_nid FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type = "%s" AND environment = "%s"', $params['project_nid'], 'platform', $params['platform_name'])); + $object_nid = db_result(db_query('SELECT object_nid FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type = "%s" AND environment = "%s"', $params['project_nid'], 'platform', $params['platform_name'])); if ($object_nid) { form_set_error('platform_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['platform_name']))); } From deb465c428748107beaadefaa5696b06eeee87ab Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 10:18:43 -0500 Subject: [PATCH 0347/3476] adding some comments --- devshop_live/devshop_live.module | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index 8e538bb08..cb913b5d8 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -4,6 +4,11 @@ * @file devshop_live.module * * Provides Node Type, UI, and tools for DevShop Live. + * + * + * @TODO: There is a situation where the project might not have a + * "live" environment yet... I am not yet sure how to handle this, + * but we should at the very least notify the user. */ include_once('inc/forms.inc'); From 53d67b9afe4068cbbf2911dfedbf73ba1e2d5bb8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 25 Jan 2013 11:06:19 -0500 Subject: [PATCH 0348/3476] only show Commit Features if features.module is enabled. --- devshop_projects/devshop_projects.module | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index b49319f4f..e9b12e13e 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -126,7 +126,13 @@ function devshop_hosting_task_menu_access($node, $task) { } if (user_access("create " . $task . " task")) { if ($node->type == 'project') { - return TRUE; + + // If Commit Features task, make sure features module is present + if ($task == 'devshop-commit'){ + return _devshop_projects_project_has_module($node, 'features'); + } else { + return TRUE; + } } } } From b170ae064e70235428f64451efb6bba0ac102bd5 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Sat, 26 Jan 2013 00:39:35 +0100 Subject: [PATCH 0349/3476] Jacinto. Delete task for projects --- devshop_projects/devshop_projects.drush.inc | 2 +- devshop_projects/inc/forms.inc | 139 +++----------------- devshop_projects/inc/tasks.inc | 86 +++++++++++- 3 files changed, 97 insertions(+), 130 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 619a04bed..dd9b8d839 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -69,7 +69,7 @@ function drush_devshop_projects_pre_hosting_task() { } } $task->args['environment'] = $task->task_args['environment']; - $task->options['tests-to-run'] = implode($tests, ","); + $task->options['tests-to-run'] = implode(',', $tests); $task->options['sync-from-live'] = $task->task_args['sync']; } } diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 26de6e98a..5977ead2e 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -11,6 +11,10 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Removing unneccesary fieldgroups if ($form_id == 'project_node_form'){ + global $user; + + $project = $form['#node']; + unset($form['menu']); unset($form['revision_information']); unset($form['author']); @@ -20,6 +24,13 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ if ($form['retry']['#value']) { $form['buttons']['submit']['#value'] = t('Save and Retry'); } + + //Add button for delete project + $form['buttons']['delete'] = array( + '#type' => 'markup', + '#value' => l(t('Delete this project'), 'node/'. $project->nid .'/project_delete', array('query' => array('token' => drupal_get_token($user->uid)))), + '#weight' => 10, + ); } // Create Project Wizard @@ -37,6 +48,12 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ $form['#submit'] = array('hosting_task_devshop_create_form_submit'); } + // On Hosting Task: Delete Project form, do our own submission. + if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'delete' && $form['#node']->type == 'project'){ + $form['#submit'] = array('hosting_task_devshop_delete_form_submit'); + } + + // If not a part of a project, bail out. if (empty($form['#node']->nid)){ return; @@ -242,8 +259,6 @@ function devshop_projects_platform_create_form_validate(&$form, &$form_state){ if (empty($form_state['values']['platform_name'])){ form_set_error('install_profile', t('You must include a platform name')); } - - } /** @@ -269,123 +284,3 @@ function devshop_projects_platform_create_form_submit(&$form, &$form_state){ $form_state['redirect'] = "node/{$project_node->nid}"; drupal_goto("node/{$project_node->nid}"); } - -/** - * Form for project delete - */ -function devshop_projects_project_delete_form($form_state, $project_nid) { - $project_node = node_load($project_nid); - - $form = devshop_projects_view($project_node)->content; - - unset($form['sites']['add_platform']); - unset($form['tasks_view']); - unset($form['devshop']); - unset($form['info']['#prefix']); - unset($form['info']['#suffix']); - - $form['remove_dir'] = array( - '#type' => 'checkbox', - '#title' => t('Remove all project files and directories.'), - '#description' => t("Note that the platform & site directories are removed regardless of this option. By choosing this option, everything else in the directory '{$project_node->code_path}', including the directory itself, will be removed."), - '#default_value' => FALSE, - '#weight' => 30, - ); - - $form['message'] = array( - '#type' => 'item', - '#value' => t("Are you sure you want to delete project '{$project_node->title}' and all of its associated sites and platforms?"), - '#weight' => 31, - ); - - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_nid, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Delete Project'), - '#weight' => 32, - ); - - $form['cancel'] = array( - '#type' => 'submit', - '#value' => t('Cancel'), - '#weight' => 33, - ); - - return $form; -} - -/** - * Form for project delete submit handler - */ -function devshop_projects_project_delete_form_submit(&$form, &$form_state){ - - // Make sure the user really wants to delete the project - if ($form_state['clicked_button']['#value'] == t('Delete Project')) { - $nid = $form['nid']['#value']; - $data = devshop_projects_project_data_get($nid); - - // First get a list of all of the objects for this project - $query = db_query("SELECT * " . - "FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d", $nid); - - $snid = array(); - $pnid = array(); - - while ($obj = db_fetch_object($query)) { - if ($obj->object_type == 'site') { - $snid[] = $obj->object_nid; - } - else if ($obj->object_type == 'platform') { - $pnid[] = $obj->object_nid; - } - } - - if (count($snid) > 0) { - $data['project_delete_site_disable'] = $snid; - $data['project_delete_site_delete'] = $snid; - } - else { - unset($data['project_delete_site_disable']); - unset($data['project_delete_site_delete']); - } - - if (count($pnid) > 0) { - $data['project_delete_platform_delete'] = $pnid; - } - else { - unset($data['project_delete_platform_delete']); - } - - if ($data['project_delete_platform_delete'] || - $data['project_delete_site_disable'] || - $data['project_delete_site_delete'] ) { - $data['deleting_project'] = TRUE; - if($form_state['values']['remove_dir']) { - $data['deleting_project_remove_dir'] = TRUE; - } - } - else { - unset($data['deleting_project']); - } - - // Save the delete task and nid lists. We're going to need them - // when the hosting post task hook gets invoked - devshop_projects_project_data_set($nid, $data); - - // Kick the whole thing off by deleting the project first - hosting_add_task($nid, 'delete'); - } - - // Go back to the main project list page - $form_state['redirect'] = 'hosting/projects'; - drupal_goto('hosting/projects'); -} - - - - diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 9dedb8ce4..a723b9600 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -39,21 +39,18 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); - - - /* - @TODO: Refactor custom forms to work with hosting_confirm_form() + $tasks['project']['delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, + 'hidden' => TRUE, ); - */ + return $tasks; } - /** * Helper to provide a select list of environments for this project */ @@ -289,4 +286,79 @@ function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ if ($form_state['values']['parameters']['source'] == $form_state['values']['parameters']['destination']){ form_set_error('source', t('The source cannot be the same as the destination.')); } -} \ No newline at end of file +} + +/** + * Implementation of hook_hosting_task_TASK_TYPE_form(). + * + * For "Delete" task. + */ +function hosting_task_delete_form($node) { + $form = array(); + + if ($node->type == 'project') { + $form['remove_dir'] = array( + '#type' => 'checkbox', + '#title' => t('Remove all project files and directories.'), + '#description' => t("Note that the platform & site directories are removed regardless of this option. By choosing this option, everything else in the directory '{$node->code_path}', including the directory itself, will be removed."), + '#default_value' => FALSE, + '#weight' => 30, + ); + + $form['nid'] = array( + '#type' => 'value', + '#value' => $node->nid, + ); + } + + return $form; +} + +/** + * Extra submit function for hosting_task_confirm_form() + * + * @see devshop_projects_form_alter(). We had to add the submit hadler there. + */ +function hosting_task_delete_form_submit($form, &$form_state) { + $project = node_load($form_state['values']['nid']); + + $snid = array(); + $pnid = array(); + + $sites = $project->project_objects['site']; + $platforms = $project->project_objects['platform']; + + //First add task to delete sites + if (!empty($sites)) { + foreach ($sites as $snid => $alias) { + hosting_add_task($snid, 'delete'); + } + } + + $data_delete = array(); + + //Second add task to delete platforms + if (!empty($platforms)) { + foreach ($platforms as $pnid => $alias) { + //Is need delete code? + if ($form_state['values']['remove_dir']) { + $pnode = node_load($pnid); + $data_delete[] = $node->publish_path; + } + hosting_add_task($pnid, 'delete'); + } + } + + if (!empty($data_delete)) { + //Add task delete to delete directories + hosting_add_task($project->nid, 'delete', $data_delete); + } + + //The last step delete project node + node_delete($project->nid); + + // Go back to the main project list page + $form_state['redirect'] = 'hosting/projects'; + drupal_goto('hosting/projects'); +} + From 38d6b3137e976e5a20a996602686d1449fb47b7e Mon Sep 17 00:00:00 2001 From: frazras Date: Tue, 22 Jan 2013 22:47:44 -0500 Subject: [PATCH 0350/3476] made git clone recursive --- devshop_projects/devshop_projects.drush.inc | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 64ad7e52c..2df17bc83 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -25,7 +25,7 @@ function drush_devshop_projects_pre_hosting_task() { if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->git_url)) { drush_devshop_provision_pre_hosting_task_platform_verify(); } - + // Pull if ($task->ref->type == 'project' && $task->task_type == 'devshop-pull') { $task->args['environments'] = $task->task_args['environments']; @@ -92,7 +92,7 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); // Build the command string - $command = "git clone $git_remote $root"; + $command = "git clone --recursive $git_remote $root"; if ($git_branch) { $command .= " --branch $git_branch"; } @@ -100,7 +100,7 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ // If the platform has been verified and has a branch and git url else { drush_log(dt("[DEVSHOP] Existing Repo found at !root. Checking out branch !branch", array('!branch' => $platform->git_branch, '!root' => $root))); - + $root = $platform->publish_path; $git_remote = $platform->git_url; $git_branch = $platform->git_branch; @@ -133,7 +133,7 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ /** * Implementation of hook_post_hosting_TASK_TYPE_task * - * + * */ function devshop_projects_post_hosting_verify_task($task, $data) { @@ -142,22 +142,22 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // Get objects $nid = $task->ref->nid; $platform = node_load($nid); - + // If this platform isn't in a project, bail. if (empty($platform->project_nid)){ drush_log('[DEVSHOP] No project found for this platform.', 'notice'); return; } - + // Get the project $project = node_load($platform->project_nid); - + // If the project doesn't have an install profile chosen yet, bail. if (empty($project->install_profile)){ drush_log('[DEVSHOP] No install profile found for this platform\'s project.', 'notice'); return; } - + // If the project has a site already, bail. $sites = array_flip($project->project_objects['site']); $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); @@ -165,13 +165,13 @@ function devshop_projects_post_hosting_verify_task($task, $data) { drush_log('[DEVSHOP] Platform already has a site.', 'notice'); return; } - + // live. Let's create a site based off of this platform. drush_log('[DEVSHOP] Platform verified. Creating your site.'); - - + + $site_node = devshop_projects_create_site($project, $platform, $platform->environment); - + drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); } } From 47b2df76f2429f9f000aa224369312643220c22f Mon Sep 17 00:00:00 2001 From: frazras Date: Mon, 28 Jan 2013 16:59:40 -0500 Subject: [PATCH 0351/3476] added 'disabled' condition/warning for feature reverting if features module doesnt exist --- devshop_projects/inc/tasks.inc | 36 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 04fc55971..82d42f20e 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -1,3 +1,4 @@ + t('list of environments'), '#type' => 'textfield', '#default_value' => 'dev test', ); - + */ - + $form['update'] = array( '#title' => t('Run update.php after code pull?'), '#type' => 'checkbox', '#default_value' => 1, ); - + if (_devshop_projects_project_has_module($node, 'features')){ $form['revert'] = array( '#title' => t('Revert all features after code pull?'), @@ -229,7 +230,7 @@ function hosting_task_devshop_pull_form($node) { '#type' => 'checkbox', '#default_value' => 1, ); - + // Add validator for environments //$form['#validate'] = array('hosting_task_devshop_pull_form'); return $form; @@ -258,13 +259,20 @@ function hosting_task_devshop_sync_form($node) { $form = array(); devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment.'), 'destination', 'Destination'); - + + + $form['databases'] = array( + '#title' => t('Sync database from destination to source.'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + $form['files'] = array( '#title' => t('Sync files folder from destination to source.'), '#type' => 'checkbox', '#default_value' => 0, ); - + $form['note'] = array( '#value' => '

'. t('This will DESTROY the database for Destination and replace it with the database for the selected Source.', array('!site' => l($node->title, "node/$nid"))) . '

', '#type' => 'markup', @@ -287,6 +295,14 @@ function hosting_task_devshop_sync_form($node) { '#default_value' => $has_features, '#access' => $has_features, ); + }else{ + $form['revert'] = array( + '#title' => t('Revert all features on Destination after content sync?'), + '#type' => 'checkbox', + '#description' => t('This site doesn\'t have features.module enabled. Please enable and then "Verify" the site.'), + '#default_value' => FALSE, + '#disabled' => TRUE, + ); } $form['cache'] = array( '#title' => t('Clear cache on Destination after content sync?'), @@ -305,4 +321,4 @@ function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ if ($form_state['values']['parameters']['source'] == $form_state['values']['parameters']['destination']){ form_set_error('source', t('The source cannot be the same as the destination.')); } -} \ No newline at end of file +} From 682f8cdd16e8f1286ff8464e6189f4d9e8470bb5 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 29 Jan 2013 16:36:32 +0100 Subject: [PATCH 0352/3476] Jacinto. #1897852. Delete project --- devshop_projects/devshop_projects.drush.inc | 44 ++++ devshop_projects/inc/delete.inc | 263 -------------------- devshop_projects/inc/forms.inc | 28 ++- devshop_projects/inc/tasks.inc | 67 +++-- 4 files changed, 89 insertions(+), 313 deletions(-) delete mode 100644 devshop_projects/inc/delete.inc diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index dd9b8d839..cd568a654 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -34,6 +34,13 @@ function drush_devshop_projects_pre_hosting_task() { $task->options['cache'] = $task->task_args['cache']; } + //delete + if ($task->ref->type == 'project' && $task->task_type == 'devshop-delete') { + $task->args['sites'] = $task->task_args['sites']; + $task->args['platforms'] = $task->task_args['platforms']; + $task->args['data'] = $task->task_args['data']; + } + // Commit if ($task->ref->type == 'project' && $task->task_type == 'devshop-commit') { $task->args['environment'] = $task->task_args['environment']; @@ -130,6 +137,43 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ } } +/** + * Implementation of hook_post_hosting_TASK_TYPE_task + */ +function devshop_projects_post_hosting_devshop_delete_task($task, $data) { + if ($task->ref->type == 'project' && $task->task_type == 'devshop-delete') { + $sites = explode(' ', $task->task_args['snids']); + $platforms = explode(' ', $task->task_args['pnids']); + + //First sites + if (!empty($sites)) { + foreach ($sites as $key => $nid) { + hosting_context_delete($nid); + + $ref = node_load($nid); + $ref->no_verify = TRUE; + $ref->site_status = HOSTING_SITE_DELETED; + node_save($ref); + } + } + + //Second platforms + if (!empty($platforms)) { + foreach ($platforms as $key => $nid) { + $ref = node_load($nid); + $ref->platform_status = HOSTING_PLATFORM_DELETED; + $ref->no_verify = TRUE; + node_save($ref); + hosting_context_delete($ref->nid); + db_query("DELETE FROM {hosting_platform_client_access} WHERE pid = %d", $ref->nid); + } + } + + //The last step delete project node + node_delete($task->ref->nid); + } +} + /** * Implementation of hook_post_hosting_TASK_TYPE_task * diff --git a/devshop_projects/inc/delete.inc b/devshop_projects/inc/delete.inc deleted file mode 100644 index 4addc1502..000000000 --- a/devshop_projects/inc/delete.inc +++ /dev/null @@ -1,263 +0,0 @@ -nid); - - // Keys to tasks to be performed. Objects must be processed in this - // order due to task dependencies. - $keys = array(array('type' => 'site', - 'task' => 'disable'), - array('type' => 'site', - 'task' => 'delete'), - array('type' => 'platform', - 'task' => 'delete')); - - // Iterate through arrays of tasks => nids and queue one tasks that - // is left to do - - foreach ($keys as $oper) { - $key1 = "project_delete_{$oper['type']}_{$oper['task']}"; - - if (isset($data[$key1])) { - if (count($data[$key1]) > 0) { - // Use the first nid in the list and stop looking for any more work - $nid = $data[$key1][0]; - break; - } - else { - // Array is empty, delete it - unset($data[$key1]); - } - $updated = TRUE; - } - - } - - // If $nid != FALSE, then we need to act on that node. If it is FALSE, - // then we are all done. We need to set the project node status - // field to 0 and delete that row from the hosting_devshop_project - // table - - if ($nid) { - hosting_add_task($nid, $oper['task']); - } - - // If we changed the project data array in any way, we need to save it - if ($updated) { - devshop_projects_project_data_set($pnode->nid, $data); - } - - if (!$nid) { - $pnode->status = 0; - - //$pnode->rid = 0; - node_save($pnode); - - // Check to see if the user wanted the project directory removed. - // If so, make sure the directory path is valid and isn't "/"!! - if ($data['deleting_project_remove_dir'] && - strlen($pnode->code_path) > 1) { - @exec("rm -rf {$pnode->code_path}"); - } - - // delete this project - db_query("DELETE FROM {hosting_devshop_project} WHERE nid = %d", - $pnode->nid); - } -} - -/* - * This function get called when we are in the process of deleting a - * project and all of its related sites & platforms. It is invoked - * by the one of the hook_post_hosting_TASK_TYPE_task() hook when - * a site disable or site/platform delete tasks complete. - * - * All of the sites that we need to disable and delete and all of the - * platforms we need to delete are kept in the project node's - * $node->data field. There are four elements in that array: - * - * - * project data['deleting_project']: if TRUE, we are in the process - * of delete this project and all sites & platforms. - * project data['project_delete_site_disable']: array of site NID's - * that are to be disabled. - * project data['project_delete_site_delete']: an array of site NID's - * that are to be deleted once they are disabled. - * project data['project_delete_platform_delete']: an array of platform - * NID's that are to be deleted once the sites based on these platforms - * are disabled and deleted. - * - * We first disable all of the sites, then we delete them. Next we delete - * all of the platforms. As soon as a site or platform is deleted, we remove - * the NID from the corresponding array. When everything is deleted, we - * remove the rows from the object table. Hostmaster will remove them from - * the hosting context table during the delete task. - * - * @param $nid - * The NID of the object which the task just completed on. It can be - * the project node (when the actual project is being deleted). The - * site site node (when the site was disabled or deleted), or the - * platform node (when the platform was deleted). - * @param $op - * The operation (task type) that just complted. Will be one of: - * 'project delete', 'site disable', 'site delete', or - * 'platform delete'. - * @param $failed - * If TRUE, this task did not complete succesfully. We know this - * the 'rollback' hook was invoked rather then the 'post task' - * hook. There isn't much we can do but log it and carry on. - */ -function devshop_projects_project_delete_continue($nid, $op, $failed = FALSE) { - - if (!($node = node_load($nid))) { - watchdog('devshop', "Delete continue: nid $nid not found"); - return; - } - - if ($node->type == 'project') { - // First step complete: project deleted. Now we need to - // disable all of the sites, delete them. And then delete - // all of the platforms. - devshop_projects_project_delete_queue_next_task($node); - return; - } - else if (($node->type != 'site') && - ($node->type != 'platform')) { - // We only care about sites and platforms - return; - } - - - // Does this object belong to a devshop project? If not, - // then just ignore it. - if (!($node->project_nid)) { - return; - } - - // load project node - if (!($project_node = node_load($node->project_nid))) { - watchdog('devshop', - "Delete continue: can't load project nid $node->project_nid"); - return; - } - - $data = devshop_projects_project_data_get($project_node->nid); - - if (!$data['deleting_project']) { - return; - } - - if ($op == 'delete') { - // delete this object if the task just complete was a delete - db_query("DELETE FROM {hosting_devshop_project_object} " . - "WHERE project_nid = %d AND object_nid = %d", - $project_node->nid, $node->nid); - } - - // Remove this nid from our array of things to do - $key1 = "project_delete_{$node->type}_{$op}"; - $key2 = array_search($node->nid, $data[$key1]); - - if ($key2 === FALSE) { - watchdog('devshop', "search for nid {$node->nid} failed, key = $key2"); - } - else { - array_shift($data[$key1]); - if (count($data[$key1]) == 0) { - unset($data[$key1]); - } - devshop_projects_project_data_set($project_node->nid, $data); - } - - // do the next delete task - devshop_projects_project_delete_queue_next_task($project_node); -} - -/* - * Implementation of hook_post_hosting_TASK_TYPE_task - * - */ -function devshop_projects_post_hosting_delete_task($task, $data) { - watchdog('devshop', "Delete post task hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'delete'); - -} - -/** - * Implementation of hook_hosting_TASK_TYPE_task_rollback(). - * - * This hook is invoked when the delete task fails. - * Carry on my wayward son. There will be peace when you are done. - * Lay your weary head to rest. Don't you cry no more. - */ -function devshop_projects_hosting_delete_task_rollback($task, $data) { - watchdog('devshop', "Delete task rollback hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); -} - -/** - * Implementation of hook_post_hosting_TASK_TYPE_task - * - */ -function devshop_projects_post_hosting_disable_task($task, $data) { - watchdog('devshop', "Disable post task hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'disable'); -} - -/** - * Implementation of hook_hosting_TASK_TYPE_task_rollback(). - * - * This hook is invoked when the disable task fails. But we must carry on! - */ -function devshop_projects_hosting_disable_task_rollback($task, $data) { - watchdog('devshop', "Disable task rollback hook called"); - devshop_projects_project_delete_continue($task->ref->nid, 'disable', TRUE); -} - - -/** - * Returns TRUE is the project create task for the given node failed. - */ -function devshop_project_project_create_failed($nid, &$task) { - - if ($nid) { - $task = hosting_get_most_recent_task($nid, 'devshop-create'); - if ($task && $task->task_status != 1) { - // Project Create failed. - return TRUE; - } - } - - // No task found OR it is successful - return FALSE; -} - diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 5977ead2e..c26381a2f 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -28,7 +28,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ //Add button for delete project $form['buttons']['delete'] = array( '#type' => 'markup', - '#value' => l(t('Delete this project'), 'node/'. $project->nid .'/project_delete', array('query' => array('token' => drupal_get_token($user->uid)))), + '#value' => l(t('Delete this project'), 'node/'. $project->nid .'/project_devshop-delete', array('query' => array('token' => drupal_get_token($user->uid)))), '#weight' => 10, ); } @@ -49,8 +49,12 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } // On Hosting Task: Delete Project form, do our own submission. - if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'delete' && $form['#node']->type == 'project'){ - $form['#submit'] = array('hosting_task_devshop_delete_form_submit'); + if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-delete') { + $node = node_load($form['nid']['#value']); + + if ($node->type == 'project') { + $form['#submit'] = array('hosting_task_devshop_delete_form_submit'); + } } @@ -64,14 +68,16 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ $node = $form['#node']; $project_node = node_load($node->project_nid); - $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); - $form['git_branch'] = array( - '#type' => 'select', - '#title' => t('Branch'), - '#default_value' => isset($node->git_branch)? $node->git_branch: NULL, - '#options' => $branch_options, - '#description' => t('Choose the branch that this platform should track.'), - ); + if ($project_node) { + $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); + $form['git_branch'] = array( + '#type' => 'select', + '#title' => t('Branch'), + '#default_value' => isset($node->git_branch)? $node->git_branch: NULL, + '#options' => $branch_options, + '#description' => t('Choose the branch that this platform should track.'), + ); + } } // Save values that need to be saved. diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index a723b9600..445b70a62 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -40,7 +40,7 @@ function devshop_projects_hosting_tasks() { 'dialog' => TRUE, ); - $tasks['project']['delete'] = array( + $tasks['project']['devshop-delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), 'access callback' => 'devshop_hosting_task_menu_access', @@ -293,18 +293,10 @@ function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ * * For "Delete" task. */ -function hosting_task_delete_form($node) { +function hosting_task_devshop_delete_form($node) { $form = array(); if ($node->type == 'project') { - $form['remove_dir'] = array( - '#type' => 'checkbox', - '#title' => t('Remove all project files and directories.'), - '#description' => t("Note that the platform & site directories are removed regardless of this option. By choosing this option, everything else in the directory '{$node->code_path}', including the directory itself, will be removed."), - '#default_value' => FALSE, - '#weight' => 30, - ); - $form['nid'] = array( '#type' => 'value', '#value' => $node->nid, @@ -319,43 +311,40 @@ function hosting_task_delete_form($node) { * * @see devshop_projects_form_alter(). We had to add the submit hadler there. */ -function hosting_task_delete_form_submit($form, &$form_state) { +function hosting_task_devshop_delete_form_submit($form, &$form_state) { $project = node_load($form_state['values']['nid']); - $snid = array(); - $pnid = array(); - - $sites = $project->project_objects['site']; - $platforms = $project->project_objects['platform']; - - //First add task to delete sites - if (!empty($sites)) { - foreach ($sites as $snid => $alias) { - hosting_add_task($snid, 'delete'); + $sites = array(); + $platforms = array(); + $snids = array(); + $pnids = array(); + $data = array(); + + if (!empty($project->project_objects['site'])) { + foreach ($project->project_objects['site'] as $nid => $type) { + //Use a query instead node_load for improve the performance + $title = db_result(db_query("SELECT title FROM {node} WHERE nid=%d", $nid)); + $sites[] = $title; + $snids[] = $nid; } } - $data_delete = array(); - - //Second add task to delete platforms - if (!empty($platforms)) { - foreach ($platforms as $pnid => $alias) { - //Is need delete code? - if ($form_state['values']['remove_dir']) { - $pnode = node_load($pnid); - $data_delete[] = $node->publish_path; - } - hosting_add_task($pnid, 'delete'); + if (!empty($project->project_objects['platform'])) { + foreach ($project->project_objects['platform'] as $nid => $type) { + $platform = node_load($nid); + $platforms[] = $platform->title; + $pnids[] = $nid; + $data[] = $platform->publish_path; } } - if (!empty($data_delete)) { - //Add task delete to delete directories - hosting_add_task($project->nid, 'delete', $data_delete); - } - - //The last step delete project node - node_delete($project->nid); + hosting_add_task($project->nid, 'devshop-delete', array( + 'sites' => implode(' ', $sites), + 'platforms' => implode(' ', $platforms), + 'snids' => implode(' ', $snids), + 'pnids' => implode(' ', $pnids), + 'data' => implode(' ', $data), + )); // Go back to the main project list page $form_state['redirect'] = 'hosting/projects'; From 4fef4c55bc3be062cc7186e5546d63a4bf44c922 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 29 Jan 2013 16:41:38 +0100 Subject: [PATCH 0353/3476] Jacinto. #1897852. Delete project --- devshop_projects/inc/tasks.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 3119b370d..4a40da999 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -334,7 +334,6 @@ function hosting_task_devshop_delete_form_submit($form, &$form_state) { $platforms = array(); $snids = array(); $pnids = array(); - $data = array(); if (!empty($project->project_objects['site'])) { foreach ($project->project_objects['site'] as $nid => $type) { @@ -350,7 +349,6 @@ function hosting_task_devshop_delete_form_submit($form, &$form_state) { $platform = node_load($nid); $platforms[] = $platform->title; $pnids[] = $nid; - $data[] = $platform->publish_path; } } @@ -359,7 +357,6 @@ function hosting_task_devshop_delete_form_submit($form, &$form_state) { 'platforms' => implode(' ', $platforms), 'snids' => implode(' ', $snids), 'pnids' => implode(' ', $pnids), - 'data' => implode(' ', $data), )); // Go back to the main project list page From d548bf29788b640bb3271d208f59c5babc940127 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 29 Jan 2013 18:01:27 +0100 Subject: [PATCH 0354/3476] Jacinto. #1882620. Show live domain in UI projects --- devshop_live/devshop_live.module | 21 +++++++++++++++++++++ devshop_projects/inc/ui.inc | 8 ++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index 8e538bb08..64c777a48 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -27,3 +27,24 @@ function devshop_live_add_domain($node, $domain) { return FALSE; } + +/** + * Implement of hook_devshop_projects_page(). + */ +function devshop_live_devshop_projects_page($rows, $header) { + $new_rows = array(); + $link_options = array('attributes' => array('target' => '_blank')); + + //add new header live + $header[] = t('Live site'); + + if (!empty($rows)) { + foreach ($rows as $nid => $row) { + $live_domain = db_result(db_query("SELECT live_domain FROM {hosting_devshop_project} WHERE nid=%d", $nid)); + $live_domain = l($live_domain, $live_domain, $link_options); + $new_rows[] = array_merge($row, array($live_domain)); + } + } + + return array('rows' => $new_rows, 'header' => $header); +} diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index abdbc2cc9..94e5074fd 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -56,10 +56,14 @@ function devshop_projects_projects_page() { $row[] = l($dev_site_url, $dev_site_url, $link_options); - $rows[] = $row; + $rows[$proj->nid] = $row; } - $output = theme('table', $header, $rows, array('class' => 'hosting-table')); + //Call hooks for alter informacion + $data = module_invoke_all('devshop_projects_page', $rows, $header); + + + $output = theme('table', $data['header'], $data['rows'], array('class' => 'hosting-table')); $output .= l(t('Create a new project?'), 'node/add/project'); return $output; } From f86bb039cac792f7838f6ea50352f5759d6316f0 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 29 Jan 2013 20:05:38 +0100 Subject: [PATCH 0355/3476] Jacinto. Fixed one check field creation --- devshop_live/devshop_live.install | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/devshop_live/devshop_live.install b/devshop_live/devshop_live.install index aaf8a0f04..6a7b7ccee 100644 --- a/devshop_live/devshop_live.install +++ b/devshop_live/devshop_live.install @@ -22,12 +22,7 @@ function devshop_live_schema_alter(&$schema) { function devshop_live_enable() { $ret = array(); - //Check if field was created - $schema = drupal_get_schema('hosting_devshop_project'); - - if (!isset($schema['fields']['live_domain'])) { - db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); - } + db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); return $ret; } From 87ab8c50a42119f0d076284436243fca1be2187d Mon Sep 17 00:00:00 2001 From: frazras Date: Tue, 29 Jan 2013 14:17:12 -0500 Subject: [PATCH 0356/3476] added database argument for syncing --- devshop_projects/devshop_projects.drush.inc | 1 + devshop_projects/inc/tasks.inc | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 2df17bc83..b3d6e4a5b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -46,6 +46,7 @@ function drush_devshop_projects_pre_hosting_task() { if ($task->ref->type == 'project' && $task->task_type == 'devshop-sync') { $task->args[] = $task->task_args['source']; $task->args[] = $task->task_args['destination']; + $task->options['database'] = $task->task_args['database']; $task->options['files'] = $task->task_args['files']; $task->options['pull'] = $task->task_args['pull']; $task->options['update'] = $task->task_args['update']; diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 82d42f20e..b44b2730d 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -60,6 +60,7 @@ function devshop_projects_hosting_tasks() { */ function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description, $key = 'environment', $title = 'Environment', $type = 'radios') { // @TODO: Add a check here. Sometimes we want to limit this list. + $options = array_combine($node->project_objects['site'], $node->project_objects['site']); $form[$key] = array( '#type' => $type, @@ -92,7 +93,7 @@ function hosting_task_devshop_create_form($node) { '#description' => t('Enter a system name for your environment. For consistency, you should make this match the branch name.'), '#required' => TRUE, ); - dsm($node); + $form['project_nid'] = array( '#type' => 'value', '#value' => $node->nid, @@ -105,13 +106,13 @@ function hosting_task_devshop_create_form($node) { */ function hosting_task_devshop_create_form_validate($form, &$form_state){ $params = $form_state['values']['parameters']; - + // Check for existing environments for this project. $object_nid = db_result(db_queryd('SELECT object_nid FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type = "%s" AND environment = "%s"', $params['project_nid'], 'platform', $params['platform_name'])); if ($object_nid) { form_set_error('platform_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['platform_name']))); } - + // Check for illegal chars if (!preg_match('!^[a-z0-9_]+$!', $params['platform_name'])) { form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); @@ -261,7 +262,7 @@ function hosting_task_devshop_sync_form($node) { devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment.'), 'destination', 'Destination'); - $form['databases'] = array( + $form['database'] = array( '#title' => t('Sync database from destination to source.'), '#type' => 'checkbox', '#default_value' => 1, From fbf23f5e9e12fe02ad8f84a06784d13b6c27131e Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 30 Jan 2013 15:29:57 +0100 Subject: [PATCH 0357/3476] Jacinto. Issue #1894126. Update branch in site when a platform is update --- devshop_projects/inc/nodes.inc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 96e80c13d..1d4db37ca 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -82,6 +82,17 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Save to table db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, environment, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); + + //If is platform update all sites with the corret git_branch + if ($node->type == 'platform') { + $result = db_query("SELECT nid FROM {hosting_site} WHERE platform=%d", $node->nid); + + while ($object = db_fetch_object($result)) { + $site = node_load($object->nid); + $site->git_branch = $node->git_branch; + node_save($site); + } + } } } } From 7ecbfaedd868583101c7ddb72a2c455971e78c9b Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 30 Jan 2013 16:26:37 +0100 Subject: [PATCH 0358/3476] Jacinto. Issue #18826200. Add link to view project and fix problem with link --- devshop_live/devshop_live.module | 2 ++ devshop_live/inc/nodes.inc | 28 ++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index 64c777a48..4019cab5d 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -41,6 +41,8 @@ function devshop_live_devshop_projects_page($rows, $header) { if (!empty($rows)) { foreach ($rows as $nid => $row) { $live_domain = db_result(db_query("SELECT live_domain FROM {hosting_devshop_project} WHERE nid=%d", $nid)); + + $live_domain = 'http://'. $live_domain; $live_domain = l($live_domain, $live_domain, $link_options); $new_rows[] = array_merge($row, array($live_domain)); } diff --git a/devshop_live/inc/nodes.inc b/devshop_live/inc/nodes.inc index 418f0f2bb..e6d097f80 100644 --- a/devshop_live/inc/nodes.inc +++ b/devshop_live/inc/nodes.inc @@ -11,17 +11,29 @@ * Implementation of hook_nodeapi(). */ function devshop_live_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { - if ($node->type == 'project' && in_array($op, array('insert', 'update'))) { - //Load all information - $node_p = node_load($node->nid); + if ($node->type == 'project') { + if (in_array($op, array('insert', 'update'))) { + //Load all information + $node_p = node_load($node->nid); - if ($node->live_domain && !isset($node_p->project_objects['site'])) { - $live_nid = array_search('live', $node_p->project_objects['site']); + if ($node->live_domain && !isset($node_p->project_objects['site'])) { + $live_nid = array_search('live', $node_p->project_objects['site']); - if ($live_nid) { - $live_node = node_load($live_nid); - devshop_live_add_domain($live_node, $node->live_domain); + if ($live_nid) { + $live_node = node_load($live_nid); + devshop_live_add_domain($live_node, $node->live_domain); + } } } + if ($op == 'view') { + $url = 'http://' . $node->live_domain; + + $node->content['info']['live_domain'] = array( + '#type' => 'item', + '#title' => t('Live domain'), + '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), + '#weight' => -9, + ); + } } } From 05543db3b224ca4a03fa0b54e19281008b5546eb Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 30 Jan 2013 17:35:34 +0100 Subject: [PATCH 0359/3476] Jacinto. Fixed minors bugs. Link empty live and delete task set status 0 --- devshop_projects/devshop_projects.drush.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 3594a618b..8f878c333 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -170,8 +170,9 @@ function devshop_projects_post_hosting_devshop_delete_task($task, $data) { } } - //The last step delete project node - node_delete($task->ref->nid); + //The last step set status = 0 project node + $task->ref->status = 0; + node_save($task->ref); } } From 37c34bb40a6d00ae4b254cecf980aef0ec73bd5f Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 30 Jan 2013 17:36:45 +0100 Subject: [PATCH 0360/3476] Jacinto. Change live domain to live site --- devshop_live/devshop_live.module | 6 ++++-- devshop_live/inc/nodes.inc | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index 27f32f455..2f92806ad 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -47,8 +47,10 @@ function devshop_live_devshop_projects_page($rows, $header) { foreach ($rows as $nid => $row) { $live_domain = db_result(db_query("SELECT live_domain FROM {hosting_devshop_project} WHERE nid=%d", $nid)); - $live_domain = 'http://'. $live_domain; - $live_domain = l($live_domain, $live_domain, $link_options); + if ($live_domain) { + $live_domain = 'http://'. $live_domain; + $live_domain = l($live_domain, $live_domain, $link_options); + } $new_rows[] = array_merge($row, array($live_domain)); } } diff --git a/devshop_live/inc/nodes.inc b/devshop_live/inc/nodes.inc index e6d097f80..13b9c3e12 100644 --- a/devshop_live/inc/nodes.inc +++ b/devshop_live/inc/nodes.inc @@ -30,7 +30,7 @@ function devshop_live_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { $node->content['info']['live_domain'] = array( '#type' => 'item', - '#title' => t('Live domain'), + '#title' => t('Live Site'), '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), '#weight' => -9, ); From 28ed965d6b4b0f71000fb2e99d23142a2dd85048 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 30 Jan 2013 17:45:27 +0100 Subject: [PATCH 0361/3476] Jacinto. Fixed perm for delete project --- devshop_projects/devshop_projects.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9db3a02df..cabfe933e 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -26,6 +26,7 @@ function devshop_projects_perm() { 'create devshop-commit task', 'create devshop-pull task', 'create devshop-sync task', + 'create devshop-delete task', 'create project', 'view project', 'edit project', From 4bd288561aefac69629bbe0874037c15b2cab3a3 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Thu, 31 Jan 2013 15:44:56 +0100 Subject: [PATCH 0362/3476] Jacinto. Remove deleted projects in list and not show task if access to unpublished(deleted) project. --- devshop_projects/devshop_projects.module | 5 +++++ devshop_projects/inc/ui.inc | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index cabfe933e..1d0c0767d 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -130,6 +130,11 @@ function devshop_hosting_task_menu_access($node, $task) { if (!isset($node->nid)){ $node = node_load($node); } + + if ($task != 'delete' && !$node->status) { + return FALSE; + } + if (user_access("create " . $task . " task")) { if ($node->type == 'project') { diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 94e5074fd..41e0e65f0 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -21,7 +21,7 @@ function devshop_projects_projects_page() { 'Dev Site', ); - $r = db_query("SELECT * FROM {hosting_devshop_project}"); + $r = db_query("SELECT hdp.* FROM {hosting_devshop_project} hdp LEFT JOIN {node} n ON hdp.nid=n.nid WHERE n.status=1"); $rows = array(); while(($proj = db_fetch_object($r))) { From 8dc6d98e1b0f05ef306966daed95cb86d5c9c956 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 1 Feb 2013 18:16:13 +0100 Subject: [PATCH 0363/3476] Jacinto. Fixed problem status = 0 new project --- devshop_projects/inc/create-wizard.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 9e04c321a..f2cbb32a1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -127,6 +127,8 @@ function devshop_projects_create_wizard_next(&$form_state) { * this is where you'd normally save. */ function devshop_projects_create_wizard_finish(&$form_state) { + global $user; + $project = &$form_state['project']; // Save the extra options to the project node. @@ -135,6 +137,8 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->base_url = $project->base_url; $node->install_profile = $project->install_profile; $node->live_domain = $project->live_domain; + $node->uid = $user->uid; + $node->status = 1; node_save($node); // Create the site nodes From 15b071fd5f14c06d19cd0965a03fad3dbfcb576e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 15:42:56 -0500 Subject: [PATCH 0364/3476] checking for domain existence before rendering it. --- devshop_live/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_live/inc/nodes.inc b/devshop_live/inc/nodes.inc index 13b9c3e12..b9c0840d1 100644 --- a/devshop_live/inc/nodes.inc +++ b/devshop_live/inc/nodes.inc @@ -25,7 +25,7 @@ function devshop_live_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { } } } - if ($op == 'view') { + if ($op == 'view' && !empty($node->live_domain)) { $url = 'http://' . $node->live_domain; $node->content['info']['live_domain'] = array( From 7f5d7c649de9721dd69c395019aa1c93fa689765 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 16:29:13 -0500 Subject: [PATCH 0365/3476] changing to right task type --- devshop_projects/inc/forms.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c26381a2f..802081dee 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -28,7 +28,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ //Add button for delete project $form['buttons']['delete'] = array( '#type' => 'markup', - '#value' => l(t('Delete this project'), 'node/'. $project->nid .'/project_devshop-delete', array('query' => array('token' => drupal_get_token($user->uid)))), + '#value' => l(t('Delete this project'), 'node/'. $project->nid .'/project_delete', array('query' => array('token' => drupal_get_token($user->uid)))), '#weight' => 10, ); } From fe5f8c2a0c9ad8a80ecd9ae2a30dfd42718c2d01 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 16:42:05 -0500 Subject: [PATCH 0366/3476] adding no_verify to project --- devshop_projects/inc/create-wizard.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f2cbb32a1..aec1de6f9 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -139,6 +139,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->live_domain = $project->live_domain; $node->uid = $user->uid; $node->status = 1; + $node->no_verify = TRUE; node_save($node); // Create the site nodes From 5ab9210aa56069692ab160df4715fd06bd16fc22 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 16:44:23 -0500 Subject: [PATCH 0367/3476] fixing project validation to ignore disabled projects. --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index aec1de6f9..fabbe04bd 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -348,10 +348,10 @@ function devshop_project_create_step_settings_validate(&$from, &$form_state) { $project = &$form_state['project']; // Code path and domain must be unique - if (db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s";', $form_state['values']['code_path']))){ + if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND code_path = "%s";', $form_state['values']['code_path']))){ form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); } - if (db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE base_url = "%s";', $form_state['values']['base_url']))){ + if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND base_url = "%s";', $form_state['values']['base_url']))){ form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); } From 133fc231a947d037b36f94bc2954ff2c7c7c8191 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 1 Feb 2013 22:46:27 +0100 Subject: [PATCH 0368/3476] Jacinto. New interface for pull --- devshop_pull/devshop_pull.module | 66 +++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 87d297d5f..1e4290011 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -79,8 +79,15 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { if (is_null($node->pull_method)) { $node->pull_method = DEVSHOP_PULL_DISABLED; } - - $form['pull_method'] = array( + + //All settings git pull in project page + $form['git_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Git Settings'), + '#tree' => TRUE, + ); + + $form['git_settings']['pull_method'] = array( '#title' => 'Automatic Git Pull Method', '#type' => 'radios', '#description' => t('Choose the method of regularly calling "Pull Code". See !link to configure the queue. See !link2 to configure URL Callback.', array( @@ -94,26 +101,26 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), ), ); - } - // On Projects, choose if Pull on Commit is enabled, and if "reset --hard" - // should be used to keep the codebase clean. - if ($form_id == 'platform_node_form') { - $platform = $form['#node']; - if (isset($platform->project_nid)) { - $default_value = $platform->nid ? $platform->pull_enabled : FALSE; - $form['pull_enabled'] = array( - '#title' => t('Pull on Commit'), - '#description' => t('If enabled. When detecting new code changes will be updated automatically.'), - '#type' => 'checkbox', - '#default_value' => $default_value, - ); - $form['pull_reset'] = array( - '#title' => 'Hard Reset on Pull', - '#type' => 'checkbox', - '#description' => t('Reset any changes to project files. WARNING: Any uncommitted changes to the project files will be discarded.'), - '#default_value' => $platform->pull_reset, - ); + $platforms = $node->project_objects['platform']; + + if (!empty($platforms)) { + foreach ($platforms as $nid => $name) { + $default_value = array(); + $platform = node_load($nid); + + $default_value['pull'] = ($platform->pull_enabled) ? 'pull' : 0; + $default_value['reset'] = ($platform->pull_reset) ? 'reset' : 0; + $form['git_settings']['git_'. $name] = array( + '#type' => 'checkboxes', + '#title' => $name, + '#options' => array( + 'pull' => t('Pull on commit'), + 'reset' => t('Hard reset'), + ), + '#default_value' => $default_value, + ); + } } } } @@ -203,7 +210,22 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { case 'update': if ($node->type == 'project') { db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->git_settings['pull_method']); + + //load all data + $project = node_load($node->nid); + $platforms = $project->project_objects['platform']; + + if (!empty($platforms)) { + foreach ($platforms as $nid => $name) { + $platform = node_load($nid); + + $platform->pull_enabled = ($node->git_settings['git_'. $name]['pull']) ? 1 : 0; + $platform->pull_reset = ($node->git_settings['git_'. $name]['reset']) ? 1 : 0; + $platform->no_verify = TRUE; + node_save($platform); + } + } } elseif ($node->type == 'platform') { db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); From 8d25c0273d4837319b5a27cb21fa566ef6481ba0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 16:47:43 -0500 Subject: [PATCH 0369/3476] cleaning up delete code --- devshop_projects/inc/tasks.inc | 62 +--------------------------------- 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index dcf6ad94a..462d0cc2c 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -40,8 +40,7 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); - - $tasks['project']['devshop-delete'] = array( + $tasks['project']['delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), 'access callback' => 'devshop_hosting_task_menu_access', @@ -319,62 +318,3 @@ function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ form_set_error('source', t('The source cannot be the same as the destination.')); } } -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Delete" task. - */ -function hosting_task_devshop_delete_form($node) { - $form = array(); - - if ($node->type == 'project') { - $form['nid'] = array( - '#type' => 'value', - '#value' => $node->nid, - ); - } - - return $form; -} - -/** - * Extra submit function for hosting_task_confirm_form() - * - * @see devshop_projects_form_alter(). We had to add the submit hadler there. - */ -function hosting_task_devshop_delete_form_submit($form, &$form_state) { - $project = node_load($form_state['values']['nid']); - - $sites = array(); - $platforms = array(); - $snids = array(); - $pnids = array(); - - if (!empty($project->project_objects['site'])) { - foreach ($project->project_objects['site'] as $nid => $type) { - //Use a query instead node_load for improve the performance - $title = db_result(db_query("SELECT title FROM {node} WHERE nid=%d", $nid)); - $sites[] = $title; - $snids[] = $nid; - } - } - - if (!empty($project->project_objects['platform'])) { - foreach ($project->project_objects['platform'] as $nid => $type) { - $platform = node_load($nid); - $platforms[] = $platform->title; - $pnids[] = $nid; - } - } - - hosting_add_task($project->nid, 'devshop-delete', array( - 'sites' => implode(' ', $sites), - 'platforms' => implode(' ', $platforms), - 'snids' => implode(' ', $snids), - 'pnids' => implode(' ', $pnids), - )); - - // Go back to the main project list page - $form_state['redirect'] = 'hosting/projects'; - drupal_goto('hosting/projects'); -} From 8e66c8e86edcf135b4e27d5a6b533d0268333d61 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 17:01:32 -0500 Subject: [PATCH 0370/3476] refactoring our delete method --- devshop_projects/devshop_projects.drush.inc | 85 ++++++++++++--------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 8f878c333..1f98547ca 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -34,13 +34,6 @@ function drush_devshop_projects_pre_hosting_task() { $task->options['cache'] = $task->task_args['cache']; } - //delete - if ($task->ref->type == 'project' && $task->task_type == 'devshop-delete') { - $task->args['sites'] = $task->task_args['sites']; - $task->args['platforms'] = $task->task_args['platforms']; - $task->args['data'] = $task->task_args['data']; - } - // Commit if ($task->ref->type == 'project' && $task->task_type == 'devshop-commit') { $task->args['environment'] = $task->task_args['environment']; @@ -140,49 +133,59 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ /** * Implementation of hook_post_hosting_TASK_TYPE_task + * + * Runs after project delete */ -function devshop_projects_post_hosting_devshop_delete_task($task, $data) { - if ($task->ref->type == 'project' && $task->task_type == 'devshop-delete') { - $sites = explode(' ', $task->task_args['snids']); - $platforms = explode(' ', $task->task_args['pnids']); - - //First sites - if (!empty($sites)) { - foreach ($sites as $key => $nid) { - hosting_context_delete($nid); - - $ref = node_load($nid); - $ref->no_verify = TRUE; - $ref->site_status = HOSTING_SITE_DELETED; - node_save($ref); +function devshop_projects_post_hosting_delete_task($task, $data) { + if ($task->ref->type == 'project') { + // We just trigger site deletion here. + // The Post Deletion hook must take care of platforms, since this must finish first. + $project = $task->ref; + if (!empty($project->project_objects['site'])) { + foreach ($project->project_objects['site'] as $nid => $name) { + hosting_add_task($nid, 'delete'); } } - //Second platforms - if (!empty($platforms)) { - foreach ($platforms as $key => $nid) { - $ref = node_load($nid); - $ref->platform_status = HOSTING_PLATFORM_DELETED; - $ref->no_verify = TRUE; - node_save($ref); - hosting_context_delete($ref->nid); - db_query("DELETE FROM {hosting_platform_client_access} WHERE pid = %d", $ref->nid); - } - } - - //The last step set status = 0 project node + // @TODO: Should probably add our own status column + // The last step set status = 0 project node $task->ref->status = 0; node_save($task->ref); } + + // When a site is deleted... delete the platform + if ($task->ref->type == 'site' && !empty($task->ref->project)) { + // We trigger platform deletion here. + hosting_add_task($task->ref->platform, 'delete'); + } + + // When a platform is deleted, if it is the last in the project, + // and the project has been unpublished, delete the directory. + if ($task->ref->type == 'platform' && !empty($task->ref->project)) { + $project = node_load($task->ref->project_nid); + drush_log('[DEVSHOP] Checking for other platforms...', 'ok'); + + if ($project->status == 0){ + // Don't know if this platform is still here or not... + unset($project->project_objects['platform'][$task->ref]); + if (empty($project->project_objects['platform'])){ + drush_log('[DEVSHOP] Last Platform! Removing code_path folder.', 'ok'); + rmdir($project->code_path); + } + } + + } + } /** * Implementation of hook_post_hosting_TASK_TYPE_task - * - * */ function devshop_projects_post_hosting_verify_task($task, $data) { + $project = $task->ref; + drush_log(print_r($project->project_objects,1), 'notice'); + // If this is a new platform created by a project, we will create a site here. if ($task->ref->type == 'platform') { // Get objects @@ -220,6 +223,16 @@ function devshop_projects_post_hosting_verify_task($task, $data) { drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); } + + // When a project is verified, queue a verification for all platforms + if ($task->ref->type == 'project'){ + $project = node_load($task->ref->nid); + $platform_nids = array_keys($project->project_objects['platform']); + foreach ($project->project_objects['platform'] as $nid => $name){ + drush_log('[DEVSHOP] Running verify on project: ' . $name, 'ok'); + hosting_add_task($nid, 'verify'); + } + } } /** From 185482f805f7dc06fc7c1393d6a8029f43dacf0c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 17:08:08 -0500 Subject: [PATCH 0371/3476] removing debug and unneeded node_load --- devshop_projects/devshop_projects.drush.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 1f98547ca..e12eb9b6b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -183,9 +183,6 @@ function devshop_projects_post_hosting_delete_task($task, $data) { */ function devshop_projects_post_hosting_verify_task($task, $data) { - $project = $task->ref; - drush_log(print_r($project->project_objects,1), 'notice'); - // If this is a new platform created by a project, we will create a site here. if ($task->ref->type == 'platform') { // Get objects From a76f9ed110dd48d9a15d0d2475054f62baf8abef Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 1 Feb 2013 23:25:17 +0100 Subject: [PATCH 0372/3476] Jacinto. Fixed little problem with new interface pull --- devshop_pull/devshop_pull.module | 37 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 1e4290011..1d26be108 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -122,6 +122,28 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { ); } } + + array_unshift($form['#submit'], 'devshop_pull_submit_save_data_platform'); + } +} + +/** + * Submit function for save data of platforms. + */ +function devshop_pull_submit_save_data_platform($form, &$form_state) { + $project = $form['#node']; + $values = $form_state['values']; + $platforms = $project->project_objects['platform']; + + if (!empty($platforms)) { + foreach ($platforms as $nid => $name) { + $platform = node_load($nid); + + $platform->pull_enabled = ($values['git_settings']['git_'. $name]['pull']) ? 1 : 0; + $platform->pull_reset = ($values['git_settings']['git_'. $name]['reset']) ? 1 : 0; + $platform->no_verify = TRUE; dpm($platform); + node_save($platform); + } } } @@ -211,21 +233,6 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'project') { db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->git_settings['pull_method']); - - //load all data - $project = node_load($node->nid); - $platforms = $project->project_objects['platform']; - - if (!empty($platforms)) { - foreach ($platforms as $nid => $name) { - $platform = node_load($nid); - - $platform->pull_enabled = ($node->git_settings['git_'. $name]['pull']) ? 1 : 0; - $platform->pull_reset = ($node->git_settings['git_'. $name]['reset']) ? 1 : 0; - $platform->no_verify = TRUE; - node_save($platform); - } - } } elseif ($node->type == 'platform') { db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); From 017bbd8c35d1d488d4d9f7adce660dc1382c1188 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 17:47:39 -0500 Subject: [PATCH 0373/3476] getting rid of dpm --- devshop_pull/devshop_pull.module | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 1d26be108..45c353a8e 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -141,7 +141,8 @@ function devshop_pull_submit_save_data_platform($form, &$form_state) { $platform->pull_enabled = ($values['git_settings']['git_'. $name]['pull']) ? 1 : 0; $platform->pull_reset = ($values['git_settings']['git_'. $name]['reset']) ? 1 : 0; - $platform->no_verify = TRUE; dpm($platform); + $platform->no_verify = TRUE; + node_save($platform); } } From 459208c0f38c623bf87f5f1572a18111a470b946 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 18:41:24 -0500 Subject: [PATCH 0374/3476] better, extendable settings form. --- devshop_projects/inc/forms.inc | 68 +++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 802081dee..9586fb3c1 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -63,23 +63,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ return; } - // On Platform node forms, allow changing branch. - if ($form_id == 'platform_node_form'){ - $node = $form['#node']; - $project_node = node_load($node->project_nid); - - if ($project_node) { - $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); - $form['git_branch'] = array( - '#type' => 'select', - '#title' => t('Branch'), - '#default_value' => isset($node->git_branch)? $node->git_branch: NULL, - '#options' => $branch_options, - '#description' => t('Choose the branch that this platform should track.'), - ); - } - } - // Save values that need to be saved. if ($form_id == 'platform_node_form' || $form_id == 'site_node_form'){ $node = $form['#node']; @@ -139,6 +122,40 @@ function devshop_projects_form(&$node) { '#maxlength' => 255, '#weight' => 2, ); + + // Extensible settings + $form['settings'] = array( + '#type' => 'fieldset', + '#title' => t('Environment Settings'), + '#tree' => TRUE, + ); + + $settings = module_invoke_all('devshop_project_settings'); + $branch_options = array_combine($node->git_branches, $node->git_branches); + foreach ($node->project_objects['platform'] as $nid => $name){ + $platform = node_load($nid); + $form['settings'][$name] = array( + '#tree' => TRUE, + '#title' => $name, + '#type' => 'fieldset', + ); + $form['settings'][$name]['git_branch'] = array( + '#type' => 'select', + '#title' => t('Branch'), + '#default_value' => isset($platform->git_branch)? $platform->git_branch: NULL, + '#options' => $branch_options, + '#description' => t('Choose the branch that this platform should track.'), + ); + foreach ($settings as $setting => $label){ + $form['settings'][$name][$setting] = array( + '#type' => 'checkbox', + '#title' => $label, + '#default_value' => $platform->{$setting}, + ); + } + } + $form['#submit'] = array('devshop_projects_submit_settings'); + // Don't allow editing if ($node->nid) { @@ -155,6 +172,23 @@ function devshop_projects_form(&$node) { return $form; } +/** + * Submit function for save data of platforms. + */ +function devshop_projects_submit_settings($form, &$form_state) { + $node = node_load($form_state['values']['nid']); + + // Go through and save our platforms + foreach ($node->project_objects['platform'] as $nid => $name){ + $platform = node_load($nid); + $platform->no_verify = TRUE; + foreach ($form_state['values']['settings'][$name] as $setting_name => $value){ + $platform->{$setting_name} = $value; + } + node_save($platform); + } +} + /** * Implementation of hook_validate(). */ From b52154059259ea3c881140a8ec050fd209fa9663 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 18:46:52 -0500 Subject: [PATCH 0375/3476] fixing it up so it uses our new settings hook --- devshop_pull/devshop_pull.module | 63 +++++++------------------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 45c353a8e..9ff9f5f10 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -65,6 +65,16 @@ function devshop_pull_hosting_queues() { return $items; } +/** + * Implements hook_devshop_project_settings + */ +function devshop_pull_devshop_project_settings(){ + return array( + 'pull_enabled' => t('Pull on Commit'), + 'pull_reset' => t('Hard Reset on Pull'), + ); +} + /** * Implements hook_form_alter(). */ @@ -84,7 +94,6 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { $form['git_settings'] = array( '#type' => 'fieldset', '#title' => t('Git Settings'), - '#tree' => TRUE, ); $form['git_settings']['pull_method'] = array( @@ -101,50 +110,6 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), ), ); - - $platforms = $node->project_objects['platform']; - - if (!empty($platforms)) { - foreach ($platforms as $nid => $name) { - $default_value = array(); - $platform = node_load($nid); - - $default_value['pull'] = ($platform->pull_enabled) ? 'pull' : 0; - $default_value['reset'] = ($platform->pull_reset) ? 'reset' : 0; - $form['git_settings']['git_'. $name] = array( - '#type' => 'checkboxes', - '#title' => $name, - '#options' => array( - 'pull' => t('Pull on commit'), - 'reset' => t('Hard reset'), - ), - '#default_value' => $default_value, - ); - } - } - - array_unshift($form['#submit'], 'devshop_pull_submit_save_data_platform'); - } -} - -/** - * Submit function for save data of platforms. - */ -function devshop_pull_submit_save_data_platform($form, &$form_state) { - $project = $form['#node']; - $values = $form_state['values']; - $platforms = $project->project_objects['platform']; - - if (!empty($platforms)) { - foreach ($platforms as $nid => $name) { - $platform = node_load($nid); - - $platform->pull_enabled = ($values['git_settings']['git_'. $name]['pull']) ? 1 : 0; - $platform->pull_reset = ($values['git_settings']['git_'. $name]['reset']) ? 1 : 0; - $platform->no_verify = TRUE; - - node_save($platform); - } } } @@ -216,24 +181,22 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'project') { $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); if (!empty($data->project_nid)) { + $node->pull_enabled = TRUE; $node->pull_method = $data->pull_method; $node->last_pull = $data->last_pull; $node->last_pull_status = $data->last_pull_status; } } - elseif ($node->type == 'platform') { - $data_project = db_fetch_array(db_query('SELECT project_nid, environment AS project_environment, n.title as project_name FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid WHERE object_nid = %d', $node->nid)); - if (isset($data_project['project_nid'])) { + elseif ($node->type == 'platform') { $data = db_fetch_array(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); return $data; } - } break; case 'insert': case 'update': if ($node->type == 'project') { db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->git_settings['pull_method']); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); } elseif ($node->type == 'platform') { db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); From 531687033e167613c0a45d2137251ce6a59f9bd0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 18:50:26 -0500 Subject: [PATCH 0376/3476] moving functions near where they are called --- devshop_pull/devshop_pull.inc | 23 +++++++++++++++++++++++ devshop_pull/devshop_pull.module | 23 ----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index b0bd49a08..e69aeb38c 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -89,3 +89,26 @@ function _devshop_pull_callback_url($node) { function _devshop_pull_hash_create($node) { return md5($node->hosting_name . $node->nid); } + +/** + * Prepares a "Pull Code" task for a project. + * + * @param $project_nid + * A project nid. + * + * Platforms in a project must be enabled to have this command run on them. + */ +function devshop_pull_project($project_nid) { + // Search platforms with pull enabled for this project + $results = db_query("SELECT environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND project_nid = %d", $project_nid); + + $args = array('environments' => ''); + + while ($info = db_fetch_object($results)){ + $args['environments'] .= $info->environment .' '; + } + hosting_add_task($project_nid, 'devshop-pull', $args); + + // Put timestamp + db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $project_nid); +} diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 9ff9f5f10..27975ef3d 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -252,26 +252,3 @@ function hosting_pull_queue($count) { module_invoke_all('devshop_pull', $project); } } - -/** - * Prepares a "Pull Code" task for a project. - * - * @param $project_nid - * A project nid. - * - * Platforms in a project must be enabled to have this command run on them. - */ -function devshop_pull_project($project_nid) { - // Search platforms with pull enabled for this project - $results = db_query("SELECT environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND project_nid = %d", $project_nid); - - $args = array('environments' => ''); - - while ($info = db_fetch_object($results)){ - $args['environments'] .= $info->environment .' '; - } - hosting_add_task($project_nid, 'devshop-pull', $args); - - // Put timestamp - db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $project_nid); -} From 9176566dc586660dbf503e0dbc983e2e4b12f06a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 19:19:19 -0500 Subject: [PATCH 0377/3476] saving settings to project context --- devshop_projects/devshop_projects.drush.inc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index e12eb9b6b..ad46a4ec4 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -257,6 +257,17 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['git_branches'] = $branches['branches']; $task->context_options['git_tags'] = $branches['tags']; + + // Load environment settings + foreach ($task->ref->project_objects['platform'] as $nid => $name){ + $platform = node_load($nid); + $task->context_options['settings'][$name] = array(); + + // @TODO: HOOK! + $task->context_options['settings'][$name]['git_branch'] = $platform->git_branch; + $task->context_options['settings'][$name]['pull_enabled'] = $platform->pull_enabled; + $task->context_options['settings'][$name]['pull_reset'] = $platform->pull_reset; + } } /** From 6937141025cb32da3f0e23c1957f0fb9eef71119 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 21:44:49 -0500 Subject: [PATCH 0378/3476] adding theme function --- devshop_projects/inc/forms.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 9586fb3c1..ab9d22ef6 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -138,6 +138,7 @@ function devshop_projects_form(&$node) { '#tree' => TRUE, '#title' => $name, '#type' => 'fieldset', + '#theme' => 'devshop_projects_settings_table', ); $form['settings'][$name]['git_branch'] = array( '#type' => 'select', From 03bec59f9099181c85e9ece91972f7c7b8642c5c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 21:45:16 -0500 Subject: [PATCH 0379/3476] removing old unused code --- devshop_projects/inc/forms.inc | 84 ---------------------------------- 1 file changed, 84 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index ab9d22ef6..eb0816dfa 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -241,87 +241,3 @@ function devshop_projects_validate($node, &$form) { form_set_error('code_path', t('Code path directory already exists.')); } } - -/** - * Form for platform creation - */ -function devshop_projects_platform_create_form($form_state, $project_nid) { - $project_node = node_load($project_nid); - - $form = array(); - - //Bail if no platforms yet. - if (!isset($project_node->project_objects['platform'])){ - $retry = devshop_project_project_create_failed($project_nid, $task); - if ($retry) { - $form['note'] = array( - '#type' => 'item', - '#title' => t('NOTICE!'), - '#description' => t('Project Create failed! You can ' . l('view the task log here.', "node/$task->nid") . ' Please ' . l('edit the project settings', "node/$project_nid/edit") . ' and make any necessary corrections and resubmit the form to try again.'), - '#weight' => -1, - ); - } - else { - $form['note'] = array( - '#type' => 'item', - '#title' => t('Clone & Verify'), - '#value' => t('Your code is on its way to the server. Once the platforms verify, you can choose an installation profile.'), - ); - } - return $form; - } - - $form['platform_name'] = array( - '#type' => 'textfield', - '#title' => t('Platform Name'), - '#required' => TRUE, - '#description' => t('Enter the name of the platform to be added to project ' . $project_node->title . '. Once the platform is created and verified, a site will be automatically created and added to this project.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => '', - '#maxlength' => 255, - ); - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Create platform'), - '#weight' => 32, - ); - return $form; -} - -/** - * Validates for platform create - */ -function devshop_projects_platform_create_form_validate(&$form, &$form_state){ - if (empty($form_state['values']['platform_name'])){ - form_set_error('install_profile', t('You must include a platform name')); - } -} - -/** - * Submit for platform create - * - */ -function devshop_projects_platform_create_form_submit(&$form, &$form_state){ - - $project_node = node_load($form_state['values']['nid']); - $args = array(); - if ($form_state['values']['git_url']) { - $args['git-url'] = $form_state['values']['git_url']; - } - - if ($form_state['values']['branch']) { - $args['branch'] = $form_state['values']['branch']; - } - - $args['platform-name'] = $form_state['values']['platform_name']; - - hosting_add_task($project_node->nid, 'devshop-platform-create', $args); - - $form_state['redirect'] = "node/{$project_node->nid}"; - drupal_goto("node/{$project_node->nid}"); -} From a26da8733c89f0aca14d4ad6cb369c0ed17ea187 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 22:23:37 -0500 Subject: [PATCH 0380/3476] finishing touches --- devshop_projects/devshop_projects.module | 1 + devshop_projects/inc/environments.css | 8 +++++- devshop_projects/inc/forms.inc | 1 + devshop_projects/inc/theme.inc | 36 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 devshop_projects/inc/theme.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 1d0c0767d..b07f4689f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -9,6 +9,7 @@ include_once('inc/forms.inc'); include_once('inc/nodes.inc'); include_once('inc/tasks.inc'); +include_once('inc/theme.inc'); // Lets keep front end code here. include_once('inc/ui.inc'); diff --git a/devshop_projects/inc/environments.css b/devshop_projects/inc/environments.css index 687b32fa8..4297ac116 100644 --- a/devshop_projects/inc/environments.css +++ b/devshop_projects/inc/environments.css @@ -8,4 +8,10 @@ body.aegir #page div.node div.content fieldset.project-environments div.form-ite fieldset.project-environments { clear: both; -} \ No newline at end of file +} + +#project-settings-table .description, +#project-settings-table .form-item-labeled label +{ + display: none; +} diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index eb0816dfa..c580d6c8f 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -127,6 +127,7 @@ function devshop_projects_form(&$node) { $form['settings'] = array( '#type' => 'fieldset', '#title' => t('Environment Settings'), + '#theme' => 'devshop_projects_settings_form', '#tree' => TRUE, ); diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc new file mode 100644 index 000000000..2045d5242 --- /dev/null +++ b/devshop_projects/inc/theme.inc @@ -0,0 +1,36 @@ + array( + 'arguments' => array( + 'form' => NULL, + ), + ), + ); +} +/** + * Theme function for environments settings + */ +function theme_devshop_projects_settings_form($form){ + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/environments.css'); + $rows = array(); + $header = array(); + $header[] = t('Environment'); + foreach (element_children($form) as $env_name) { + $row = array(); + $row[] = $env_name; + foreach(element_children($form[$env_name]) as $setting){ + if (!isset($header[$setting])){ + $header[$setting] = $form[$env_name][$setting]['#title']; + } + $row[] = drupal_render($form[$env_name][$setting]); + } + $rows[] = $row; + } + $output = theme('table', $header, $rows, array('id' => 'project-settings-table')); + return $output; +} \ No newline at end of file From e684ec9e0dafe46497ede4de0a354a369752876f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 22:30:37 -0500 Subject: [PATCH 0381/3476] removing the title from the checkbox --- devshop_projects/inc/theme.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 2045d5242..8ec02ecd8 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -27,6 +27,7 @@ function theme_devshop_projects_settings_form($form){ if (!isset($header[$setting])){ $header[$setting] = $form[$env_name][$setting]['#title']; } + $form[$env_name][$setting]['#title'] = ''; $row[] = drupal_render($form[$env_name][$setting]); } $rows[] = $row; From 421124844bcfb02d4fec0758e90333248ef8d27e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 22:38:04 -0500 Subject: [PATCH 0382/3476] get rid of pointless displays --- devshop_projects/inc/forms.inc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c580d6c8f..e6bb47c55 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -165,10 +165,6 @@ function devshop_projects_form(&$node) { foreach ($locked as $field){ $form[$field]['#value'] = $form[$field]['#default_value']; $form[$field]['#type'] = 'value'; - - // Be nice and show it. - $form["{$field}_display"] = $form[$field]; - $form["{$field}_display"]['#type'] = 'item'; } } return $form; From b6c3991b6a16bbb61564ce77e9b9f513e1f46c2e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 23:06:22 -0500 Subject: [PATCH 0383/3476] separating update and insert. --- devshop_pull/devshop_pull.module | 84 +++++++++++--------------------- 1 file changed, 29 insertions(+), 55 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 27975ef3d..989616269 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -122,58 +122,26 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { case 'view': if (!$a3) { //!teaser if($node->type == 'project') { - $pnode = $node; - } - elseif (isset($node->project_nid)) { - $pnode = node_load($node->project_nid); - } - else { - return; - } - - // @TODO: PULL QUEUE must be enabled first! Show a notice to the user if Pull Queue - // is not enabled! - - $pull_method = ($pnode->pull_method == DEVSHOP_PULL_CALLBACK? t('URL Callback'): - ($pnode->pull_method == DEVSHOP_PULL_QUEUE? t('Hosting Queue'): - (t('Disabled')))); - - if ($pnode->pull_method == DEVSHOP_PULL_CALLBACK) { - module_load_include('inc', 'devshop_pull'); - $url = _devshop_pull_callback_url($pnode); - $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); - } + if ($node->pull_method == DEVSHOP_PULL_CALLBACK) { + module_load_include('inc', 'devshop_pull'); + $url = _devshop_pull_callback_url($node); + $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); + + $last = (int) $node->last_pull; + if ($last == 0){ + $output = t('No Requests. Setup your Git host to ping'); + $output .= strtr(" ", array('!url' => $url)); - if (isset($pnode) && $pnode->pull_method != DEVSHOP_PULL_DISABLED) { - $node->content['devshop_pull'] = array( - '#type' => 'fieldset', - '#title' => t('Pull Configuration'), - '#weight' => 12, - ); - $node->content['devshop_pull']['pull'] = array( - '#type' => 'item', - '#title' => t('Pull Method'), - '#weight' => 30, - '#value' => $pull_method, - ); - $node->content['devshop_pull']['pull_reset'] = array( - '#type' => 'item', - '#title' => t('Reset on Pull'), - '#weight' => 31, - '#value' => $node->pull_reset ? t('Enabled') : t('Disabled'), - ); - $node->content['devshop_pull']['last_pull'] = array( - '#type' => 'item', - '#title' => t('Last pull'), - '#weight' => 32, - '#value' => hosting_format_interval($pnode->last_pull), - ); - $node->content['devshop_pull']['last_pull_status'] = array( - '#type' => 'item', - '#title' => t('Last pull status'), - '#weight' => 33, - '#value' => $pnode->last_pull_status == 1 ? t('OK') : ($pnode->last_pull ? t('Failed') : t('Never Pulled')), - ); + } else { + $output = hosting_format_interval($node->last_pull); + } + $node->content['info']['last_pull'] = array( + '#type' => 'item', + '#title' => t('Last Pull Request'), + '#weight' => 32, + '#value' => $output, + ); + } } } break; @@ -188,21 +156,27 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { } } elseif ($node->type == 'platform') { - $data = db_fetch_array(db_query("SELECT pull_enabled, pull_reset FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); + $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); return $data; } break; case 'insert': - case 'update': if ($node->type == 'project') { - db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); } elseif ($node->type == 'platform') { - db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_enabled, $node->pull_reset); } break; + + case 'update': + if ($node->type == 'project') { + db_query('UPDATE {hosting_devshop_pull_projects} SET project_nid = %d, pull_method = %d', $node->nid, $node->pull_method); + } + elseif ($node->type == 'platform') { + db_query('UPDATE {hosting_devshop_pull_platforms} SET platform_nid = %d, pull_enabled = %d, pull_reset = %d', $node->nid, $node->pull_enabled, $node->pull_reset); + } + break; } } } From 5376d3fc669e80b79d8d64fe31d13551b501336f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 23:08:56 -0500 Subject: [PATCH 0384/3476] cleaner branches display --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 41e0e65f0..55a756c10 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -127,7 +127,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->content['info']['git_branches'] = array( '#type' => 'item', '#title' => t('Remote Branches'), - '#value' => theme('item_list', $node->git_branches), + '#value' => implode($node->git_branches, ' '), '#weight' => -8 ); } From bdd0bbd3d8ca0c7181990acdceae2249ce35fe4d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 23:09:07 -0500 Subject: [PATCH 0385/3476] removing comments --- devshop_projects/inc/ui.inc | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 55a756c10..e2c2305b2 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -75,25 +75,6 @@ function devshop_projects_projects_page() { */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { - // If not ready yet: - if ($node->project_status != 'sites_ready'){ - //// @TODO: Once we have a smarter status, we can redirect them back the - //// create wizard. - // - //// Site install form - //$node->content['devshop'] = array( - // '#value' => '

' . t('Your project is still being configured... Sites are not ready yet.') . '

', - // '#type' => 'markup', - // '#weight' => 1, - //); - //$node->content['create'] = array( - // '#value' => '

' . l(t('Go back to Create Project Wizard.'), 'node/add/project/settings') . '

', - // '#type' => 'markup', - // '#weight' => 2, - //); - //return $node; - } - modalframe_parent_js(); $node->content['info'] = array( From 449df37018f695458b43fcbd9518f69bffa80b76 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 23:09:39 -0500 Subject: [PATCH 0386/3476] removing unimplemented feature --- devshop_pull/devshop_pull.settings.inc | 62 -------------------------- 1 file changed, 62 deletions(-) diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index 643f8c35e..c77586746 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -23,65 +23,3 @@ function devshop_pull_settings_form() { ); return system_settings_form($form); } - -/** - * Tool to enable pull on all projects. - */ -function devshop_pull_reset_form() { - $form['note'] = array( - '#value' => '

' . t('Reset all Projects') . '

' . '

' . t('If you have a lot of projects and need to turn them all on, you can do so here.') . '

', - ); - $form['pull_method'] = array( - '#title' => 'Automatic Git Pull Method', - '#type' => 'radios', - '#default_value' => DEVSHOP_PULL_DISABLED, - '#options' => array( -DEVSHOP_PULL_DISABLED => t('Pull disabled.'), -DEVSHOP_PULL_QUEUE => t('Pull on queue (every minute).'), -DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), - - ), - ); - - $form['enable_dev_sites'] = array( - '#title' => t('On all dev sites, Enable Pull on Commit and Reset Hard.'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Enable Pull for all projects'), - ); - return $form; -} - - -/** - * Submit function for devshop_pull_reset_form() - */ -function devshop_pull_reset_form_submit($form, &$form_state) { - /* - // Clear out tables - db_query('TRUNCATE {hosting_devshop_pull_platforms}'); - db_query('TRUNCATE {hosting_devshop_pull_projects}'); - - // Find all projects - $query = db_query('SELECT nid FROM node WHERE type="project" AND status=1;'); - while($node = db_fetch_object($query)){ - $pull_data = array(); - $pull_data['project_nid'] = $node->nid; - $pull_data['pull_method'] = $form_state['values']['pull_method']; - drupal_write_record('hosting_devshop_pull_projects', $pull_data); - } - - // Loop through and insert data into tables - - // Find all dev sites - - // Loop through and insert data into tables - - drupal_set_message(t('Set all projects to !method', array('!method' => $form_state['values']['pull_method']))); - */ - drupal_set_message('Not yet implemented!'); -} \ No newline at end of file From f91954d3578dab5776383dfc3846cc9ec6a93576 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 23:10:46 -0500 Subject: [PATCH 0387/3476] removing form --- devshop_pull/devshop_pull.settings.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index c77586746..988280a37 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -5,7 +5,6 @@ */ function devshop_pull_settings_page(){ $output .= drupal_get_form('devshop_pull_settings_form'); - $output .= drupal_get_form('devshop_pull_reset_form'); return $output; } From 3719cf6c422ae3ae809dd1cabf3ff2f3c8f146bb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 23:22:57 -0500 Subject: [PATCH 0388/3476] removing saving of sites on saving of a platform --- devshop_projects/inc/nodes.inc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 1d4db37ca..96e80c13d 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -82,17 +82,6 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Save to table db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, environment, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); - - //If is platform update all sites with the corret git_branch - if ($node->type == 'platform') { - $result = db_query("SELECT nid FROM {hosting_site} WHERE platform=%d", $node->nid); - - while ($object = db_fetch_object($result)) { - $site = node_load($object->nid); - $site->git_branch = $node->git_branch; - node_save($site); - } - } } } } From b980387645517ca6caf3d103e6bc49e6129fe08d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Feb 2013 23:29:24 -0500 Subject: [PATCH 0389/3476] putting back update branch code and adding no_verify --- devshop_projects/inc/nodes.inc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 96e80c13d..64a202cae 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -82,6 +82,18 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Save to table db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, environment, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); + + //If is platform update all sites with the corret git_branch + if ($node->type == 'platform') { + $result = db_query("SELECT nid FROM {hosting_site} WHERE platform=%d", $node->nid); + + while ($object = db_fetch_object($result)) { + $site = node_load($object->nid); + $site->no_verify; + $site->git_branch = $node->git_branch; + node_save($site); + } + } } } } From 192e098e342203640c9be7df69efe055feca918f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 3 Feb 2013 11:03:53 -0500 Subject: [PATCH 0390/3476] removing old hack file --- devshop_projects/inc/hack.inc | 129 ---------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 devshop_projects/inc/hack.inc diff --git a/devshop_projects/inc/hack.inc b/devshop_projects/inc/hack.inc deleted file mode 100644 index 38ce76840..000000000 --- a/devshop_projects/inc/hack.inc +++ /dev/null @@ -1,129 +0,0 @@ -type == 'project') { - module_load_include('tasks.inc', 'devshop_projects'); - $return['markup'] = devshop_projects_hosting_task_table($node); - $return['changed'] = $node->changed; - drupal_json($return); - exit(); - } else { - hosting_task_ajax_list($node); - } -} - -/** - * Replacement menu callback for hosting_js_page(). - * - * This is used for Project Tasks. Since the tasks are really tasks on the site nodes, - * we have to modify the JS call to modify what node gets passed in. - */ -function devshop_projects_hosting_js_page(){ - $args = func_get_args(); - $nid = &$args[1]; - $task = &$args[2]; - - // Get task type from $task argument... For whatver reason, they don't make it an argument - list($aegir_context, $task_type) = explode('_', $task); - - // If this is coming from a project page: - if ($aegir_context == 'project'){ - // Change the task object from project to site - $task = str_replace('project', 'site', $task); - - // Load the project - $project = node_load($nid); - $sites = array_flip($project->project_objects['site']); - - // Change the NID based on the task type - if ($task_type == 'devshop-commit') { - $nid = $sites['dev']; - } elseif ($task_type == 'devshop-sync') { - $nid = $sites['test']; - } elseif ($task_type == 'devshop-pull') { - $nid = $sites['live']; - } - } - - $output .= call_user_func_array('hosting_js_page', $args); - return $output; -} -/** - * A replacement for hosting_task_table, to allow us to add - * tasks from other nodes. - */ -function devshop_projects_hosting_task_table($node) { - $output = ''; - - $headers[] = t('Task'); - $headers[] = array( - 'data' => t('Actions'), - 'class' => 'hosting-actions', - ); - - $tasklist = hosting_task_fetch_tasks($node->nid); - if ($node->project_status != 'sites_ready'){ - return; - } - - // Get tasklists for all sites - $tasks = array(); - foreach ($node->project_objects['site'] as $nid => $env){ - $site_nodes[$env] = node_load($nid); - $tasks[$env] = hosting_task_fetch_tasks($nid); - } - - // Add our own specific tasks - $tasklist['devshop-commit'] = $tasks['dev']['devshop-commit']; - $tasklist['devshop-sync'] = $tasks['test']['devshop-sync']; - $tasklist['devshop-pull'] = $tasks['live']['devshop-pull']; - - // Enhance titles - $tasklist['devshop-commit']['title'] .= ' on ' . l($site_nodes['dev']->title, 'http://'. $site_nodes['dev']->title, array('attributes' => array('target' => '_blank'))); - $tasklist['devshop-sync']['title'] .= ' on ' . l($site_nodes['test']->title, 'http://'. $site_nodes['test']->title, array('attributes' => array('target' => '_blank'))); - $tasklist['devshop-pull']['title'] .= ' on ' . l($site_nodes['live']->title, 'http://'. $site_nodes['live']->title, array('attributes' => array('target' => '_blank'))); - - // Override some - unset($tasklist['devshop-create']['task_permitted']); - unset($tasklist['devshop-create']['nid']); - - unset($tasklist['devshop-platform-create']['task_permitted']); - unset($tasklist['devshop-platform-create']['nid']); - - foreach ($tasklist as $task => $info) { - $row = array(); - - if (!isset($info['nid']) && !$info['task_permitted']) { - // just don't show those tasks, since we'll not be able to run them - continue; - } - - $row['type'] = array( - 'data' => $info['title'], - 'class' => 'hosting-status', - ); - $actions = array(); - - if (isset($info['task_status']) && ($info['task_status'] == 0)) { - $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']); - } - else { - $actions['run'] = _hosting_task_button(t('Run'), sprintf("node/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']); - } - - $actions['log'] = _hosting_task_button(t('Log'), 'node/' . $info['nid'], t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE); - $row['actions'] = array( - 'data' => implode('', $actions), - 'class' => 'hosting-actions', - ); - - $rows[] = array( - 'data' => $row, - 'class' => $info['class'], - ); - } - $output .= theme('table', $headers, $rows, array('class' => 'hosting-table')); - return $output; -} \ No newline at end of file From 19468c50a34d4ccd8ca2af49bdd4fda78f8b6a9e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 3 Feb 2013 11:12:46 -0500 Subject: [PATCH 0391/3476] adding breadcrumbs --- devshop_projects/inc/nodes.inc | 24 ---------------- devshop_projects/inc/ui.inc | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 64a202cae..f88b0ccc6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -45,30 +45,6 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { return $data; } - // Display Project info - if ($op == 'view'){ - if (!empty($node->project)){ - $node->content['info']['project'] = array( - '#type' => 'item', - '#title' => t('Project'), - '#value' => l($node->project, "node/{$node->project_nid}"), - '#weight' => -12, - ); - $node->content['info']['env'] = array( - '#type' => 'item', - '#title' => t('Environment'), - '#value' => $node->environment, - '#weight' => -11, - ); - $node->content['info']['branch'] = array( - '#type' => 'item', - '#title' => t('Branch'), - '#value' => $node->git_branch, - '#weight' => -11, - ); - } - } - // On update or delete, delete the existing records. We will replace below. if ($op == 'update' || $op == 'delete') { db_query('DELETE FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index e2c2305b2..ca6c7675c 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -196,5 +196,56 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#type' => 'fieldset', '#weight' => 100, ); + + + // Add Breadcrumbs + $crumbs = array(); + $crumbs[] = l(t('Home'), ''); + $crumbs[] = l(t('Projects'), 'hosting/projects'); + drupal_set_breadcrumb($crumbs); + return $node; } + +/** + * Implements hook_nodeapi_TYPE_OP() + */ +function devshop_projects_nodeapi_site_view(&$node, $a3, $a4) { + if (!empty($node->project)){ + + + // Display Project, Environment and Branch. + $node->content['info']['project'] = array( + '#type' => 'item', + '#title' => t('Project'), + '#value' => l($node->project, "node/{$node->project_nid}"), + '#weight' => -12, + ); + $node->content['info']['env'] = array( + '#type' => 'item', + '#title' => t('Environment'), + '#value' => $node->environment, + '#weight' => -11, + ); + $node->content['info']['branch'] = array( + '#type' => 'item', + '#title' => t('Branch'), + '#value' => $node->git_branch, + '#weight' => -11, + ); + + // Add Breadcrumbs + $crumbs = array(); + $crumbs[] = l(t('Home'), ''); + $crumbs[] = l(t('Projects'), 'hosting/projects'); + $crumbs[] = l($node->project, "node/" . $node->project_nid); + drupal_set_breadcrumb($crumbs); + } +} + +/** + * Implements hook_nodeapi_TYPE_OP() + */ +function devshop_projects_nodeapi_platform_view(&$node, $a3, $a4) { + devshop_projects_nodeapi_site_view($node, $a3, $a4); +} \ No newline at end of file From 489d9d3d051982014588e1fcf8ce6505e131edec Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 19:48:18 -0500 Subject: [PATCH 0392/3476] removing old code --- devshop_projects/inc/ui.inc | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index abdbc2cc9..ce7878123 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -71,25 +71,6 @@ function devshop_projects_projects_page() { */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { - // If not ready yet: - if ($node->project_status != 'sites_ready'){ - //// @TODO: Once we have a smarter status, we can redirect them back the - //// create wizard. - // - //// Site install form - //$node->content['devshop'] = array( - // '#value' => '

' . t('Your project is still being configured... Sites are not ready yet.') . '

', - // '#type' => 'markup', - // '#weight' => 1, - //); - //$node->content['create'] = array( - // '#value' => '

' . l(t('Go back to Create Project Wizard.'), 'node/add/project/settings') . '

', - // '#type' => 'markup', - // '#weight' => 2, - //); - //return $node; - } - modalframe_parent_js(); $node->content['info'] = array( From 945d3245a5c13d7400f7803a8eca7bdfa7ccd16c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 19:59:55 -0500 Subject: [PATCH 0393/3476] Reworking the menu callbacks. --- devshop_projects/devshop_projects.module | 27 ++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index ab2d8ccbc..bf20abafe 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -35,14 +35,30 @@ function devshop_projects_perm() { function devshop_projects_menu() { $items['hosting/projects'] = array( - 'title' => 'Projects', + 'title' => t('Projects'), 'description' => 'Display a list of all projects', 'page callback' => 'devshop_projects_projects_page', 'access arguments' => array('view projects'), 'menu_name' => 'primary-links', 'weight' => 1, ); - + $items['hosting/projects/list'] = array( + 'title' => 'Projects', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + $items['hosting/projects/add'] = array( + 'title' => 'Start New Project', + 'type' => MENU_LOCAL_TASK, + 'title' => t('Start a new Project'), + 'title callback' => 'check_plain', + 'page callback' => 'devshop_projects_create_wizard', + 'page arguments' => array(3), + 'access callback' => 'node_access', + 'access arguments' => array('create', 'project'), + 'description' => 'Start a new Drupal website project.', + 'file' => 'create-wizard.inc', + 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', + ); // hosting tasks ajax pages. foreach (hosting_available_tasks('project') as $task => $info){ @@ -72,10 +88,9 @@ function devshop_projects_hosting_task_confirm_form_page($nid, $task){ * Replaces node/add/project with a ctools wizard. */ function devshop_projects_menu_alter(&$items) { - $items['node/add/project']['page callback'] = 'devshop_projects_create_wizard'; - $items['node/add/project']['page arguments'] = array(3); - $items['node/add/project']['file'] = 'create-wizard.inc'; - $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects') . '/inc'; + $items['node/add/project']['page callback'] = 'drupal_goto'; + $items['node/add/project']['page arguments'] = array('hosting/projects/add'); + } From 53ba3f5490dbb1194e1e133bbd1caac3a9911490 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 20:00:11 -0500 Subject: [PATCH 0394/3476] head back to the wizard if the project isn't ready yet. --- devshop_projects/inc/ui.inc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index ce7878123..2abcbd26d 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -71,6 +71,16 @@ function devshop_projects_projects_page() { */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { + // Check to see if this project is still in the wizard + ctools_include('object-cache'); + $project_wizard_cache = ctools_object_cache_get('project', NULL); + if ($node->nid == $project_wizard_cache->project_nid){ + drupal_goto('hosting/projects/add/' . $project_wizard_cache->step); + } + + // Setup project + + modalframe_parent_js(); $node->content['info'] = array( From 0cca78929ea7677ad7af25dbb15600f859b7ae76 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 20:00:29 -0500 Subject: [PATCH 0395/3476] changing wizard to live at hosting/projects/add --- devshop_projects/inc/create-wizard.inc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 9e04c321a..4375d8b79 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -27,6 +27,7 @@ function devshop_projects_create_wizard($step = NULL){ // set form to first step -- we have no data $step = current(array_keys($form_info['order'])); $project = new stdClass(); + $project->step = $step; $project->git_url = ''; $project->project_nid = NULL; $project->title = ''; @@ -36,6 +37,10 @@ function devshop_projects_create_wizard($step = NULL){ // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); + } else { + // Quickly save the current step + $project->step = $step; + ctools_object_cache_set('project', $form_state['cache name'], $project); } // Check verification status @@ -50,7 +55,7 @@ function devshop_projects_create_wizard($step = NULL){ drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); // If not on the first step, go to it. if ($step != current(array_keys($form_info['order']))){ - drupal_goto('node/add/project'); + drupal_goto('hosting/projects/add'); } } @@ -71,7 +76,7 @@ function devshop_projects_create_wizard($step = NULL){ function devshop_projects_create_wizard_info(){ return array( 'id' => 'devshop_project_create', - 'path' => "node/add/project/%step", + 'path' => "hosting/projects/add/%step", 'show trail' => TRUE, 'show back' => TRUE, 'show cancel' => TRUE, @@ -569,7 +574,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } elseif ($project_node->project_status == 'preparing-project') { // @TODO: Not yet, needs more logic - // drupal_goto('node/add/project'); + // drupal_goto('hosting/projects/add'); } elseif ($project_node->project_status == 'sites-installed') { return; @@ -630,7 +635,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { $completed = FALSE; - $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'node/add/project/environments'))); + $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))); } // If platform verified successfully: elseif ($task->task_status == HOSTING_TASK_PROCESSING) { @@ -668,7 +673,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project->no_finish = TRUE; $form['error'] = array( '#type' => 'markup', - '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'node/add/project/environments'))) + '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) ); return $form; } else { From dad4ae5fde0ea13b441eff3d8bfe4b5ff6f0c14b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 20:30:37 -0500 Subject: [PATCH 0396/3476] removing bad merge --- devshop_projects/inc/ui.inc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 42ba19f39..11f0f017a 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -75,7 +75,6 @@ function devshop_projects_projects_page() { */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { -<<<<<<< HEAD // Check to see if this project is still in the wizard ctools_include('object-cache'); $project_wizard_cache = ctools_object_cache_get('project', NULL); @@ -83,11 +82,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { drupal_goto('hosting/projects/add/' . $project_wizard_cache->step); } - // Setup project - - -======= ->>>>>>> 19468c50a34d4ccd8ca2af49bdd4fda78f8b6a9e modalframe_parent_js(); $node->content['info'] = array( From d8ef650be208bc11cc804a8b08673ac85232a61b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 20:31:32 -0500 Subject: [PATCH 0397/3476] removing bad merge --- devshop_projects/devshop_projects.module | 6 ------ 1 file changed, 6 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 2810ca25d..ee83b27b5 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -100,11 +100,6 @@ function devshop_projects_hosting_task_confirm_form_page($nid, $task){ * Replaces node/add/project with a ctools wizard. */ function devshop_projects_menu_alter(&$items) { -<<<<<<< HEAD - $items['node/add/project']['page callback'] = 'drupal_goto'; - $items['node/add/project']['page arguments'] = array('hosting/projects/add'); - -======= $items['node/add/project']['page callback'] = 'devshop_projects_create_wizard'; $items['node/add/project']['page arguments'] = array(3); $items['node/add/project']['file'] = 'create-wizard.inc'; @@ -117,7 +112,6 @@ function devshop_projects_menu_alter(&$items) { $items['node/%node/edit']['title callback'] = 'devshop_hosting_project_tab_title'; $items['node/%node/edit']['title arguments'] = array('Edit', 1); ->>>>>>> 19468c50a34d4ccd8ca2af49bdd4fda78f8b6a9e } /** From 4d01c5f48549c1294ee2fdd6c9235d02aba0b283 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 20:32:39 -0500 Subject: [PATCH 0398/3476] removing old "create project" link --- devshop_projects/inc/ui.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 11f0f017a..b676b64bc 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -64,7 +64,6 @@ function devshop_projects_projects_page() { $output = theme('table', $data['header'], $data['rows'], array('class' => 'hosting-table')); - $output .= l(t('Create a new project?'), 'node/add/project'); return $output; } From fa472f2a7069cd5f5911cae9a57844e6853f1d7c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 20:33:32 -0500 Subject: [PATCH 0399/3476] order project page by alphabet --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index b676b64bc..ccee295d9 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -21,7 +21,7 @@ function devshop_projects_projects_page() { 'Dev Site', ); - $r = db_query("SELECT hdp.* FROM {hosting_devshop_project} hdp LEFT JOIN {node} n ON hdp.nid=n.nid WHERE n.status=1"); + $r = db_query("SELECT hdp.* FROM {hosting_devshop_project} hdp LEFT JOIN {node} n ON hdp.nid = n.nid WHERE n.status = 1 ORDER BY n.title"); $rows = array(); while(($proj = db_fetch_object($r))) { From d2204af5b1c55f6e974ad59d41625f0e837cdea6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 21:15:11 -0500 Subject: [PATCH 0400/3476] avoiding a php notice. --- devshop_projects/devshop_projects.module | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index ee83b27b5..5ac33dff9 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -304,7 +304,11 @@ function _devshop_projects_site_has_module($node, $module) { * Check if a site has features diff enabled. */ function _devshop_projects_project_has_module($node, $module) { - $sites = array_flip($node->project_objects['site']); + if (is_array($node->project_objects['site'])){ + $sites = array_flip($node->project_objects['site']); + } else { + $sites = array(); + } return _devshop_projects_site_has_module(node_load($sites['dev']), $module); } From cc19667b1bae3b38221600fb59207693d2a105e0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 21:27:31 -0500 Subject: [PATCH 0401/3476] adding DevShop Hosting module! Are ready for some UI overrides, aegir? --- devshop_hosting.module | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 devshop_hosting.module diff --git a/devshop_hosting.module b/devshop_hosting.module new file mode 100644 index 000000000..10f4a12f8 --- /dev/null +++ b/devshop_hosting.module @@ -0,0 +1,59 @@ + 'Welcome to DevShop', + 'page callback' => 'devshop_hosting_home', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + return $items; +} + + +/** + * Implements hook_menu() + */ +function devshop_hosting_menu_alter(&$items){ + $items['user/password']['type'] = MENU_CALLBACK; + + unset($items['hosting/sites']); + unset($items['hosting/platforms']); +} + +/** + * Provides a login page or redirects to proejcts + */ +function devshop_hosting_home(){ + if (user_is_logged_in()){ + drupal_goto(variable_get('devshop_frontpage', 'hosting/projects')); + } else { + $output = '

' . t('Please log in.') . '

'; + return drupal_get_form('user_login'); + } +} + +/** + * Implements hook_form_user_login_alter() + * Provides some UI enhancements. + */ +function devshop_hosting_form_user_login_alter(&$form){ + $form['pass']['#description'] .= ' ' . l(t('Forgot your Password?'), 'user/password'); +} + + +/** + * Implements hook_form_user_login_block_alter() + * Provides some UI enhancements. + */ +function devshop_hosting_form_user_login_block_alter(&$form){ + if (arg(0) == 'devshop'){ + $form = array(); + } +} \ No newline at end of file From ccddb80bad0f251d3204e1bb929795b62354e6d0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 21:27:49 -0500 Subject: [PATCH 0402/3476] adding DevShop Hosting module! Are ready for some UI overrides, aegir? --- devshop_hosting.info | 6 ++++++ devshop_hosting.install | 9 +++++++++ 2 files changed, 15 insertions(+) create mode 100644 devshop_hosting.info create mode 100644 devshop_hosting.install diff --git a/devshop_hosting.info b/devshop_hosting.info new file mode 100644 index 000000000..787466f2e --- /dev/null +++ b/devshop_hosting.info @@ -0,0 +1,6 @@ +name = DevShop +description = Drupal Development Environment Manager +core = 6.x +package = DevShop + +dependencies[] = hosting diff --git a/devshop_hosting.install b/devshop_hosting.install new file mode 100644 index 000000000..02c17460e --- /dev/null +++ b/devshop_hosting.install @@ -0,0 +1,9 @@ +devshop.')); +} \ No newline at end of file From 1a54203d3a905f2025ccbfcb65759b80fae1afd7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 21:49:01 -0500 Subject: [PATCH 0403/3476] Much better empty project page. --- devshop_projects/inc/environments.css | 20 ++++++++++++++++++++ devshop_projects/inc/theme.inc | 2 +- devshop_projects/inc/ui.inc | 25 ++++++++++++++++++++----- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/devshop_projects/inc/environments.css b/devshop_projects/inc/environments.css index 4297ac116..793bcdf69 100644 --- a/devshop_projects/inc/environments.css +++ b/devshop_projects/inc/environments.css @@ -15,3 +15,23 @@ fieldset.project-environments { { display: none; } +.empty { + text-align: center; +} +.empty .empty-message { + padding:30px; +} +.empty .empty-button a { + font: bold 13px/20px "Helvetica Neue",Arial,sans-serif; + border: 0px; + padding: 5px 10px; + color: #fff; + background: #666; + cursor: pointer; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +textarea#rsa { + display: none; +} \ No newline at end of file diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 8ec02ecd8..f098310eb 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -16,7 +16,7 @@ function devshop_projects_theme(){ * Theme function for environments settings */ function theme_devshop_projects_settings_form($form){ - drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/environments.css'); + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); $rows = array(); $header = array(); $header[] = t('Environment'); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index ccee295d9..5393dabf0 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -11,6 +11,7 @@ * a table. */ function devshop_projects_projects_page() { + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/environments.css'); $header = array( 'Name', @@ -59,11 +60,25 @@ function devshop_projects_projects_page() { $rows[$proj->nid] = $row; } - //Call hooks for alter informacion - $data = module_invoke_all('devshop_projects_page', $rows, $header); - - - $output = theme('table', $data['header'], $data['rows'], array('class' => 'hosting-table')); + // No Projects + if (empty($rows)){ + $output = '
'; + $output .= '
You haven\'t created a project yet!
'; + $output .= '
' . l(t('Create your first Project*'), 'hosting/projects/add') . '
'; + $output .= '
*If you haven\'t granted this server access to your Git repositories, you should do so now with our public SSH key.
'; + + $pubkey = file_get_contents('/var/aegir/.ssh/id_rsa.pub'); + $output .= ''; + $output .= '
'; + } else { + //Call hooks for alter informacion + $data = module_invoke_all('devshop_projects_page', $rows, $header); + + $output = theme('table', $data['header'], $data['rows'], array('class' => 'hosting-table')); + } + + + return $output; } From 796041bd4ba42b9b0e68cc888ecdc1b7c1e2fa97 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 21:54:41 -0500 Subject: [PATCH 0404/3476] adding t() --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 5ac33dff9..d2c8db78e 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -50,7 +50,7 @@ function devshop_projects_menu() { 'weight' => 1, ); $items['hosting/projects/list'] = array( - 'title' => 'Projects', + 'title' => t('All Projects'), 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['hosting/projects/add'] = array( From 512b2e1afbaf81ccc0f291f752e67cf9bd19a31f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 22:18:49 -0500 Subject: [PATCH 0405/3476] renaming css file --- devshop_projects/inc/{environments.css => devshop.css} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename devshop_projects/inc/{environments.css => devshop.css} (100%) diff --git a/devshop_projects/inc/environments.css b/devshop_projects/inc/devshop.css similarity index 100% rename from devshop_projects/inc/environments.css rename to devshop_projects/inc/devshop.css From 8db2d479fe6fe46e74f35a1ab114c53aaaea6669 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 23:09:47 -0500 Subject: [PATCH 0406/3476] More UI updates, checks for SSH keys --- devshop_projects/inc/create-wizard.inc | 24 ++++++++++++++++++++++-- devshop_projects/inc/ui.inc | 19 +++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index d661f9306..e969fc774 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -7,6 +7,7 @@ function devshop_projects_create_wizard($step = NULL){ // required includes for wizard ctools_include('wizard'); ctools_include('object-cache'); + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); // Setup form info $form_info = devshop_projects_create_wizard_info(); @@ -222,7 +223,6 @@ function devshop_project_create_step_git(&$form, &$form_state) { ); // If there is already a title, make this an item. - // @TODO: Can we allow "renaming" of projects? (probably only at this stage). if (!empty($project->title)) { // Force title $form['title']['#value'] = $form['title']['#default_value']; @@ -232,8 +232,28 @@ function devshop_project_create_step_git(&$form, &$form_state) { // Be nice and show it. $form['title_display'] = $form['title']; $form['title_display']['#type'] = 'item'; + } + + // Display helpful tips for connecting. + $pubkey_path = variable_get('devshop_public_key_path', '/var/aegir/.ssh/id_rsa.pub'); + if (!file_exists($pubkey_path)){ + $output = t('No file was found at %path! To clone a Git repository to this server you need an SSH Key. See !link for a nice guide to generating SSH keys.', array('%path' => $pubkey_path, '!link' => l('github:help', 'https://help.github.com/articles/generating-ssh-keys', array('target' => '_blank')))); + } else { + $pubkey = file_get_contents('/var/aegir/.ssh/id_rsa.pub'); + + // @TODO: Make this Translatable. + $output = <<If you haven't granted this server access to your Git repositories, you should do so now using our public SSH key. + +HTML; + } - } + // Add info about connecting to Repo + $form['connect'] = array( + '#type' => 'item', + '#title' => t('Repository Access'), + '#value' => $output, + ); } /** diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 5393dabf0..3e39ab644 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -11,7 +11,7 @@ * a table. */ function devshop_projects_projects_page() { - drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/environments.css'); + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); $header = array( 'Name', @@ -62,14 +62,13 @@ function devshop_projects_projects_page() { // No Projects if (empty($rows)){ - $output = '
'; - $output .= '
You haven\'t created a project yet!
'; - $output .= '
' . l(t('Create your first Project*'), 'hosting/projects/add') . '
'; - $output .= '
*If you haven\'t granted this server access to your Git repositories, you should do so now with our public SSH key.
'; - - $pubkey = file_get_contents('/var/aegir/.ssh/id_rsa.pub'); - $output .= ''; - $output .= '
'; + $button = l(t('Create your first Project'), 'hosting/projects/add'); + $output = << +
You haven't created a project yet!
+
$button
+ +HTML; } else { //Call hooks for alter informacion $data = module_invoke_all('devshop_projects_page', $rows, $header); @@ -210,7 +209,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { drupal_add_js($settings, 'setting'); drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); - drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/environments.css'); + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); // MAIN DISPLAY $node->content['devshop'] = array( From 67daf072d022bc0dca5e3233156f63a451d1603d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 23:29:33 -0500 Subject: [PATCH 0407/3476] much nicer wait page. --- devshop_projects/inc/create-wizard.inc | 5 ++++- devshop_projects/inc/devshop.css | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index e969fc774..0a6396bf1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -412,9 +412,12 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? + $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; + $note .= '

' . t('NOTE: It is a known bug that this page doesn\'t automatically reload. Please reload the page manually once the task is complete.') . '

'; + $form['note'] = array( '#type' => 'markup', - '#value' => t('Please wait while we connect to your repository and determine any branches. NOTE: You must reload the page when the tasks complete to continue.'), + '#value' => $note, ); $form['not_ready'] = array( '#type' => 'value', diff --git a/devshop_projects/inc/devshop.css b/devshop_projects/inc/devshop.css index 793bcdf69..ac20f2cd2 100644 --- a/devshop_projects/inc/devshop.css +++ b/devshop_projects/inc/devshop.css @@ -34,4 +34,9 @@ fieldset.project-environments { textarea#rsa { display: none; +} + +p.wait { + text-align: center; + padding: 30px 0px 0px; } \ No newline at end of file From 70b5d4db2d543054df2c4b80c640dd525138d2c9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Feb 2013 23:55:57 -0500 Subject: [PATCH 0408/3476] Renamed steps --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 0a6396bf1..45b382322 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -89,8 +89,8 @@ function devshop_projects_create_wizard_info(){ 'order' => array( 'git_url' => t('Step 1: Source'), 'settings' => t('Step 2: Settings'), - 'environments' => t('Step 3: Platforms'), - 'sites' => t('Step 4: Sites'), + 'environments' => t('Step 3: Environments'), + 'sites' => t('Step 4: Install Profile'), ), 'forms' => array( 'git_url' => array( From 1f0ad6d0587b6c2d6fd1bc6da9b0a9b891d3c031 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:10:49 -0500 Subject: [PATCH 0409/3476] cleaner environments wizard page. --- devshop_projects/inc/create-wizard.inc | 11 +++++++---- devshop_projects/inc/devshop.css | 11 +++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 45b382322..30fbc43fc 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -441,13 +441,16 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $form['default_platforms'] = array( '#type' => 'fieldset', - '#title' => t('Default Platform Environments'), + '#title' => t('Project Environments'), + '#description' => t('Choose the environments you would like to create for this project. You will be able to create more or change the branch later.'), '#theme' => 'devshop_project_create_platforms_table', '#tree' => TRUE, ); foreach ($default_platforms as $env) { $form['default_platforms'][$env] = array( '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', ); $form['default_platforms'][$env]['enabled'] = array( '#type' => 'checkbox', @@ -457,15 +460,15 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $form['default_platforms'][$env]['branch'] = array( '#type' => 'select', '#default_value' => isset($project->default_platforms[$env])? $project->default_platforms[$env]: NULL, - '#options' => array(t('Choose a branch...')) + $branch_options + '#options' => array(t('Choose a branch...')) + $branch_options, ); } $form['branch_platforms'] = array( '#type' => 'checkboxes', '#multiple' => 'true', - '#title' => t('Branch Platform Environments'), - '#description' => t('Check the branches you wish to create platforms for.'), + '#title' => t('Branch Environments'), + '#description' => t('Create new environments based on the project\'s git branches.'), '#default_value' => $project->branch_platforms, '#options' => $branch_options ); diff --git a/devshop_projects/inc/devshop.css b/devshop_projects/inc/devshop.css index ac20f2cd2..bfa5ebd0e 100644 --- a/devshop_projects/inc/devshop.css +++ b/devshop_projects/inc/devshop.css @@ -1,6 +1,13 @@ -#edit-default-platforms-dev-enabled-wrapper, #edit-default-platforms-dev-branch-wrapper { +#devshop-project-create-step-environments .platform { + clear: both; + border-bottom: 1px solid #e8e8e8; +} +#devshop-project-create-step-environments .platform .form-item { float: left; - width: 50%; + width: 46%; + clear: none; + height: 2.0em; + border-bottom: none; } body.aegir #page div.node div.content fieldset.project-environments div.form-item { padding: 5px; From 82cc06cb45cde16d77745aaca983430ca4bbafb6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:12:43 -0500 Subject: [PATCH 0410/3476] removing no_next, because it can get stuck. --- devshop_projects/inc/create-wizard.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 30fbc43fc..8e840e1b9 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -427,8 +427,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // Add JS that reloads the page when tasks finish. $path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; drupal_add_js($path); - - $project->no_next = TRUE; return; } From ea3a32c087c20a84a4b976bb2804fd1ef6e58cc2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:15:01 -0500 Subject: [PATCH 0411/3476] better note about page reloading. --- devshop_projects/inc/create-wizard.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 8e840e1b9..00885061b 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -691,9 +691,12 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Provide something for people while they wait. if (!$completed){ $project->no_finish = TRUE; + $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; + $note .= '

' . t('NOTE: It is a known bug that this page doesn\'t automatically reload. Please reload the page manually once the task is complete.') . '

'; + $form['help'] = array( '#type' => 'markup', - '#value' => t('Please wait while we download and verify your drupal code. NOTE: You will have to manually reload the page after the tasks complete to continue.'), + '#value' => $note, ); return $form; } From b21ac78fd1386f5c020bfa1bc13b2849ede3f7f2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:20:36 -0500 Subject: [PATCH 0412/3476] figured out "stay on step"! --- devshop_projects/devshop_projects.module | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index d2c8db78e..8aac24a65 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -58,7 +58,7 @@ function devshop_projects_menu() { 'type' => MENU_LOCAL_TASK, 'title' => t('Start a new Project'), 'title callback' => 'check_plain', - 'page callback' => 'devshop_projects_create_wizard', + 'page callback' => 'devshop_projects_add', 'page arguments' => array(3), 'access callback' => 'node_access', 'access arguments' => array('create', 'project'), @@ -84,6 +84,19 @@ function devshop_projects_menu() { return ($items); } +/** + * Page Callback for hosting/projects/add + */ +function devshop_projects_add($step = NULL){ + if ($step == NULL){ + // Check to see if this project is still in the wizard + ctools_include('object-cache'); + $project_wizard_cache = ctools_object_cache_get('project', NULL); + drupal_goto('hosting/projects/add/' . $project_wizard_cache->step); + } else { + return devshop_projects_create_wizard($step); + } +} /** * Replacement for hosting_task_confirm_form() * From 72d2498e516da28ab1363d553aef3d9273e890ca Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:23:41 -0500 Subject: [PATCH 0413/3476] Better notification of the reload problem --- devshop_projects/inc/create-wizard.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 00885061b..551a8f633 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -703,9 +703,12 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If no available profiles: elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; + $note = '

' . t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; + $note .= '

' . t('NOTE: It is a known bug that this page doesn\'t automatically reload. Please reload the page manually once the task is complete.') . '

'; + $form['error'] = array( '#type' => 'markup', - '#value' => t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) + '#value' => $note, ); return $form; } else { From d7851684b9e85fe4d64736f15da63d3540b208aa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:26:19 -0500 Subject: [PATCH 0414/3476] making finish button more prominent --- devshop_projects/inc/devshop.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/inc/devshop.css b/devshop_projects/inc/devshop.css index bfa5ebd0e..e6ea9b96f 100644 --- a/devshop_projects/inc/devshop.css +++ b/devshop_projects/inc/devshop.css @@ -46,4 +46,10 @@ textarea#rsa { p.wait { text-align: center; padding: 30px 0px 0px; +} + +#edit-return { + float: right; + font-size: 3.0em; + height: 1.0em; } \ No newline at end of file From 88bf55ee561401b4354aacf67e05f115affc5fe2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:30:01 -0500 Subject: [PATCH 0415/3476] adding a friendly message --- devshop_projects/inc/create-wizard.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 551a8f633..507c1c2d0 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -160,6 +160,9 @@ function devshop_projects_create_wizard_finish(&$form_state) { // Removing last step session variable. unset($_SESSION['last_step']); + + // Friendly message + drupal_set_message(t('Your project has been created. Once installed, your sites will be available.')); } From e2dd13fa6b9d2c270770e402404244723e343f8e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:30:27 -0500 Subject: [PATCH 0416/3476] adding "Create new environment" link near environments list --- devshop_projects/inc/ui.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 3e39ab644..67839a7f3 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -187,9 +187,11 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'class' => 'project-environments', ), ); + $link = l(t('Create New Environment'), 'node/81/project_devshop-create'); $node->content['sites']['table'] = array( '#type' => 'item', '#value' => $table, + '#suffix' => $link, ); } From 4bf3e0219cb3a2cfcd58f67da3c003e4fc6de7dc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:31:53 -0500 Subject: [PATCH 0417/3476] hiding create environment task --- devshop_projects/inc/tasks.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 462d0cc2c..331f6f674 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -21,6 +21,7 @@ function devshop_projects_hosting_tasks() { 'description' => t('Creates a new site & platform within this project.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, + 'hidden' => TRUE, ); $tasks['project']['devshop-commit'] = array( 'title' => t('Commit Features'), From 16d10af675d8431a235a9722f330b5be0cc964bb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:38:36 -0500 Subject: [PATCH 0418/3476] adding another friendly message --- devshop_projects/inc/tasks.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 331f6f674..b9b840872 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -149,6 +149,9 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $values = $form_state['values']; $form_state['redirect'] = 'node/' . $values['nid']; modalframe_close_dialog(); + + // Friendly message + drupal_set_message(t('Your environment is being created.')); } From b61db8c844f6f4f98db8bc36657b0b9c8b6f4f30 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:38:52 -0500 Subject: [PATCH 0419/3476] fixing first step bug! --- devshop_projects/devshop_projects.module | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 8aac24a65..00a9ab9d9 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -92,10 +92,11 @@ function devshop_projects_add($step = NULL){ // Check to see if this project is still in the wizard ctools_include('object-cache'); $project_wizard_cache = ctools_object_cache_get('project', NULL); - drupal_goto('hosting/projects/add/' . $project_wizard_cache->step); - } else { - return devshop_projects_create_wizard($step); + if (!empty($project_wizard_cache->step)) { + drupal_goto('hosting/projects/add/' . $project_wizard_cache->step); + } } + return devshop_projects_create_wizard($step); } /** * Replacement for hosting_task_confirm_form() From 73c2e56687323f56b24a16f8c06ef2d175256db2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:41:47 -0500 Subject: [PATCH 0420/3476] better wording --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 67839a7f3..0dd3177ff 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -65,7 +65,7 @@ function devshop_projects_projects_page() { $button = l(t('Create your first Project'), 'hosting/projects/add'); $output = << -
You haven't created a project yet!
+
You have no projects.
$button
HTML; From 4a8f6699927455a9d99bf24f179e43b4de45049e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:51:07 -0500 Subject: [PATCH 0421/3476] better wording --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 507c1c2d0..183ffe73b 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -240,7 +240,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { // Display helpful tips for connecting. $pubkey_path = variable_get('devshop_public_key_path', '/var/aegir/.ssh/id_rsa.pub'); if (!file_exists($pubkey_path)){ - $output = t('No file was found at %path! To clone a Git repository to this server you need an SSH Key. See !link for a nice guide to generating SSH keys.', array('%path' => $pubkey_path, '!link' => l('github:help', 'https://help.github.com/articles/generating-ssh-keys', array('target' => '_blank')))); + $output = t('No file was found at %path! To clone a Git repository to this server you probably need a public SSH Key. See !link for a nice guide to generating SSH keys.', array('%path' => $pubkey_path, '!link' => l('github:help', 'https://help.github.com/articles/generating-ssh-keys', array('target' => '_blank')))); } else { $pubkey = file_get_contents('/var/aegir/.ssh/id_rsa.pub'); From dc4a6ae91b521b73aeb261a91f6a5118a092c45e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 00:56:52 -0500 Subject: [PATCH 0422/3476] matching UI for servers --- devshop_hosting.module | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 10f4a12f8..eca5cc371 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -22,9 +22,17 @@ function devshop_hosting_menu(){ */ function devshop_hosting_menu_alter(&$items){ $items['user/password']['type'] = MENU_CALLBACK; - unset($items['hosting/sites']); unset($items['hosting/platforms']); + + $items['hosting/servers/add'] = $items['node/add/server']; + $items['hosting/servers/add']['title'] = t('Add new Server'); + $items['hosting/servers/add']['type'] = MENU_LOCAL_TASK; + $items['hosting/servers/list'] = array( + 'title' => t('All Servers'), + 'weight' => -1, + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); } /** From bf0b0c7a7b5a23b8f5c235dbe0aaa0054e70ca7e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 13:22:06 -0500 Subject: [PATCH 0423/3476] better public key detection and handling in wizard. --- devshop_projects/inc/create-wizard.inc | 29 ++++++++++++++++++++------ devshop_projects/inc/devshop.css | 14 ++++++++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 183ffe73b..81f530508 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -238,15 +238,32 @@ function devshop_project_create_step_git(&$form, &$form_state) { } // Display helpful tips for connecting. + $pubkey = variable_get('devshop_public_key', ''); + $ssh_path = variable_get('devshop_ssh_path', '/var/aegir/.ssh'); $pubkey_path = variable_get('devshop_public_key_path', '/var/aegir/.ssh/id_rsa.pub'); - if (!file_exists($pubkey_path)){ - $output = t('No file was found at %path! To clone a Git repository to this server you probably need a public SSH Key. See !link for a nice guide to generating SSH keys.', array('%path' => $pubkey_path, '!link' => l('github:help', 'https://help.github.com/articles/generating-ssh-keys', array('target' => '_blank')))); + + // If we don't yet have the server's public key saved as a variable... + if (empty($pubkey)){ + // Try to read it from the default location, sometimes www-data can read the public key + if (is_readable($ssh_path)){ + $pubkey = file_get_contents($ssh_path); + variable_set('devshop_public_key', $pubkey); + } + // Sometimes www-data can't read /var/aegir/.ssh/id_rsa.pub, so file_exists + // fails. Thats why I'm checking for the parent folder. + elseif (!file_exists('/var/aegir/.ssh')) { + $output .= t("It appears you do not have SSH keys setup. Please ensure that %path exists, or see !link for a nice guide to generating SSH keys.", array('%path' => $pubkey_path, '!link' => l('github:help', 'https://help.github.com/articles/generating-ssh-keys', array('target' => '_blank')))); + + } else { + $output = t("We were unable to read your public SSH key. On your server, run the following command and then reload this page.."); + $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; + + $output .= "
"; + } } else { - $pubkey = file_get_contents('/var/aegir/.ssh/id_rsa.pub'); - // @TODO: Make this Translatable. $output = <<If you haven't granted this server access to your Git repositories, you should do so now using our public SSH key. +
If you haven't granted this server access to your Git repository, you should do so now using our public SSH key.
HTML; } @@ -255,7 +272,7 @@ HTML; $form['connect'] = array( '#type' => 'item', '#title' => t('Repository Access'), - '#value' => $output, + '#description' => $output, ); } diff --git a/devshop_projects/inc/devshop.css b/devshop_projects/inc/devshop.css index e6ea9b96f..d49f4e944 100644 --- a/devshop_projects/inc/devshop.css +++ b/devshop_projects/inc/devshop.css @@ -48,8 +48,16 @@ p.wait { padding: 30px 0px 0px; } -#edit-return { +.form-submit { + margin-top: 10px; +} +#edit-return, #edit-next { float: right; - font-size: 3.0em; - height: 1.0em; + font-size: 2.0em; + height: 2.0em; +} + +.command input { + font-family: Courier New, monospace; + width: 100%; } \ No newline at end of file From a0a34ddc7ccd9e0784a75eeb8897d41696a18817 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 13:29:31 -0500 Subject: [PATCH 0424/3476] making sure we have at least an array) --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 81f530508..f31b14a7d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -455,7 +455,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; drupal_add_js($path); - $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); + $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); $form['default_platforms'] = array( '#type' => 'fieldset', From fca81524edaf2512e2e1a0590cbcc3d10b863927 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 18:01:36 -0500 Subject: [PATCH 0425/3476] fixing page argument --- devshop_hosting.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_hosting.module b/devshop_hosting.module index eca5cc371..abd177fbf 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -28,6 +28,8 @@ function devshop_hosting_menu_alter(&$items){ $items['hosting/servers/add'] = $items['node/add/server']; $items['hosting/servers/add']['title'] = t('Add new Server'); $items['hosting/servers/add']['type'] = MENU_LOCAL_TASK; + $items['hosting/servers/add']['page arguments'] = array('server'); + $items['hosting/servers/list'] = array( 'title' => t('All Servers'), 'weight' => -1, From 6e4eddc21387ffd47faea03e27c522a40fe11407 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 18:07:47 -0500 Subject: [PATCH 0426/3476] adding icon --- icon.png | Bin 0 -> 6796 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icon.png diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4168d9399910ed1a5db9c02bc3ac44465c0b7d76 GIT binary patch literal 6796 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 u=KoCj)T>7~ed*|hve}m7 Date: Tue, 5 Feb 2013 18:10:54 -0500 Subject: [PATCH 0427/3476] moving drupal_add_css --- devshop_hosting.module | 2 ++ devshop_projects/inc/devshop.css | 8 ++++++++ devshop_projects/inc/theme.inc | 1 - devshop_projects/inc/ui.inc | 2 -- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index abd177fbf..e0d84576c 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -2,6 +2,8 @@ /** * @file devshop_hosting.module */ +drupal_add_css(drupal_get_path('module', 'devshop_hosting') . '/devshop.css'); + /** * Implements hook_menu() diff --git a/devshop_projects/inc/devshop.css b/devshop_projects/inc/devshop.css index d49f4e944..6e9b16df0 100644 --- a/devshop_projects/inc/devshop.css +++ b/devshop_projects/inc/devshop.css @@ -1,3 +1,11 @@ +div#header div.logo a { + background: none; + text-indent: -999px; + overflow: hidden; + height: 72px; + width: 72px; +} + #devshop-project-create-step-environments .platform { clear: both; border-bottom: 1px solid #e8e8e8; diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index f098310eb..793e9aa1e 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -16,7 +16,6 @@ function devshop_projects_theme(){ * Theme function for environments settings */ function theme_devshop_projects_settings_form($form){ - drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); $rows = array(); $header = array(); $header[] = t('Environment'); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 0dd3177ff..da75ddd42 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -11,7 +11,6 @@ * a table. */ function devshop_projects_projects_page() { - drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); $header = array( 'Name', @@ -211,7 +210,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { drupal_add_js($settings, 'setting'); drupal_add_js(drupal_get_path('module','hosting_task') . '/hosting_task.js'); - drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); // MAIN DISPLAY $node->content['devshop'] = array( From 61da0f8147d1d455f11bdf935ecd65e2c8411365 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 18:11:46 -0500 Subject: [PATCH 0428/3476] moving devshop.css --- devshop_projects/inc/devshop.css => devshop.css | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename devshop_projects/inc/devshop.css => devshop.css (100%) diff --git a/devshop_projects/inc/devshop.css b/devshop.css similarity index 100% rename from devshop_projects/inc/devshop.css rename to devshop.css From 8dc44a568982bec8d30da1f11ef400bbfbd541a8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 18:19:53 -0500 Subject: [PATCH 0429/3476] adding logo override --- devshop_hosting.module | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index e0d84576c..7d1793804 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -2,8 +2,12 @@ /** * @file devshop_hosting.module */ -drupal_add_css(drupal_get_path('module', 'devshop_hosting') . '/devshop.css'); +function devshop_hosting_init(){ + drupal_add_css(drupal_get_path('module', 'devshop_hosting') . '/devshop.css'); + $path = drupal_get_path('module', 'devshop_hosting') . '/icon.png'; + drupal_set_html_head(""); +} /** * Implements hook_menu() From c33c9320a7ae90208e6c990e31712941ae9ebd35 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 18:19:58 -0500 Subject: [PATCH 0430/3476] adding logo override --- devshop.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop.css b/devshop.css index 6e9b16df0..15ad18fea 100644 --- a/devshop.css +++ b/devshop.css @@ -1,9 +1,9 @@ div#header div.logo a { - background: none; text-indent: -999px; overflow: hidden; - height: 72px; - width: 72px; + height: 72px !important; + width: 72px !important; + margin: 10px; } #devshop-project-create-step-environments .platform { From bc4c50c8577f88f0f6613768808a7814a10da6a2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 18:21:28 -0500 Subject: [PATCH 0431/3476] adding docblock --- devshop_hosting.module | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_hosting.module b/devshop_hosting.module index 7d1793804..0d2e0ccd7 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -3,6 +3,9 @@ * @file devshop_hosting.module */ + /** + * Implements hook_init() + */ function devshop_hosting_init(){ drupal_add_css(drupal_get_path('module', 'devshop_hosting') . '/devshop.css'); $path = drupal_get_path('module', 'devshop_hosting') . '/icon.png'; From 68e8e2a1ff7ac90c85dc03bf9827f367a8cd404c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 19:47:39 -0500 Subject: [PATCH 0432/3476] Adding built by blocks --- devshop.css | 18 ++++++++++++++++++ devshop_hosting.module | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/devshop.css b/devshop.css index 15ad18fea..02eab9c0c 100644 --- a/devshop.css +++ b/devshop.css @@ -68,4 +68,22 @@ p.wait { .command input { font-family: Courier New, monospace; width: 100%; +} +#footer { + height: 100px !important; + line-height: inherit !important; +} +#footer h2 { + border: none; + font-weight: normal; + text-transform: uppercase; +} +#footer .block { + float: left; + margin-right: 20px; + +} + +#block-system-0.block { + float: right; } \ No newline at end of file diff --git a/devshop_hosting.module b/devshop_hosting.module index 0d2e0ccd7..e23a44de9 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -75,4 +75,37 @@ function devshop_hosting_form_user_login_block_alter(&$form){ if (arg(0) == 'devshop'){ $form = array(); } +} + +/** + * Implements hook_block() + */ +function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { + switch ($op) { + case 'list': + $blocks['built'] = array( + 'info' => t('Built by THINKDROP'), + 'weight' => '10', + // Not worth caching. + 'cache' => BLOCK_NO_CACHE, + ); + $blocks['driven'] = array( + 'info' => t('Driven by Aegir'), + 'weight' => '10', + // Not worth caching. + 'cache' => BLOCK_NO_CACHE, + ); + return $blocks; + case 'view': + if ($delta == 'built'){ + $image_path = drupal_get_path('module', 'devshop_hosting') . '/logo-td.png'; + $block['subject'] = t('Built by'); // Don't display a title + $block['content'] = l(theme('image', $image_path), 'http://thinkdrop.net/', array('html' => TRUE)); + } elseif ($delta == 'driven') { + $image_path = drupal_get_path('module', 'devshop_hosting') . '/logo-aegir.png'; + $block['subject'] = t('Powered by'); // Don't display a title + $block['content'] = l(theme('image', $image_path), 'http://aegirproject.org/', array('html' => TRUE)); + } + return $block; + } } \ No newline at end of file From fd21ba2e92aba9665ba5553c3d261bbd63a8792b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 22:08:14 -0500 Subject: [PATCH 0433/3476] adding default block regions --- devshop_hosting.module | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index e23a44de9..49437c908 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -86,14 +86,16 @@ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { $blocks['built'] = array( 'info' => t('Built by THINKDROP'), 'weight' => '10', - // Not worth caching. 'cache' => BLOCK_NO_CACHE, + 'status' => 1, + 'region' => 'footer', ); $blocks['driven'] = array( 'info' => t('Driven by Aegir'), 'weight' => '10', - // Not worth caching. 'cache' => BLOCK_NO_CACHE, + 'status' => 1, + 'region' => 'footer', ); return $blocks; case 'view': From b0cbcd002cd852a8c0b6ec6b989030787f297d9c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 22:33:43 -0500 Subject: [PATCH 0434/3476] better module description --- devshop_hosting.info | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_hosting.info b/devshop_hosting.info index 787466f2e..b4ee72a1b 100644 --- a/devshop_hosting.info +++ b/devshop_hosting.info @@ -1,5 +1,5 @@ -name = DevShop -description = Drupal Development Environment Manager +name = DevShop Hosting +description = Provides the streamlined DevShop user interface and experience. NOTE: This module alters some of of the UI of Aegir Hostmaster. core = 6.x package = DevShop From f58d68d49dff89b097b9b411a7e8cea43481a841 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 22:34:02 -0500 Subject: [PATCH 0435/3476] better language --- devshop_hosting.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 49437c908..6c40d9737 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -84,7 +84,7 @@ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { switch ($op) { case 'list': $blocks['built'] = array( - 'info' => t('Built by THINKDROP'), + 'info' => t('Created by THINKDROP'), 'weight' => '10', 'cache' => BLOCK_NO_CACHE, 'status' => 1, @@ -101,7 +101,7 @@ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { case 'view': if ($delta == 'built'){ $image_path = drupal_get_path('module', 'devshop_hosting') . '/logo-td.png'; - $block['subject'] = t('Built by'); // Don't display a title + $block['subject'] = t('Created by'); // Don't display a title $block['content'] = l(theme('image', $image_path), 'http://thinkdrop.net/', array('html' => TRUE)); } elseif ($delta == 'driven') { $image_path = drupal_get_path('module', 'devshop_hosting') . '/logo-aegir.png'; From e5c8860177a6089c4bd719a0d94f2bb7f18837f2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Feb 2013 22:34:12 -0500 Subject: [PATCH 0436/3476] fixing bad create environment link --- devshop_projects/inc/ui.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index da75ddd42..f0b76acfe 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -186,7 +186,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'class' => 'project-environments', ), ); - $link = l(t('Create New Environment'), 'node/81/project_devshop-create'); + global $user; + $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('query' => array('token' => drupal_get_token($user->uid)))); $node->content['sites']['table'] = array( '#type' => 'item', '#value' => $table, From 39ee5e0d705386c2f26cf6f266e5906fa4d907e3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 6 Feb 2013 16:22:15 -0500 Subject: [PATCH 0437/3476] adding helpful title to link --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f31b14a7d..ec2175f20 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -261,9 +261,9 @@ function devshop_project_create_step_git(&$form, &$form_state) { $output .= "
"; } } else { - // @TODO: Make this Translatable. + // @TODO: Make this Translatable $output = <<If you haven't granted this server access to your Git repository, you should do so now using our public SSH key. +
If you haven't granted this server access to your Git repository, you should do so now using our public SSH key.
HTML; } From eda50a0fbb69fa864ccfa383c70210054cd453e9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 6 Feb 2013 16:46:48 -0500 Subject: [PATCH 0438/3476] fixing some queries... Errors are showing when saving a project settings page, but the query runs fine, and the data is altered! --- devshop_pull/devshop_pull.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 989616269..26f7e8c7c 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -171,10 +171,10 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { case 'update': if ($node->type == 'project') { - db_query('UPDATE {hosting_devshop_pull_projects} SET project_nid = %d, pull_method = %d', $node->nid, $node->pull_method); + db_query('UPDATE {hosting_devshop_pull_projects} SET pull_method = %d WHERE project_nid = %d', $node->pull_method, $node->nid); } elseif ($node->type == 'platform') { - db_query('UPDATE {hosting_devshop_pull_platforms} SET platform_nid = %d, pull_enabled = %d, pull_reset = %d', $node->nid, $node->pull_enabled, $node->pull_reset); + db_query('UPDATE {hosting_devshop_pull_platforms} SET pull_enabled = %d, pull_reset = %d WHERE platform_nid = %d,', $node->pull_enabled, $node->pull_reset, $node->nid); } break; } From 3fb1b85cb3fa0005cf22ade55e88a678000ff58d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 6 Feb 2013 16:53:56 -0500 Subject: [PATCH 0439/3476] adding login link to the environments list --- devshop.css | 5 +++++ devshop_projects/inc/ui.inc | 20 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/devshop.css b/devshop.css index 02eab9c0c..0575f4946 100644 --- a/devshop.css +++ b/devshop.css @@ -86,4 +86,9 @@ p.wait { #block-system-0.block { float: right; +} + +body.node-type-project a.hosting-goto-site-link { + background: none; + padding: inherit; } \ No newline at end of file diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index f0b76acfe..f60a54570 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -153,8 +153,9 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $row = array(); if ($site->site_status != -2) { - $url = "http://$site->title"; - $row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); + + //$row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); + $row[] = devshop_hosting_site_goto_link($site); $row[] = l('Site Dashboard', "node/$site->nid"); } else { @@ -270,3 +271,18 @@ function devshop_projects_nodeapi_site_view(&$node, $a3, $a4) { function devshop_projects_nodeapi_platform_view(&$node, $a3, $a4) { devshop_projects_nodeapi_site_view($node, $a3, $a4); } + +/** + * Our own version of _hosting_site_goto_link() + */ +function devshop_hosting_site_goto_link($node) { + $cache = cache_get("hosting:site:" . $node->nid . ":login_link"); + if (!is_null($cache) && (time() < $cache->data['expire'])) { + $title = t("Log in: !url", array('!url' => $node->title)); + } + else { + $title = t("!url", array('!url' => $node->title)); + } + $options['attributes']['class'] = 'hosting-goto-site-link'; + return l($title, "node/" . $node->nid . "/goto_site", $options); +} \ No newline at end of file From 816cd77216284f2ab945ba258a5c2ac198aa056c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 6 Feb 2013 17:03:51 -0500 Subject: [PATCH 0440/3476] adding pull trigger url to settings form --- devshop_pull/devshop_pull.module | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 26f7e8c7c..5047f55f9 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -110,6 +110,16 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), ), ); + module_load_include('inc', 'devshop_pull'); + $form['git_settings']['pull_url'] = array( + '#type' => 'textfield', + '#title' => t('Pull Trigger URL'), + '#value' => _devshop_pull_callback_url($node), + '#description' => t('Configure your repo to hit this URL when it receives a commit.'), + '#attributes' => array( + 'onclick' => 'this.select()', + ), + ); } } From 932853c7554a49232a55c5a611cb8a9da61d0f2b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 12 Feb 2013 16:55:00 -0500 Subject: [PATCH 0441/3476] adding target=blank for site link --- devshop_projects/inc/ui.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index f60a54570..b93254b35 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -283,6 +283,7 @@ function devshop_hosting_site_goto_link($node) { else { $title = t("!url", array('!url' => $node->title)); } + $options['attributes']['target'] = '_blank'; $options['attributes']['class'] = 'hosting-goto-site-link'; return l($title, "node/" . $node->nid . "/goto_site", $options); } \ No newline at end of file From c0bd55c309f3d614c58f778d2985586903a151c2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 15 Feb 2013 11:30:52 -0500 Subject: [PATCH 0442/3476] fixing first step bug --- devshop_projects/inc/create-wizard.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index ec2175f20..108e4a473 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -20,7 +20,9 @@ function devshop_projects_create_wizard($step = NULL){ // Setup Step. if ($step == NULL){ - $step = current(array_keys($form_info['order'])); + drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); + + //$step = current(array_keys($form_info['order'])); } // Create default project object @@ -56,7 +58,7 @@ function devshop_projects_create_wizard($step = NULL){ drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); // If not on the first step, go to it. if ($step != current(array_keys($form_info['order']))){ - drupal_goto('hosting/projects/add'); + drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); } } From 876c8d38a11d6592c49fd0cd1b73e8e43a7d6b42 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 15 Feb 2013 11:31:03 -0500 Subject: [PATCH 0443/3476] fixing first step bug --- devshop_projects/inc/create-wizard.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 108e4a473..c5736581e 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -21,8 +21,6 @@ function devshop_projects_create_wizard($step = NULL){ // Setup Step. if ($step == NULL){ drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); - - //$step = current(array_keys($form_info['order'])); } // Create default project object From 6b1f55827ef9590ec318a19a434b336aa583df4c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 15 Feb 2013 12:03:08 -0500 Subject: [PATCH 0444/3476] better handling of bad git urls --- devshop_projects/inc/create-wizard.inc | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index c5736581e..892cd9cc6 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -44,22 +44,6 @@ function devshop_projects_create_wizard($step = NULL){ ctools_object_cache_set('project', $form_state['cache name'], $project); } - // Check verification status - $project_node = node_load($project->project_nid); - if (!empty($project_node->nid)){ - $tasks = hosting_task_fetch_tasks($project_node->nid); - } - $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; - - // If project verification failed, we need to ask for a new git url. - if ($project->verify_task_status === HOSTING_TASK_ERROR && !empty($project_node->nid)){ - drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); - // If not on the first step, go to it. - if ($step != current(array_keys($form_info['order']))){ - drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); - } - } - // All forms can access $form_state['project']; $form_state['project'] = $project; @@ -312,9 +296,8 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project = &$form_state['project']; // If the project already exists, and git url has changed... - if ($project->project_nid && $project->git_url != $form_state['values']['git_url']) { + if ($project->project_nid) { // Change the git url and save the node. Verification SHOULD run again. - drupal_set_message(t('You have changed the git URL. Please wait while we verify your remote and branches.')); $node = node_load($project->project_nid); $node->git_url = $form_state['values']['git_url']; $node->branches = array(); From da056d782ecdbf82996577024fdfbfcb23c2c375 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 15 Feb 2013 12:11:55 -0500 Subject: [PATCH 0445/3476] Avoiding php notices --- devshop_projects/devshop_projects.drush.inc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index ad46a4ec4..eed6a63a0 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -222,7 +222,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } // When a project is verified, queue a verification for all platforms - if ($task->ref->type == 'project'){ + if ($task->ref->type == 'project' && isset($project->project_objects['platform'])){ $project = node_load($task->ref->nid); $platform_nids = array_keys($project->project_objects['platform']); foreach ($project->project_objects['platform'] as $nid => $name){ @@ -253,20 +253,23 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->ref->git_tags = $branches['tags']; // Save the project node now that we have branches and tags. + $task->ref->no_verify = TRUE; node_save($task->ref); $task->context_options['git_branches'] = $branches['branches']; $task->context_options['git_tags'] = $branches['tags']; // Load environment settings - foreach ($task->ref->project_objects['platform'] as $nid => $name){ - $platform = node_load($nid); - $task->context_options['settings'][$name] = array(); - - // @TODO: HOOK! - $task->context_options['settings'][$name]['git_branch'] = $platform->git_branch; - $task->context_options['settings'][$name]['pull_enabled'] = $platform->pull_enabled; - $task->context_options['settings'][$name]['pull_reset'] = $platform->pull_reset; + if (isset($task->ref->project_objects['platform'])) { + foreach ($task->ref->project_objects['platform'] as $nid => $name){ + $platform = node_load($nid); + $task->context_options['settings'][$name] = array(); + + // @TODO: HOOK! + $task->context_options['settings'][$name]['git_branch'] = $platform->git_branch; + $task->context_options['settings'][$name]['pull_enabled'] = $platform->pull_enabled; + $task->context_options['settings'][$name]['pull_reset'] = $platform->pull_reset; + } } } From 73884a0fe0d77b8bb56d2c3dd761a1080cc8f960 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 15 Feb 2013 12:13:56 -0500 Subject: [PATCH 0446/3476] putting verification status back in. --- devshop_projects/inc/create-wizard.inc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 892cd9cc6..b0c944e66 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -43,6 +43,22 @@ function devshop_projects_create_wizard($step = NULL){ $project->step = $step; ctools_object_cache_set('project', $form_state['cache name'], $project); } + + // Check verification status + $project_node = node_load($project->project_nid); + if (!empty($project_node->nid)){ + $tasks = hosting_task_fetch_tasks($project_node->nid); + } + $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; + + // If project verification failed, we need to ask for a new git url. + if ($project->verify_task_status === HOSTING_TASK_ERROR && !empty($project_node->nid)){ + drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); + // If not on the first step, go to it. + if ($step != current(array_keys($form_info['order']))){ + drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); + } + } // All forms can access $form_state['project']; $form_state['project'] = $project; From a3a0f67dedefcb70de9ae698652225db10cac858 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 17 Feb 2013 17:20:15 -0500 Subject: [PATCH 0447/3476] renaming to devshop_testing --- .../devshop_projects_testing.info | 5 ----- devshop_testing/devshop_testing.info | 5 +++++ .../devshop_testing.install | 10 +++++----- .../devshop_testing.module | 16 ++++++++-------- 4 files changed, 18 insertions(+), 18 deletions(-) delete mode 100644 devshop_projects_testing/devshop_projects_testing.info create mode 100644 devshop_testing/devshop_testing.info rename devshop_projects_testing/devshop_projects_testing.install => devshop_testing/devshop_testing.install (76%) rename devshop_projects_testing/devshop_projects_testing.module => devshop_testing/devshop_testing.module (89%) diff --git a/devshop_projects_testing/devshop_projects_testing.info b/devshop_projects_testing/devshop_projects_testing.info deleted file mode 100644 index eb55d4c15..000000000 --- a/devshop_projects_testing/devshop_projects_testing.info +++ /dev/null @@ -1,5 +0,0 @@ -name = DevShop Projects Testing -description = A DevShop Projects module that allows the user to incorporate simple tests within a project and have the test run automatically. -core = 6.x -package = DevShop -~ diff --git a/devshop_testing/devshop_testing.info b/devshop_testing/devshop_testing.info new file mode 100644 index 000000000..a7c06fa98 --- /dev/null +++ b/devshop_testing/devshop_testing.info @@ -0,0 +1,5 @@ +name = DevShop Testing +description = Allow simpletests to be run easily on your DevShop projects. +core = 6.x +package = DevShop +~ diff --git a/devshop_projects_testing/devshop_projects_testing.install b/devshop_testing/devshop_testing.install similarity index 76% rename from devshop_projects_testing/devshop_projects_testing.install rename to devshop_testing/devshop_testing.install index 961adc9c5..fc11cd5b6 100644 --- a/devshop_projects_testing/devshop_projects_testing.install +++ b/devshop_testing/devshop_testing.install @@ -7,7 +7,7 @@ /** * Implementation of hook_schema(). */ -function devshop_projects_testing_schema() { +function devshop_testing_schema() { $schema['hosting_devshop_project_testing'] = array( 'fields' => array( 'project_nid' => array( @@ -34,16 +34,16 @@ function devshop_projects_testing_schema() { /** * Implementation of hook_install(). */ -function devshop_projects_testing_install() { +function devshop_testing_install() { // Create tables. - drupal_install_schema('devshop_projects_testing'); + drupal_install_schema('devshop_testing'); } /** * Implementation of hook_uninstall(). */ -function devshop_projects_testing_uninstall() { +function devshop_testing_uninstall() { // Delete tables. - drupal_uninstall_schema('devshop_projects_testing'); + drupal_uninstall_schema('devshop_testing'); } diff --git a/devshop_projects_testing/devshop_projects_testing.module b/devshop_testing/devshop_testing.module similarity index 89% rename from devshop_projects_testing/devshop_projects_testing.module rename to devshop_testing/devshop_testing.module index b100b2bd0..4674b3ac8 100644 --- a/devshop_projects_testing/devshop_projects_testing.module +++ b/devshop_testing/devshop_testing.module @@ -9,7 +9,7 @@ /** * Implementation of hook_perm() */ -function devshop_projects_testing_perm() { +function devshop_testing_perm() { return array( 'run project tests', ); @@ -18,7 +18,7 @@ function devshop_projects_testing_perm() { /** * Implements hook_form_alter(). */ -function devshop_projects_testing_form_alter(&$form, &$form_state, $form_id) { +function devshop_testing_form_alter(&$form, &$form_state, $form_id) { if ($form_id == 'project_node_form') { $form['tests_to_run'] = array( @@ -35,7 +35,7 @@ function devshop_projects_testing_form_alter(&$form, &$form_state, $form_id) { /** * Implements hook_nodeapi() */ -function devshop_projects_testing_nodeapi(&$node, $op, $a3 = null) { +function devshop_testing_nodeapi(&$node, $op, $a3 = null) { if ($node->type != 'project' || $a3 != null) { return; @@ -44,7 +44,7 @@ function devshop_projects_testing_nodeapi(&$node, $op, $a3 = null) { switch ($op) { case 'load': $data = db_fetch_array(db_query("SELECT tests_to_run " . - "FROM {hosting_devshop_project_testing} ". + "FROM {hosting_devshop_testing} ". "WHERE project_nid = %d", $node->nid)); return $data; @@ -78,15 +78,15 @@ function devshop_projects_testing_nodeapi(&$node, $op, $a3 = null) { * Implementation of hook_hosting_tasks() * */ -function devshop_projects_testing_hosting_tasks() { +function devshop_testing_hosting_tasks() { $tasks = array(); - $tasks['site']['devshop-test'] = array( + $tasks['project']['devshop-test'] = array( 'title' => t('Run Tests'), 'description' => t('Run a group of previousely defined SimpleTest tests.'), 'dialog' => TRUE, 'task_permitted' => TRUE, - 'access callback' => 'devshop_projects_testing_access', + 'access callback' => 'devshop_testing_access', ); return $tasks; @@ -99,7 +99,7 @@ function devshop_projects_testing_hosting_tasks() { * To qualify, it much be part of a devshop project group and must * be either the 'test' site or one of the branch sites. */ -function devshop_projects_testing_access($node, $type) { +function devshop_testing_access($node, $type) { if ($node->type != 'site' || !$node->project_nid) { return FALSE; From 08eccb034fe886bdf8ac60623f91be90c1304488 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 17 Feb 2013 20:55:51 -0500 Subject: [PATCH 0448/3476] fixing permissions setup, better run tests form --- devshop_testing/devshop_testing.module | 56 +++++--------------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/devshop_testing/devshop_testing.module b/devshop_testing/devshop_testing.module index 4674b3ac8..dddecfc02 100644 --- a/devshop_testing/devshop_testing.module +++ b/devshop_testing/devshop_testing.module @@ -11,7 +11,7 @@ */ function devshop_testing_perm() { return array( - 'run project tests', + 'create devshop-test task', ); } @@ -83,70 +83,32 @@ function devshop_testing_hosting_tasks() { $tasks['project']['devshop-test'] = array( 'title' => t('Run Tests'), - 'description' => t('Run a group of previousely defined SimpleTest tests.'), + 'description' => t('Run a group of SimpleTests.'), 'dialog' => TRUE, 'task_permitted' => TRUE, - 'access callback' => 'devshop_testing_access', + 'access callback' => 'devshop_hosting_task_menu_access', ); return $tasks; } -/* - * Task access callback hook - * - * Returne TRUE is this is a site node and qualifies to run tests. - * To qualify, it much be part of a devshop project group and must - * be either the 'test' site or one of the branch sites. - */ -function devshop_testing_access($node, $type) { - - if ($node->type != 'site' || !$node->project_nid) { - return FALSE; - } - - $query = db_query("SELECT * FROM {hosting_devshop_project_object} " . - "WHERE object_nid = %d", $node->nid); - - if (!($obj = db_fetch_object($query))) { - // This should not happen, but let's check for it anyway - return FALSE; - } - - if ($obj->environment == 'live' || $obj->environment == 'dev') { - return FALSE; - } - - // Everything else is OK - return TRUE; -} - /** * Implementation of hook_hosting_task_TASK_TYPE_form(). - * */ function hosting_task_devshop_test_form($node) { - if ($node->project_nid) { - $project_node = node_load($node->project_nid); - $tests = $project_node->tests_to_run; - } - else { - $tests = ""; - } + $tests = $node->tests_to_run; + + $form = array(); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to run tests on.'), 'environment', 'Environment', 'radios'); + $example_site = key($form['environment']['#options']) . "." . $node->base_url; $form['tests_to_run'] = array( '#type' => 'textarea', '#title' => t('Tests To Run'), '#default_value' => $tests, '#rows' => 6, - '#description' => t('Enter the names of the simpletests to run when this project receives a commit, one per line.'), - ); - - $form['sync'] = array( - '#type' => 'checkbox', - '#title' => t("Sync from Live before testing?"), - '#default_value' => FALSE, + '#description' => t('Enter the names of the simpletests to run, one per line. See the !link for examples, or call drush @@site test-run for a list of available tests.', array('!link' => l(t('Drupal API'), 'http://api.drupal.org/api/drupal/modules%21simpletest%21drupal_web_test_case.php/class/hierarchy/DrupalWebTestCase/7'), '@site' => $example_site)), ); return $form; From 8ef8c2cbb4fb7fc693be308bc2cd0b9db71153f6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Feb 2013 10:05:49 -0500 Subject: [PATCH 0449/3476] cleaner info files --- devshop_live/devshop_live.info | 1 - devshop_log/devshop_log.info | 1 - devshop_testing/devshop_testing.info | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/devshop_live/devshop_live.info b/devshop_live/devshop_live.info index ef68e0e8c..20e55ef61 100644 --- a/devshop_live/devshop_live.info +++ b/devshop_live/devshop_live.info @@ -2,6 +2,5 @@ name = DevShop Live description = A DevShop module to Going Live. core = 6.x package = DevShop - dependencies[] = devshop_projects dependencies[] = hosting_alias diff --git a/devshop_log/devshop_log.info b/devshop_log/devshop_log.info index b60a6efdf..974e6fd96 100644 --- a/devshop_log/devshop_log.info +++ b/devshop_log/devshop_log.info @@ -2,5 +2,4 @@ name = DevShop Log description = A DevShop module to display the git commit log for each platform. core = 6.x package = DevShop - dependencies[] = devshop_projects \ No newline at end of file diff --git a/devshop_testing/devshop_testing.info b/devshop_testing/devshop_testing.info index a7c06fa98..350dc758f 100644 --- a/devshop_testing/devshop_testing.info +++ b/devshop_testing/devshop_testing.info @@ -2,4 +2,4 @@ name = DevShop Testing description = Allow simpletests to be run easily on your DevShop projects. core = 6.x package = DevShop -~ +dependencies[] = devshop_projects \ No newline at end of file From 9d71506ae05a1b6b41f6cfdf668fd075c1f973e4 Mon Sep 17 00:00:00 2001 From: ergonlogic Date: Mon, 18 Feb 2013 10:09:51 -0500 Subject: [PATCH 0450/3476] Issue #1914370 by ergonlogic: Project names with capitals cause alias errors --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index b0c944e66..385cb6ddf 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -351,7 +351,7 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { // Setup project url. $base_url = $_SERVER['SERVER_NAME']; $server = variable_get('devshop_project_default_base_url', $base_url); - $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => $project_name, '@server' => $server)); + $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => strtolower($project_name), '@server' => $server)); } /********** From 00c9ee4350b9b3c656c29e926f0c704fc5e808dd Mon Sep 17 00:00:00 2001 From: ergonlogic Date: Mon, 18 Feb 2013 10:12:49 -0500 Subject: [PATCH 0451/3476] Issue #1914696 by ergonlogic: Allow hosting-import to import project contexts --- devshop_projects/devshop_projects.drush.inc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index eed6a63a0..9cde75e74 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -273,6 +273,26 @@ function devshop_projects_hosting_project_context_options(&$task) { } } +/** + * Implements hook_drush_context_import() + * + * This allows project nodes to be created from contexts (aliases) + */ +function devshop_projects_drush_context_import($context, &$node) { + if ($context->type == 'project') { + $node->title = $context->project_name; + $node->type = $context->type; + $node->code_path = $context->code_path; + $node->base_url = $context->base_url; + $node->install_profile = $context->install_profile; + + $node->git_url = $context->git_url; + $branches = getBranchesAndTags($context->git_url); + $node->git_branches = $branches['branches']; + $node->git_tags = $branches['tags']; + } +} + /** * Helpfer for getting branches and tags from a git URL */ From 382960f759c744a812dec65ded76e7038adde8f8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Feb 2013 12:18:11 -0500 Subject: [PATCH 0452/3476] putting back verify for project node. need to save new context data. --- devshop_projects/devshop_projects.drush.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 9cde75e74..77ac2376b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -253,7 +253,6 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->ref->git_tags = $branches['tags']; // Save the project node now that we have branches and tags. - $task->ref->no_verify = TRUE; node_save($task->ref); $task->context_options['git_branches'] = $branches['branches']; From 785f6b5f88946650f6d3ecf45b28cc19f74cb566 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Feb 2013 12:33:06 -0500 Subject: [PATCH 0453/3476] changing "content" to "data" --- devshop_projects/inc/tasks.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index b9b840872..55f2e315c 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -36,8 +36,8 @@ function devshop_projects_hosting_tasks() { 'dialog' => TRUE, ); $tasks['project']['devshop-sync'] = array( - 'title' => t('Sync Content'), - 'description' => t('Sync content from another site and (optionally) run update.php, clear cache, and revert features.'), + 'title' => t('Sync Data'), + 'description' => t('Sync database and files from another site and (optionally) run update.php, clear cache, and revert features.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); From cf07b9b2aa5e347ddab6918d98057e4c3a0c0a01 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 21 Feb 2013 10:57:09 -0500 Subject: [PATCH 0454/3476] forgot to add logos! --- logo-aegir.png | Bin 0 -> 2259 bytes logo-td.png | Bin 0 -> 3657 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 logo-aegir.png create mode 100644 logo-td.png diff --git a/logo-aegir.png b/logo-aegir.png new file mode 100644 index 0000000000000000000000000000000000000000..2aa6f5e984355d701d1a1085a8b035768f571495 GIT binary patch literal 2259 zcmV;^2rT!BP) zK~!ko?U`$gUR4!_-*?ZYQ#xEyS{hp#t3ZGX3KD@tQ-wsR#A*=aqKPDE2u9S%4`U1} zB$z73K(H~ym`34`CRPnKEfG*4F$Jp@QOX?~MlOX`rkzXYx*mVbI?e9aY3I^{MmfpJ zcfNhj-g}*OdDpwvk^YyDh}e4#ys3M8Hj{uOfRR$l*d+HR7PgxgJ5ExuJz%@9dErvZ zjQ4Gyp^&mI;2PTxO4>5n{fL+kTq@}TNhbpV^aB5ov|iFZk_Kig^i15~NMIvy#$*}= z7DvQA5iuGOKL&1!h+bd>7y@1geg!Ok!(9pZ81NjhXfl6}h}(dOh(6$cz!9-Lh3o@% z1H%#VDDbwK2wY|;XdCbeN!xSd)<@>qey^la+hdYWlC-3hvQ^Srpox)TNv8sn5d?0pPciI)N@pPfL0wZ~xgg`GU4RD(O?U&uT@PLkh5nkS8Qt zX?w1uXKa@`vYX(t{as1eUM%TrsbNXl)kxVGN|J%ql2#nnKm)oW;%+96%Yh3H1Y47h zMZ{GRF_7-lpJ!i05D^z07B{E_SZ4cdNqZ84t^`)>1KKTaQ9)gsr1L9mrm%X>;19rd;EsqmbCNkrfZKq7Rc7x`SE$`#Pej}`6^wbB0k+ciMoE2=`X${eX(4dC z?RzDiP~GcE1bC;U<0TyljHNr+Hm!T-_#8>ywucT?s~aO?cSP)pi1CQHBqE-Ph&2(h zAR=y$h@lLY(X_xob@l`Osnz~wJOi$|9*u}w4+dkN24tNe>36`<>BC1#`U-HS?JFcb z3|wY=Ab&5C^d3paNZKc8(kGperG&UfvnAu<)I-tgr&AldfL*Dbb-=qLq6Zj@2!QWL z#CZ{M%uFd~L=>PM=!}RmW1p?S{`ZpJCuux261K;Im9~!u?o5q!0wcCxm$XgNbLqc5 zlKz=VVF(ztJpv48S{{}(DrqEX=lHAwEFzZL-Ujq%<{?eiE@><90oxZ#S_5t#nX5C?$)NduAwB@Ngfko1D28*KN_s2YffJ0oH+&A%%dQ*%8Y5%&YjfRli= z5%Fre#Bjy1nzMnefrP3(`P?WlmYkOi&huvK1`8!^wmrYXKbjy(&^Pd!q+3cUcL48@ z^l?d_1x{<0O0$nO`{f!mNvX7j7!e zNz(DQ*GlRGo|p8zq|Ly6rIgjpy0#Yq$Jt&0yxsPx30KQAi70YlE1qN;R+m06ZJI(g_S!mv3d-tTmR=)EkNjF!5 zHs+>U25@ zzugCOCvmIJwBV@iO_FYtbhqvC$=sj~SY!Lz%qTtpsdjDVmis;cxAiQU-?_FQ+3&%K zuG@qNIM{ft-Ij zu+8>kl6nqWrUQ#2;wQjPU_85kyOVE@0izKy77?!kOLES25z&){V1GpHjEL3AW%mH9 zBBBHMO+*X<-^h92$9wSV zMbZ}FWnejQLl%^6l7?*01-_i1+16lc&h2b0oB!KvFOYOqX3p_EzivDBbEjqixG^He zfR})ifpfD0cnMg3ApXllV|GVGTvZi=cczv`fK@<8L~MwO1;3fk-ICVpi->PkYo1v9 zb!ll?3OF0M)^;0kiS4H(bu}6Q76KhWH_#msT{-=fT=YTPKW)~teSMnIfLchIDs5ZR z>!p;Ns_|!&SnV%tPWVg`WgHt+>Wb0jST_Ou4nY>w8xQg;T^ z^R{=CQg)V7dTsaSCgYM8084Ez0hZXl()Q9)$_tXNOih=pcIRinwlzdPtudaRnW)ws z&2xJFS>UYvBC}1>U#ptxSYSogO09Xl*xqIPp&UC((nVRKY_|PGM2zHfD{bGG>2{H% zCvESOZf)N`D}oylGx`Du%mYquS>uX`7|wLMWTr&sjbdHL0zb=4^i|+O;2L0CX2W$6 z(fMD&`jhzFooShf=x?rpd$SMr=6}LJNH4cb(ke-xl(bCJOOn<}`i1R1Z}?a6*^uw# h5S{t`>;JRp`!{Nhghf`lT#o<%002ovPDHLkV1gT2MI`_L literal 0 HcmV?d00001 diff --git a/logo-td.png b/logo-td.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1b00cd9e7b272640abad2c099f932f4b7dc561 GIT binary patch literal 3657 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 ZThXmEmll#q#oMC~kgOf8>MeYd{s(}T$;ki! literal 0 HcmV?d00001 From cac35acd884b23ac053dcc4cee7739cd09cc5f2e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 00:38:28 -0500 Subject: [PATCH 0455/3476] handle failure to connect --- devshop_projects/inc/create-wizard.inc | 28 +++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 385cb6ddf..3cf24cb8f 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -48,8 +48,12 @@ function devshop_projects_create_wizard($step = NULL){ $project_node = node_load($project->project_nid); if (!empty($project_node->nid)){ $tasks = hosting_task_fetch_tasks($project_node->nid); + } + if (isset($tasks['verify']['nid'])){ + } $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; + $project->verify_task_nid = $tasks['verify']['nid']; // If project verification failed, we need to ask for a new git url. if ($project->verify_task_status === HOSTING_TASK_ERROR && !empty($project_node->nid)){ @@ -443,11 +447,29 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#value' => TRUE, ); - // Add JS that reloads the page when tasks finish. - $path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; - drupal_add_js($path); + // @TODO: Add proper JS that reloads the page when tasks finish. + //$path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; + //drupal_add_js($path); return; } + // Handle failure + elseif ($project->verify_task_status == HOSTING_TASK_ERROR){ + $type = 'error'; + $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, $type)); + drupal_set_message($error, 'error'); + $note = '

'. t('Something Failed. Please check the error message, and retry verification.') . '

'; + + $form['note'] = array( + '#type' => 'markup', + '#value' => $note, + ); + + $form['not_ready'] = array( + '#type' => 'value', + '#value' => TRUE, + ); + return; + } // Add JS that reloads the page when tasks finish. From ef5038ba657f375afb7747639d9c81ecafa6111a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 00:50:41 -0500 Subject: [PATCH 0456/3476] adding git branch to form --- devshop_projects/inc/forms.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e6bb47c55..0232ea0e3 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -74,6 +74,10 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#type' => 'value', '#value' => $node->environment, ); + $form['git_branch'] = array( + '#type' => 'value', + '#value' => $node->git_branch, + ); } } From e69ea622978d8dd902ad571a608e70e9951e3239 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 10:43:51 -0500 Subject: [PATCH 0457/3476] fixing bad code preventing verification of platforms after project. --- devshop_projects/devshop_projects.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 77ac2376b..126dc0c1c 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -222,7 +222,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } // When a project is verified, queue a verification for all platforms - if ($task->ref->type == 'project' && isset($project->project_objects['platform'])){ + if ($task->ref->type == 'project' && isset($task->ref->project_objects['platform'])){ $project = node_load($task->ref->nid); $platform_nids = array_keys($project->project_objects['platform']); foreach ($project->project_objects['platform'] as $nid => $name){ From d0ce8399da451fa499fc6ebf10c5235cd0ff1df8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 10:56:48 -0500 Subject: [PATCH 0458/3476] adding verification of sites post verification of their platforms --- devshop_projects/devshop_projects.drush.inc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 126dc0c1c..ca84e7f74 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -185,6 +185,8 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // If this is a new platform created by a project, we will create a site here. if ($task->ref->type == 'platform') { + drush_log('[DEVSHOP] Platform Verification complete.', 'notice'); + // Get objects $nid = $task->ref->nid; $platform = node_load($nid); @@ -204,18 +206,20 @@ function devshop_projects_post_hosting_verify_task($task, $data) { return; } - // If the project has a site already, bail. + // If the project has a site already, trigger verification, then bail. $sites = array_flip($project->project_objects['site']); $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); if (!empty($sites)){ drush_log('[DEVSHOP] Platform already has a site.', 'notice'); + foreach($sites as $site_nid => $site){ + hosting_add_task($site_nid, 'verify'); + drush_log(t('[DEVSHOP] Queued verification for !site', array('!site' => $site->title)), 'notice'); + } return; } // live. Let's create a site based off of this platform. drush_log('[DEVSHOP] Platform verified. Creating your site.'); - - $site_node = devshop_projects_create_site($project, $platform, $platform->environment); drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); From c005a5b3afbb2f2210578bf77d56c46c8d80c693 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 11:02:09 -0500 Subject: [PATCH 0459/3476] fixing site update on platform update. --- devshop_projects/inc/nodes.inc | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index f88b0ccc6..d7a280df6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -58,16 +58,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Save to table db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, environment, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); - - //If is platform update all sites with the corret git_branch + + //If we are updating or inserting a platform, update all sites with the correct git_branch and environment if ($node->type == 'platform') { - $result = db_query("SELECT nid FROM {hosting_site} WHERE platform=%d", $node->nid); - - while ($object = db_fetch_object($result)) { - $site = node_load($object->nid); - $site->no_verify; - $site->git_branch = $node->git_branch; - node_save($site); + $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); + while ($site = db_fetch_object($result)) { + db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $site->nid); } } } From 9cbc7b7c359deb0699c24512477aa8e1fe0649f0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 11:15:39 -0500 Subject: [PATCH 0460/3476] fixed bad query --- devshop_testing/devshop_testing.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_testing/devshop_testing.module b/devshop_testing/devshop_testing.module index dddecfc02..b1fdccd72 100644 --- a/devshop_testing/devshop_testing.module +++ b/devshop_testing/devshop_testing.module @@ -44,7 +44,7 @@ function devshop_testing_nodeapi(&$node, $op, $a3 = null) { switch ($op) { case 'load': $data = db_fetch_array(db_query("SELECT tests_to_run " . - "FROM {hosting_devshop_testing} ". + "FROM {hosting_devshop_project_testing} ". "WHERE project_nid = %d", $node->nid)); return $data; From 2599fb1890ce4c084bd5538265cbda15f63627b2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 11:52:44 -0500 Subject: [PATCH 0461/3476] Stray Comma in SQL Query!!! --- devshop_pull/devshop_pull.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 5047f55f9..29540636c 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -184,7 +184,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { db_query('UPDATE {hosting_devshop_pull_projects} SET pull_method = %d WHERE project_nid = %d', $node->pull_method, $node->nid); } elseif ($node->type == 'platform') { - db_query('UPDATE {hosting_devshop_pull_platforms} SET pull_enabled = %d, pull_reset = %d WHERE platform_nid = %d,', $node->pull_enabled, $node->pull_reset, $node->nid); + db_query('UPDATE {hosting_devshop_pull_platforms} SET pull_enabled = %d, pull_reset = %d WHERE platform_nid = %d', $node->pull_enabled, $node->pull_reset, $node->nid); } break; } From 85810ce4c5676bbc55a50c030219bb942e054b79 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 15:16:00 -0500 Subject: [PATCH 0462/3476] Fixing lost git branches and tags on save. Also a @TODO about the proposed settings system. --- devshop_projects/inc/forms.inc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 0232ea0e3..3365c2627 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -127,6 +127,16 @@ function devshop_projects_form(&$node) { '#weight' => 2, ); + // Unchanging Values + $form['git_branches'] = array( + '#type' => 'value', + '#value' => $node->git_branches, + ); + $form['git_tags'] = array( + '#type' => 'value', + '#value' => $node->git_tags, + ); + // Extensible settings $form['settings'] = array( '#type' => 'fieldset', @@ -179,7 +189,10 @@ function devshop_projects_form(&$node) { */ function devshop_projects_submit_settings($form, &$form_state) { $node = node_load($form_state['values']['nid']); - + + // @TODO: Using the devshop_project_settings hook, determine if this + // property should be added to the platform or site node. + // Go through and save our platforms foreach ($node->project_objects['platform'] as $nid => $name){ $platform = node_load($nid); From ba0c75e4346b5f139c23846ff8c5ba3c93d17f1d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 15:17:09 -0500 Subject: [PATCH 0463/3476] removing old comment --- devshop_projects/inc/nodes.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index d7a280df6..91e997332 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -126,8 +126,9 @@ function devshop_projects_update($node) { if (!$node->no_verify) { hosting_add_task($node->nid, 'verify'); } - $data = array(); + + // Branches and tags get saved upon verify. $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; From 4bf94f73590c8bd29ba0fa4bc1ea5c2a60c6491b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 15:21:59 -0500 Subject: [PATCH 0464/3476] Adding project_nid to {hosting_devshop_pull_platforms} --- devshop_pull/devshop_pull.install | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index b469c6d98..61a82f060 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -35,6 +35,11 @@ function devshop_pull_schema() { ); $schema['hosting_devshop_pull_platforms'] = array( 'fields' => array( + 'project_nid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), 'platform_nid' => array( 'type' => 'int', 'not null' => TRUE, @@ -72,3 +77,12 @@ function devshop_pull_uninstall() { // Create tables. drupal_uninstall_schema('devshop_pull'); } + +/** + * Adds project_nid column to our table. + */ +function devshop_pull_update_6000(){ + $ret = array(); + db_add_field($ret, 'hosting_devshop_pull_platforms', 'project_nid', array('type' => 'int', 'not null' => TRUE, 'default' => 0)); + return $ret; +} \ No newline at end of file From 5df558f8718ef0a87185e98bc35dfd98b7645cb2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 15:43:52 -0500 Subject: [PATCH 0465/3476] fixing node api problems. --- devshop_pull/devshop_pull.module | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 29540636c..4b6516ea4 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -157,34 +157,29 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { break; case 'load': if ($node->type == 'project') { - $data = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); - if (!empty($data->project_nid)) { - $node->pull_enabled = TRUE; - $node->pull_method = $data->pull_method; - $node->last_pull = $data->last_pull; - $node->last_pull_status = $data->last_pull_status; + $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); + if (!empty($data)) { + return $data; } } elseif ($node->type == 'platform') { $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); - return $data; + if (!empty($data)){ + return $data; + } } break; case 'insert': - if ($node->type == 'project') { - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method) VALUES (%d, %d)', $node->nid, $node->pull_method); - } - elseif ($node->type == 'platform') { - db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d)', $node->nid, $node->pull_enabled, $node->pull_reset); - } - break; + break; case 'update': if ($node->type == 'project') { - db_query('UPDATE {hosting_devshop_pull_projects} SET pull_method = %d WHERE project_nid = %d', $node->pull_method, $node->nid); + db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status) VALUES (%d, %d, %d, %d)', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status); } elseif ($node->type == 'platform') { - db_query('UPDATE {hosting_devshop_pull_platforms} SET pull_enabled = %d, pull_reset = %d WHERE platform_nid = %d', $node->pull_enabled, $node->pull_reset, $node->nid); + db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); } break; } From 8ea9cf2cbbb1b456c5746cf9bfef429516a4dc50 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 15:57:14 -0500 Subject: [PATCH 0466/3476] fixing query --- devshop_pull/devshop_pull.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index e69aeb38c..cdd8f42f6 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -100,7 +100,7 @@ function _devshop_pull_hash_create($node) { */ function devshop_pull_project($project_nid) { // Search platforms with pull enabled for this project - $results = db_query("SELECT environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND project_nid = %d", $project_nid); + $results = db_query("SELECT environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND p.project_nid = %d", $project_nid); $args = array('environments' => ''); From 5b0933b2472174ab3fc9b676ac5c4b2556a230cb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 15:57:23 -0500 Subject: [PATCH 0467/3476] fixing bad variable name --- devshop_pull/devshop_pull.settings.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index 988280a37..ce1d80bad 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -15,7 +15,7 @@ function devshop_pull_settings_form() { $form['devshop_pull_ip_acl'] = array( '#type' => 'textarea', '#title' => t('Control Access by IP'), - '#default_value' => variable_get('devshop_github_ip_acl', + '#default_value' => variable_get('devshop_pull_ip_acl', "50.57.128.197\n50.74.68.114\n108.171.174.178\n207.97.227.253\n"), '#rows' => 6, '#description' => t('Enter the IP addresses that are allowed to trigger a "Pull Code" task. GitHub post-receive callback servers are: 50.57.128.197, 50.74.68.114, 108.171.174.178, and 207.97.227.253. Your IP address is !ip', array('!ip' => $_SERVER['REMOTE_ADDR'])) From 59d902746d1ad6f9fe5dcdd001fdc96da78ba571 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 15:58:01 -0500 Subject: [PATCH 0468/3476] renaming setting --- devshop_pull/devshop_pull.module | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 4b6516ea4..447cd96e0 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -71,7 +71,7 @@ function devshop_pull_hosting_queues() { function devshop_pull_devshop_project_settings(){ return array( 'pull_enabled' => t('Pull on Commit'), - 'pull_reset' => t('Hard Reset on Pull'), + 'pull_reset' => t('Reset on Commit'), ); } @@ -226,7 +226,6 @@ function hosting_pull_queue($count) { $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); while ($project = db_fetch_object($result)) { - devshop_pull_project($project->project_nid); module_invoke_all('devshop_pull', $project); } From 5cbe4ecee595287cfc0d1b9473a3565620c817bb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 22:55:04 -0500 Subject: [PATCH 0469/3476] Making settings hookable --- devshop_projects/devshop_projects.drush.inc | 19 +++--- devshop_projects/devshop_projects.module | 17 ++++++ devshop_projects/inc/forms.inc | 67 ++++++++++++--------- devshop_projects/inc/nodes.inc | 12 ++++ 4 files changed, 77 insertions(+), 38 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index ca84e7f74..d46dff70e 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -263,17 +263,14 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['git_tags'] = $branches['tags']; // Load environment settings - if (isset($task->ref->project_objects['platform'])) { - foreach ($task->ref->project_objects['platform'] as $nid => $name){ - $platform = node_load($nid); - $task->context_options['settings'][$name] = array(); - - // @TODO: HOOK! - $task->context_options['settings'][$name]['git_branch'] = $platform->git_branch; - $task->context_options['settings'][$name]['pull_enabled'] = $platform->pull_enabled; - $task->context_options['settings'][$name]['pull_reset'] = $platform->pull_reset; - } - } + if (isset($task->ref->settings)) { + $task->context_options['settings'] = $task->ref->settings; + + // // @TODO: HOOK! + // $task->context_options['settings'][$name]['git_branch'] = $platform->git_branch; + // $task->context_options['settings'][$name]['pull_enabled'] = $platform->pull_enabled; + // $task->context_options['settings'][$name]['pull_reset'] = $platform->pull_reset; + //} } /** diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 00a9ab9d9..364ff0161 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -84,6 +84,23 @@ function devshop_projects_menu() { return ($items); } + +/** + * Implements hook_devshop_project_settings + */ +function devshop_projects_devshop_project_settings($project_node = NULL){ + $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); + return array( + 'git_branch' => array( + '#title' => t('Git Branch'), + '#node_type' => 'platform', + '#type' => 'select', + '#options' => $branch_options, + ), + ); +} + + /** * Page Callback for hosting/projects/add */ diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 3365c2627..4990bbdcf 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -145,29 +145,25 @@ function devshop_projects_form(&$node) { '#tree' => TRUE, ); - $settings = module_invoke_all('devshop_project_settings'); - $branch_options = array_combine($node->git_branches, $node->git_branches); - foreach ($node->project_objects['platform'] as $nid => $name){ + $settings = module_invoke_all('devshop_project_settings', $node); + foreach ($node->project_objects['platform'] as $nid => $environment_name){ $platform = node_load($nid); - $form['settings'][$name] = array( + $form['settings'][$environment_name] = array( '#tree' => TRUE, '#title' => $name, '#type' => 'fieldset', '#theme' => 'devshop_projects_settings_table', ); - $form['settings'][$name]['git_branch'] = array( - '#type' => 'select', - '#title' => t('Branch'), - '#default_value' => isset($platform->git_branch)? $platform->git_branch: NULL, - '#options' => $branch_options, - '#description' => t('Choose the branch that this platform should track.'), - ); - foreach ($settings as $setting => $label){ - $form['settings'][$name][$setting] = array( - '#type' => 'checkbox', - '#title' => $label, - '#default_value' => $platform->{$setting}, - ); + //$form['settings'][$name]['git_branch'] = array( + // '#type' => 'select', + // '#title' => t('Branch'), + // '#default_value' => isset($platform->git_branch)? $platform->git_branch: NULL, + // '#options' => $branch_options, + // '#description' => t('Choose the branch that this platform should track.'), + //); + foreach ($settings as $setting_id => $setting){ + $form['settings'][$environment_name][$setting_id] = $setting; + $form['settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; } } $form['#submit'] = array('devshop_projects_submit_settings'); @@ -188,19 +184,36 @@ function devshop_projects_form(&$node) { * Submit function for save data of platforms. */ function devshop_projects_submit_settings($form, &$form_state) { - $node = node_load($form_state['values']['nid']); + - // @TODO: Using the devshop_project_settings hook, determine if this - // property should be added to the platform or site node. + // Go through and save our settings + $project_node = node_load($form_state['values']['nid']); + $settings_info = module_invoke_all('devshop_project_settings', $project_node); + $nodes = array(); - // Go through and save our platforms - foreach ($node->project_objects['platform'] as $nid => $name){ - $platform = node_load($nid); - $platform->no_verify = TRUE; - foreach ($form_state['values']['settings'][$name] as $setting_name => $value){ - $platform->{$setting_name} = $value; + // Go through each environment + foreach ($form_state['values']['settings'] as $environment_name => $settings){ + + // Then through each setting of each environment + foreach ($settings as $setting_name => $setting_value){ + + //Find the node type for this setting. + $node_type = $settings_info[$setting_name]['#node_type']; + $nids = array_flip($project_node->project_objects[$node_type]); + + // Load the site or platform node for this environment, then set the value and save. + $nid = $nids[$environment_name]; + if (empty($nodes[$nid])){ + $nodes[$nid] = node_load($nid); + $nodes[$nid]->no_verify = TRUE; + } + $nodes[$nid]->{$setting_name} = $setting_value; } - node_save($platform); + } + + // Go save all nodes + foreach ($nodes as $nid => $node){ + node_save($node); } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 91e997332..f39d40c39 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -190,6 +190,18 @@ function devshop_projects_load($node) { // Set status $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node))); + + // Get Settings + $settings = module_invoke_all('devshop_project_settings'); + $additions['settings'] = array(); + + foreach ($settings as $setting_name => $setting){ + $setting_node_type = $setting['#node_type']; + foreach ($additions['project_objects'][$setting_node_type] as $nid => $environment_name){ + $object = node_load($nid); + $additions['settings'][$environment_name][$setting_name] = $object->{$setting_name}; + } + } return $additions; } From a99da36a6f27e3c0fa99df596328b7f537f14759 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 22:55:50 -0500 Subject: [PATCH 0470/3476] adding comment about pull reset --- devshop_pull/devshop_pull.module | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 447cd96e0..c61277798 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -70,8 +70,19 @@ function devshop_pull_hosting_queues() { */ function devshop_pull_devshop_project_settings(){ return array( - 'pull_enabled' => t('Pull on Commit'), - 'pull_reset' => t('Reset on Commit'), + 'pull_enabled' => array( + '#title' => t('Pull on Commit'), + '#node_type' => 'platform', + '#type' => 'checkbox', + '#description' => t('When DevShop receives commit notification, run Pull Code task.'), + ), + // Pull reset won't work until we are storing the settings in the project drush context. + //'pull_reset' => array( + // '#title' => t('Reset files'), + // '#node_type' => 'platform', + // '#type' => 'checkbox', + // '#description' => t('When runing a Pull Code task triggered by a git commit, run git reset --hard just before.'), + //), ); } From 2a4aceef9cf7013e8e63ff2e0f20b623da9e90fd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 23:09:21 -0500 Subject: [PATCH 0471/3476] make sure hosting_logs is present before using adding project settings. --- devshop_log/devshop_log.module | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 76e832525..20fc4d60d 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -14,6 +14,28 @@ function devshop_log_perm() { ); } + +/** + * Implements hook_devshop_project_settings + */ +function devshop_log_devshop_project_settings($project_node = NULL){ + if (module_exists('hosting_logs')){ + return array( + 'logs_enabled' => array( + '#title' => t('Separate Error Logs'), + '#node_type' => 'site', + '#type' => 'checkbox', + ), + 'logs_available' => array( + '#title' => t('Visible Error Logs'), + '#node_type' => 'site', + '#type' => 'checkbox', + ), + ); + } +} + + /** * The access callback for the Git Log menu tab. * @@ -73,13 +95,7 @@ function devshop_log_page($nid) { * General settings form. */ function devshop_git_log_settings() { - $form['devsop_github'] = array( - '#type' => 'item', - '#title' => t('DevShop Git Commit Log View settings'), - '#value' => '', - '#weight' => 0, - ); - $form['devsop_git_log']['devshop_git_log_count'] = array( + $form['devshop_git_log']['devshop_git_log_count'] = array( '#type' => 'textfield', '#maxlength' => 5, '#title' => t('Max log entries to display'), @@ -89,7 +105,6 @@ function devshop_git_log_settings() { 'display in the Git Commit Log View page. A ' . 'value of 0 will display all of the log entries.'), ); - return system_settings_form($form); } From eb740082e0fc9dc9568c1bba2e77b02c35bea8a1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 23:12:36 -0500 Subject: [PATCH 0472/3476] removing old code, we've got settings! --- devshop_projects/devshop_projects.drush.inc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index d46dff70e..0f3097071 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -265,12 +265,7 @@ function devshop_projects_hosting_project_context_options(&$task) { // Load environment settings if (isset($task->ref->settings)) { $task->context_options['settings'] = $task->ref->settings; - - // // @TODO: HOOK! - // $task->context_options['settings'][$name]['git_branch'] = $platform->git_branch; - // $task->context_options['settings'][$name]['pull_enabled'] = $platform->pull_enabled; - // $task->context_options['settings'][$name]['pull_reset'] = $platform->pull_reset; - //} + } } /** From 683951795d7de35ebff37e7347c62ede553392de Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 23:48:30 -0500 Subject: [PATCH 0473/3476] code style, handling a notice --- devshop_projects/devshop_projects.module | 15 ++++++++------- devshop_projects/inc/nodes.inc | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 364ff0161..33a765230 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -90,14 +90,15 @@ function devshop_projects_menu() { */ function devshop_projects_devshop_project_settings($project_node = NULL){ $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); - return array( - 'git_branch' => array( - '#title' => t('Git Branch'), - '#node_type' => 'platform', - '#type' => 'select', - '#options' => $branch_options, - ), + + $settings = array(); + $settings['git_branch'] = array( + '#title' => t('Git Branch'), + '#node_type' => 'platform', + '#type' => 'select', + '#options' => $branch_options, ); + return $settings; } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index f39d40c39..79ccae959 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -197,7 +197,7 @@ function devshop_projects_load($node) { foreach ($settings as $setting_name => $setting){ $setting_node_type = $setting['#node_type']; - foreach ($additions['project_objects'][$setting_node_type] as $nid => $environment_name){ + foreach ((array) $additions['project_objects'][$setting_node_type] as $nid => $environment_name){ $object = node_load($nid); $additions['settings'][$environment_name][$setting_name] = $object->{$setting_name}; } From 778f1224d8ee5e286d9adce5b3d99ceb58e4665e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Feb 2013 23:49:09 -0500 Subject: [PATCH 0474/3476] adding api file for example hooks --- devshop_projects/devshop_projects.api.php | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 devshop_projects/devshop_projects.api.php diff --git a/devshop_projects/devshop_projects.api.php b/devshop_projects/devshop_projects.api.php new file mode 100644 index 000000000..cf1e46f6f --- /dev/null +++ b/devshop_projects/devshop_projects.api.php @@ -0,0 +1,35 @@ +git_branches, (array) $project_node->git_branches); + return array( + 'git_branch' => array( + '#title' => t('Git Branch'), + '#node_type' => 'platform', + '#type' => 'select', + '#options' => $branch_options, + ), + ); +} From 29d79d12f5a7f171659c136efd625e005a5ecec6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Mar 2013 12:44:56 -0500 Subject: [PATCH 0475/3476] simplifying ssh access help --- devshop_projects/inc/create-wizard.inc | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 3cf24cb8f..4a9152ec1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -248,22 +248,9 @@ function devshop_project_create_step_git(&$form, &$form_state) { // If we don't yet have the server's public key saved as a variable... if (empty($pubkey)){ - // Try to read it from the default location, sometimes www-data can read the public key - if (is_readable($ssh_path)){ - $pubkey = file_get_contents($ssh_path); - variable_set('devshop_public_key', $pubkey); - } - // Sometimes www-data can't read /var/aegir/.ssh/id_rsa.pub, so file_exists - // fails. Thats why I'm checking for the parent folder. - elseif (!file_exists('/var/aegir/.ssh')) { - $output .= t("It appears you do not have SSH keys setup. Please ensure that %path exists, or see !link for a nice guide to generating SSH keys.", array('%path' => $pubkey_path, '!link' => l('github:help', 'https://help.github.com/articles/generating-ssh-keys', array('target' => '_blank')))); - - } else { - $output = t("We were unable to read your public SSH key. On your server, run the following command and then reload this page.."); - $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; - - $output .= "
"; - } + $output = t("For convenience, the server's SSH public key would be displayed here. First, on your server, run the following command and then reload this page:"); + $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; + $output .= "
"; } else { // @TODO: Make this Translatable $output = << Date: Tue, 5 Mar 2013 12:54:36 -0500 Subject: [PATCH 0476/3476] cleaning up ssh help --- devshop_projects/inc/create-wizard.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 4a9152ec1..ba5c30444 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -243,18 +243,16 @@ function devshop_project_create_step_git(&$form, &$form_state) { // Display helpful tips for connecting. $pubkey = variable_get('devshop_public_key', ''); - $ssh_path = variable_get('devshop_ssh_path', '/var/aegir/.ssh'); - $pubkey_path = variable_get('devshop_public_key_path', '/var/aegir/.ssh/id_rsa.pub'); // If we don't yet have the server's public key saved as a variable... if (empty($pubkey)){ - $output = t("For convenience, the server's SSH public key would be displayed here. First, on your server, run the following command and then reload this page:"); + $output = t("For convenience, the server's SSH public key will be displayed here, once you run the following command on your server:"); $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; $output .= "
"; } else { // @TODO: Make this Translatable $output = <<If you haven't granted this server access to your Git repository, you should do so now using our public SSH key. +
If you haven't granted this server access to your Git repository, you should do so now using it's public SSH key.
HTML; } From 33b9f088ebf41d54726c3739b0bba6e779c6d4d9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Mar 2013 14:36:32 -0500 Subject: [PATCH 0477/3476] fixing broken projects table! --- devshop_projects/inc/ui.inc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index b93254b35..fa5577d76 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -71,12 +71,18 @@ HTML; } else { //Call hooks for alter informacion $data = module_invoke_all('devshop_projects_page', $rows, $header); + + // @TODO: Better hook... + if (empty($data['rows'])){ + $data['rows'] = $rows; + } + if (empty($data['header'])){ + $data['header'] = $header; + } $output = theme('table', $data['header'], $data['rows'], array('class' => 'hosting-table')); } - - - +$output .= 'hey'; return $output; } From 8eb63f58868f6f8f9dd7c616af80047e1ae4a998 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Mar 2013 14:38:19 -0500 Subject: [PATCH 0478/3476] oops, removing debug output --- devshop_projects/inc/ui.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index fa5577d76..c7980343d 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -82,7 +82,6 @@ HTML; $output = theme('table', $data['header'], $data['rows'], array('class' => 'hosting-table')); } -$output .= 'hey'; return $output; } From 20237a1891b39f5034b2b3da6113fa4208219b8c Mon Sep 17 00:00:00 2001 From: ergonlogic Date: Tue, 5 Mar 2013 15:20:32 -0500 Subject: [PATCH 0479/3476] Issue #1931514 by ergonlogic: Fixed Error on project page. --- devshop_projects/inc/nodes.inc | 35 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 79ccae959..30ba383f0 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -36,6 +36,20 @@ function devshop_projects_node_info() { */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { + if ($node->type == 'project') { + // Settings are added here, since $git_branches hadn't been added to the + // node yet in devshop_projects_load(). + $settings = module_invoke_all('devshop_project_settings', $node); + + foreach ($settings as $setting_name => $setting){ + $setting_node_type = $setting['#node_type']; + foreach ((array) $node->project_objects[$setting_node_type] as $nid => $environment_name){ + $object = node_load($nid); + $node->settings[$environment_name][$setting_name] = $object->{$setting_name}; + } + } + } + // Project-enabled platforms get if ($node->type == 'platform' || $node->type == 'site'){ @@ -178,11 +192,11 @@ function devshop_projects_load($node) { $objects = array(); while($project_object = db_fetch_object($query)) { - + // Only load if site or platform is enabled. // FYI HOSTING_SITE_ENABLED == HOSTING_PLATFORM_ENABLED - $node = node_load($project_object->object_nid); - if ($node->{$project_object->object_type .'_status'} == HOSTING_SITE_ENABLED || $node->{$project_object->object_type .'_status'} == HOSTING_SITE_QUEUED) { + $site_node = node_load($project_object->object_nid); + if ($site_node->{$project_object->object_type .'_status'} == HOSTING_SITE_ENABLED || $site_node->{$project_object->object_type .'_status'} == HOSTING_SITE_QUEUED) { $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; } } @@ -190,18 +204,9 @@ function devshop_projects_load($node) { // Set status $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node))); - - // Get Settings - $settings = module_invoke_all('devshop_project_settings'); - $additions['settings'] = array(); - - foreach ($settings as $setting_name => $setting){ - $setting_node_type = $setting['#node_type']; - foreach ((array) $additions['project_objects'][$setting_node_type] as $nid => $environment_name){ - $object = node_load($nid); - $additions['settings'][$environment_name][$setting_name] = $object->{$setting_name}; - } - } + + // Settings are added in devshop_projects_nodeapi + return $additions; } From cb1947965d279d68606bb8cbd9d0271842ddef75 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Mar 2013 15:33:49 -0500 Subject: [PATCH 0480/3476] fixing how project objects are loaded --- devshop_projects/inc/nodes.inc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 30ba383f0..3d093ea2b 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -192,13 +192,10 @@ function devshop_projects_load($node) { $objects = array(); while($project_object = db_fetch_object($query)) { - // Only load if site or platform is enabled. - // FYI HOSTING_SITE_ENABLED == HOSTING_PLATFORM_ENABLED + // @TODO: We should load all. Let other code hide by status. $site_node = node_load($project_object->object_nid); - if ($site_node->{$project_object->object_type .'_status'} == HOSTING_SITE_ENABLED || $site_node->{$project_object->object_type .'_status'} == HOSTING_SITE_QUEUED) { - $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; - } + $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; } $additions['project_objects'] = $objects; From e578fc07223a774a7938c7e37418743d5a75dfe9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Mar 2013 17:21:07 -0500 Subject: [PATCH 0481/3476] we have to verify again because we have more info. --- devshop_projects/inc/create-wizard.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index ba5c30444..fbf426f68 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -149,7 +149,6 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->live_domain = $project->live_domain; $node->uid = $user->uid; $node->status = 1; - $node->no_verify = TRUE; node_save($node); // Create the site nodes From 9c16b62da55f7013d76fb19d00d126ae97b76a72 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Mar 2013 17:23:33 -0500 Subject: [PATCH 0482/3476] removing blocking of verification. --- devshop_projects/inc/create-wizard.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index fbf426f68..ba5c30444 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -149,6 +149,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->live_domain = $project->live_domain; $node->uid = $user->uid; $node->status = 1; + $node->no_verify = TRUE; node_save($node); // Create the site nodes From 6bee77363f2c91025d7c92a616151e08b8737a5e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 8 Mar 2013 16:50:44 -0500 Subject: [PATCH 0483/3476] making default allowed IPs a constant, updating them to include githubs new servers --- devshop_pull/devshop_pull.inc | 2 +- devshop_pull/devshop_pull.module | 9 +++++++++ devshop_pull/devshop_pull.settings.inc | 5 ++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index cdd8f42f6..b2c3a83f5 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -10,7 +10,7 @@ function devshop_pull_callback($project, $hash) { // Get the access from list - $acl = explode("\n", trim(variable_get('devshop_pull_ip_acl', ''))); + $acl = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); // I wish I didn't have to do this for($i = 0; $i < count($acl); $i++) { diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index c61277798..31a6ea1bb 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -16,6 +16,15 @@ define('DEVSHOP_PULL_DISABLED', 0); define('DEVSHOP_PULL_QUEUE', 1); define('DEVSHOP_PULL_CALLBACK', 2); +define('DEVSHOP_PULL_DEFAULT_ALLOWED_IPS', "207.97.227.253 +50.57.128.197 +108.171.174.178 +50.57.231.61 +204.232.175.64 +192.30.252.0 +204.232.175.75 +"); + // The base URL to use for the Post Commit callback. define('DEVSHOP_PULL_CALLBACK_URL', 'devshop/pull'); diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index ce1d80bad..ba8a67dee 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -15,10 +15,9 @@ function devshop_pull_settings_form() { $form['devshop_pull_ip_acl'] = array( '#type' => 'textarea', '#title' => t('Control Access by IP'), - '#default_value' => variable_get('devshop_pull_ip_acl', - "50.57.128.197\n50.74.68.114\n108.171.174.178\n207.97.227.253\n"), + '#default_value' => variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS), '#rows' => 6, - '#description' => t('Enter the IP addresses that are allowed to trigger a "Pull Code" task. GitHub post-receive callback servers are: 50.57.128.197, 50.74.68.114, 108.171.174.178, and 207.97.227.253. Your IP address is !ip', array('!ip' => $_SERVER['REMOTE_ADDR'])) + '#description' => t('Enter the IP addresses that are allowed to trigger a "Pull Code" task. GitHub post-receive callback servers are: !github_ips. Your IP address is !ip', array('!github_ips' =>variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS), '!ip' => $_SERVER['REMOTE_ADDR'])), ); return system_settings_form($form); } From 371b82b4b4af28d52c2e60738bb2dad34cb765c8 Mon Sep 17 00:00:00 2001 From: ergonlogic Date: Tue, 26 Mar 2013 11:35:09 -0400 Subject: [PATCH 0484/3476] Issue #1931514 by ergonlogic: Fixed Error on project page. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3d093ea2b..3ceda8f9d 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -36,7 +36,7 @@ function devshop_projects_node_info() { */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - if ($node->type == 'project') { + if ($node->type == 'project' && isset($node->git_branches)) { // Settings are added here, since $git_branches hadn't been added to the // node yet in devshop_projects_load(). $settings = module_invoke_all('devshop_project_settings', $node); From a6f64e71c2b97b76b8bf1a6a219c8de2fcb3f47f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 26 Mar 2013 13:31:32 -0400 Subject: [PATCH 0485/3476] ajax reloading! --- devshop_projects/devshop_projects.module | 11 +++ devshop_projects/inc/create-wizard.inc | 87 ++++++++++++++++++--- devshop_projects/inc/reload.js | 98 +++++++++++++++++++++++- 3 files changed, 182 insertions(+), 14 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 33a765230..87cec2d67 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -66,6 +66,15 @@ function devshop_projects_menu() { 'file' => 'create-wizard.inc', 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', ); + + // Ajax endpoint for reloads + $items['hosting/projects/add/status'] = array( + 'page callback' => 'devshop_projects_add_status', + 'access callback' => 'node_access', + 'access arguments' => array('create', 'project'), + 'file' => 'create-wizard.inc', + 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', + ); // hosting tasks ajax pages. foreach (hosting_available_tasks('project') as $task => $info){ @@ -81,6 +90,8 @@ function devshop_projects_menu() { ); $items[$path] = array_merge($items[$path], $info); } + + return ($items); } diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index ba5c30444..f662dce35 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -421,7 +421,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; - $note .= '

' . t('NOTE: It is a known bug that this page doesn\'t automatically reload. Please reload the page manually once the task is complete.') . '

'; $form['note'] = array( '#type' => 'markup', @@ -431,10 +430,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#type' => 'value', '#value' => TRUE, ); - - // @TODO: Add proper JS that reloads the page when tasks finish. - //$path = drupal_get_path('module', 'devshop_projects') . '/inc/reload.js'; - //drupal_add_js($path); + // Add code to reload the page when complete. + devshop_form_reloader($form, 'project'); return; } // Handle failure @@ -455,9 +452,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); return; } - - - // Add JS that reloads the page when tasks finish. + + // this JS handles the form element hiding/showing $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; drupal_add_js($path); @@ -497,6 +493,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#options' => $branch_options ); $form_state['no buttons'] = TRUE; + } @@ -692,8 +689,8 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $completed = FALSE; $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))); } - // If platform verified successfully: - elseif ($task->task_status == HOSTING_TASK_PROCESSING) { + // If platform is still processing: + elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { $completed = FALSE; $row[] = t('Platform download & verification in process.'); } @@ -707,18 +704,21 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Store rows for display $rows[] = $row; } // end foreach platform + // Output our table. $form['platforms'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), ); - + // Provide something for people while they wait. if (!$completed){ $project->no_finish = TRUE; $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; - $note .= '

' . t('NOTE: It is a known bug that this page doesn\'t automatically reload. Please reload the page manually once the task is complete.') . '

'; + + devshop_form_reloader($form, 'platform'); + $form['help'] = array( '#type' => 'markup', @@ -784,3 +784,66 @@ function devshop_project_create_step_sites_submit(&$from, &$form_state) { $project = &$form_state['project']; $project->install_profile = $form_state['values']['install_profile']; } + + +function devshop_projects_add_status($type = 'platform'){ + $return = array(); + + // Get Project Cache + ctools_include('wizard'); + ctools_include('object-cache'); + $project = ctools_object_cache_get('project', NULL); + + + $project_node = node_load($project->project_nid); + + $good_to_go = TRUE; + $nids = array(); + + // When checking project... + if ($type == 'project') { + $nids = array($project_node->nid); + } + + // When checking platforms... + if ($type == 'platform') { + foreach ($project_node->project_objects['platform'] as $nid => $name){ + $nids[] = $nid; + } + } + + // Check verification task for all nids + foreach ($nids as $nid){ + $task = hosting_get_most_recent_task($nid, 'verify'); + if ($task->task_status != HOSTING_TASK_SUCCESS) { + $good_to_go = FALSE; + } + } + + $return['reload'] = $good_to_go; + drupal_json($return); + exit; +} + + + +/** + * Helper for adding reloading feature to form + */ +function devshop_form_reloader(&$form, $type = 'platform'){ + // Add JS that reloads the page when tasks finish. + $form[$element_id] = array( + '#type' => 'item', + '#value' => 'Waiting on verification...', + '#prefix' => '
', + '#suffix' => '
', + '#weight' => 10 + ); + $settings['devshopReload'] = array( + 'type' => $type, + 'delay' => 1000, + ); + + drupal_add_js($settings, 'setting'); + drupal_add_js(drupal_get_path('module','devshop_projects') . '/inc/reload.js'); +} \ No newline at end of file diff --git a/devshop_projects/inc/reload.js b/devshop_projects/inc/reload.js index 5ffc3c13f..ae93738fa 100644 --- a/devshop_projects/inc/reload.js +++ b/devshop_projects/inc/reload.js @@ -1,5 +1,99 @@ - Drupal.behaviors.devshopReload = function() { + setTimeout("devshopCheckProject()", Drupal.settings.devshopReload.delay); +} + +var devshopCheckProject = function(){ + $.get('/hosting/projects/add/status/' + Drupal.settings.devshopReload.type, null, devshopReloadPage , 'json'); +} + +var devshopReloadPage = function(data){ -} \ No newline at end of file + if (data.reload){ + document.location.reload(); + } else { + setTimeout("devshopCheckProject()", Drupal.settings.devshopReload.delay); + + } +} + +// +//hostingTaskRefreshList = function() { +// if (!Drupal.settings.hostingTaskRefresh.nid) { +// return null; +// } +// +// var hostingTaskListRefreshCallback = function(data, responseText) { +// // If the node has been modified, reload the whole page. +// if (Drupal.settings.hostingTaskRefresh.changed < data.changed) { +// // only reload if there is no modal frame currently open +// if ($(document).data('hostingOpenModalFrame') != true) { +// document.location.reload(); +// } +// } +// else { +// $(".hosting-task-list").html(data.markup); +// +// hostingTaskBindButtons('.hosting-task-list'); +// setTimeout("hostingTaskRefreshList()", 30000); +// } +// } +// +// hostingTaskAddOverlay('#hosting-task-list'); +// $.get('/hosting/tasks/' + Drupal.settings.hostingTaskRefresh.nid + '/list', null, hostingTaskListRefreshCallback , 'json' ); +//} +// +// +//function hostingTaskAddOverlay(elem) { +// $(elem).prepend('
'); +//} +// +// +//function hostingTaskRefreshQueueBlock() { +// if (Drupal.settings.hostingTaskRefresh.queueBlock != 1) { +// return null; +// } +// +// var hostingTaskQueueRefreshCallback = function(data, responseText) { +// $("#hosting-task-queue-block").html(data.markup); +// +// hostingTaskBindButtons('#hosting-task-queue-block'); +// setTimeout("hostingTaskRefreshQueueBlock()", 30000); +// } +// +// hostingTaskAddOverlay('#hosting-task-queue-block'); +// $.get('/hosting/tasks/queue', null, hostingTaskQueueRefreshCallback , 'json' ); +//} +// +//$(document).ready(function() { +// $(document).data('hostingOpenModalFrame', false); +// setTimeout("hostingTaskRefreshList()", 30000); +// setTimeout("hostingTaskRefreshQueueBlock()", 30000); +// hostingTaskBindButtons($(this)); +// $('#hosting-task-confirm-form-actions a').click(function() { +// if (parent.Drupal.modalFrame.isOpen) { +// setTimeout(function() { parent.Drupal.modalFrame.close({}, {}); }, 1); +// return false; +// } +// }); +// +//}); +// +//hostingTaskBindButtons = function(elem) { +// $('.hosting-button-dialog', elem).click(function() { +// $(document).data('hostingOpenModalFrame', true) +// var options = { +// url : '/hosting/js' + $(this).attr('href'), +// draggable : false, +// width : 600, +// height : 150, +// onSubmit : function() { +// $(document).data('hostingOpenModalFrame', false) +// hostingTaskRefreshQueueBlock(); +// hostingTaskRefreshList(); +// } +// } +// Drupal.modalFrame.open(options); +// return false; +// }); +//} From f4099ebbdd5a4c2ede47489a4f1f84da125b15a7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 18:41:31 -0400 Subject: [PATCH 0486/3476] better branches UI --- devshop_projects/devshop-style.css | 47 ++++++++++++++++++++++++ devshop_projects/devshop_projects.module | 9 +++++ devshop_projects/inc/ui.inc | 20 ++++++++-- 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 devshop_projects/devshop-style.css diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css new file mode 100644 index 000000000..a83f63382 --- /dev/null +++ b/devshop_projects/devshop-style.css @@ -0,0 +1,47 @@ +div.node div.content div.item-list ul.branches { + margin: 0px; + padding: 0px; +} +div.item-list ul.branches li, +span.branch +{ + list-style: none; + + background-color:#dfdfdf; + + -moz-border-radius:3px; + -webkit-border-radius:3px; + border-radius:3px; + display:inline-block; + white-space: nowrap; + color:#777777; + font-family:arial; + font-size:11px; + font-weight:bold; + padding:0px 6px; + margin: 0px 5px 5px 0px; + text-decoration:none; +} +body.aegir #page div.node div.content .branches-list div.form-item { + padding-bottom: 2em; +} +.refresh-link a { + display: block; + z-index: 2; + float: right; + margin-top: -2em; +} + +fieldset.project-environments { + position: relative; +} +.create-new-environment { + position: absolute; + top: 0px; + right: 0px; + margin-top: 2px; + display: block; +} +a.hosting-goto-site-link { + display: inline-block; +} \ No newline at end of file diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 87cec2d67..3dafa697b 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -14,6 +14,15 @@ include_once('inc/theme.inc'); // Lets keep front end code here. include_once('inc/ui.inc'); +/** + * Implementation of hook_init() + * + * Adds a css file. + */ +function devshop_projects_init() { + drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/devshop-style.css', 'theme'); +} + /** * Implementation of hook_perm() * diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index c7980343d..0ffb220cc 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -129,11 +129,22 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); if (!empty($node->git_branches)){ + $items = theme_item_list($node->git_branches, NULL, 'ul', array('class' => 'branches')); + $verify_task = hosting_get_most_recent_task($node->nid, 'verify'); + + if ($verify_task->task_status == HOSTING_TASK_SUCCESS){ + $refresh_link = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); + } else { + $refresh_link = t('Refresh in progress...'); + } + $node->content['info']['git_branches'] = array( '#type' => 'item', '#title' => t('Remote Branches'), - '#value' => implode($node->git_branches, ' '), - '#weight' => -8 + '#value' => $items . $refresh_link, + '#weight' => -8, + '#prefix' => '
', + '#suffix' => '
', ); } @@ -160,7 +171,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($site->site_status != -2) { //$row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); - $row[] = devshop_hosting_site_goto_link($site); + $branch = "$site->git_branch"; + $row[] = devshop_hosting_site_goto_link($site) . $branch; $row[] = l('Site Dashboard', "node/$site->nid"); } else { @@ -193,7 +205,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ), ); global $user; - $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('query' => array('token' => drupal_get_token($user->uid)))); + $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('attributes' => array('class' => 'create-new-environment'), 'query' => array('token' => drupal_get_token($user->uid)))); $node->content['sites']['table'] = array( '#type' => 'item', '#value' => $table, From d6e43f2ce14f8176b9ddd684e6b77443b395012e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 18:43:29 -0400 Subject: [PATCH 0487/3476] "Pull request" is confusing --- devshop_pull/devshop_pull.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 31a6ea1bb..1df697d4a 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -159,7 +159,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { $last = (int) $node->last_pull; if ($last == 0){ - $output = t('No Requests. Setup your Git host to ping'); + $output = t('No commit notifications received. Setup your Git host to ping'); $output .= strtr(" ", array('!url' => $url)); } else { @@ -167,7 +167,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { } $node->content['info']['last_pull'] = array( '#type' => 'item', - '#title' => t('Last Pull Request'), + '#title' => t('Last Commit'), '#weight' => 32, '#value' => $output, ); From d4fb99dc3198ca45cf380332e902aa5441fd4e01 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 18:45:07 -0400 Subject: [PATCH 0488/3476] removing display of code path and dev site --- devshop_projects/inc/ui.inc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 0ffb220cc..a594c6f8f 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -106,20 +106,20 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#suffix' => '' ); - $node->content['info']['code_path'] = array( - '#type' => 'item', - '#title' => t('Code path'), - '#value' => filter_xss($node->code_path), - '#weight' => -8 - ); - - $url = 'http://dev.' . $node->base_url; - $node->content['info']['base_url'] = array( - '#type' => 'item', - '#title' => t('Dev Site'), - '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), - '#weight' => -10 - ); + //$node->content['info']['code_path'] = array( + // '#type' => 'item', + // '#title' => t('Code path'), + // '#value' => filter_xss($node->code_path), + // '#weight' => -8 + //); + // + //$url = 'http://dev.' . $node->base_url; + //$node->content['info']['base_url'] = array( + // '#type' => 'item', + // '#title' => t('Dev Site'), + // '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), + // '#weight' => -10 + //); $node->content['info']['git_url'] = array( '#type' => 'item', From 78e93bea0cab48a0b59e1f382ac472f3480712cc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 19:03:41 -0400 Subject: [PATCH 0489/3476] hiding "verify project" task --- devshop_projects/inc/tasks.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 55f2e315c..2bf4544f7 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -15,6 +15,8 @@ function devshop_projects_hosting_tasks() { 'description' => t('Verifies access to the git repository, downloads branch and tag information.'), 'provision_save' => TRUE, 'access callback' => 'devshop_hosting_task_menu_access', + 'hidden' => TRUE, + 'nid' => NULL, // This silly hack hides verify... hidden won't work! ); $tasks['project']['devshop-create'] = array( 'title' => t('Create New Environment'), From df04ce456a42ddddbf7764483e8429c349a36754 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 19:05:16 -0400 Subject: [PATCH 0490/3476] better page title --- devshop_projects/inc/forms.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 4990bbdcf..239fef14e 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -45,6 +45,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // On Hosting Task: Create Project form, do our own submission. if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-create'){ + drupal_set_title(t('Create new environment')); $form['#submit'] = array('hosting_task_devshop_create_form_submit'); } From ec57014e7ab6d4e20b2f284771f406a707adaa70 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 19:12:23 -0400 Subject: [PATCH 0491/3476] adding a "hostmaster tasks" hook and file. --- hosting.feature.devshop.inc | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 hosting.feature.devshop.inc diff --git a/hosting.feature.devshop.inc b/hosting.feature.devshop.inc new file mode 100644 index 000000000..34a6b7019 --- /dev/null +++ b/hosting.feature.devshop.inc @@ -0,0 +1,48 @@ + t('DevShop'), + 'description' => t('Enables the project-centric DevShop UI.'), + 'status' => HOSTING_FEATURE_DISABLED, + 'module' => 'devshop_hosting', + 'group' => 'devshop', + ); + $features['devshop_projects'] = array( + 'title' => t('DevShop Projects'), + 'description' => t('Enables the project-centric DevShop UI.'), + 'status' => HOSTING_FEATURE_DISABLED, + 'module' => 'devshop_projects', + 'group' => 'devshop', + ); + $features['devshop_pull'] = array( + 'title' => t('DevShop Pull'), + 'description' => t('Allows automated Pull Code on commit or via queue.'), + 'status' => HOSTING_FEATURE_DISABLED, + 'module' => 'devshop_projects', + 'group' => 'devshop', + ); + $features['devshop_testing'] = array( + 'title' => t('DevShop Testing'), + 'description' => t('Allows running of tests on sites.'), + 'status' => HOSTING_FEATURE_DISABLED, + 'module' => 'devshop_testing', + 'group' => 'devshop', + ); + $features['devshop_logs'] = array( + 'title' => t('DevShop Logs'), + 'description' => t('Provides error and commit logs to DevShop.'), + 'status' => HOSTING_FEATURE_DISABLED, + 'module' => 'devshop_logs', + 'group' => 'devshop', + ); + $features['devshop_live'] = array( + 'title' => t('DevShop Live'), + 'description' => t('Provides tools for going live.'), + 'status' => HOSTING_FEATURE_DISABLED, + 'module' => 'devshop_live', + 'group' => 'devshop', + ); + return $features; +} \ No newline at end of file From b91d2d3bbdd17539a12c4f0314f071fe8a52402b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 19:17:22 -0400 Subject: [PATCH 0492/3476] better UI --- devshop_projects/inc/ui.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index a594c6f8f..2d4d80e51 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -141,10 +141,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->content['info']['git_branches'] = array( '#type' => 'item', '#title' => t('Remote Branches'), - '#value' => $items . $refresh_link, + '#value' => '
' . $items . $refresh_link . '
', '#weight' => -8, - '#prefix' => '
', - '#suffix' => '
', ); } @@ -153,7 +151,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#type' => 'item', '#title' => t('Install profile'), '#value' => ($node->install_profile), - '#weight' => -8 + '#weight' => -7 ); } From cfa8133d958f8ac78dd81221cabb39ed47ddf030 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 20:36:42 -0400 Subject: [PATCH 0493/3476] Major refactor, now stores last IP requested. Added different statuses. --- devshop_pull/devshop_pull.inc | 96 ++++++++++++++----------------- devshop_pull/devshop_pull.install | 15 ++++- devshop_pull/devshop_pull.module | 21 +++++-- 3 files changed, 74 insertions(+), 58 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index b2c3a83f5..d0a6b7c61 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -9,67 +9,59 @@ */ function devshop_pull_callback($project, $hash) { - // Get the access from list - $acl = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); + // @TODO: Should we switch to using node ID so we can use hook_menu's autoloader? + + // Load the project node & list of allowed IPs + $project_node = hosting_context_load(str_replace('project_', '', $project)); + $allowed_ips = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); + array_filter(array_map('trim', $allowed_ips)); - // I wish I didn't have to do this - for($i = 0; $i < count($acl); $i++) { - $acl[$i] = trim($acl[$i]); + + // Make sure we got the project. + if (!$project_node){ + $message = "Project $project not found."; + } + // Make sure the security code is valid + else if (_devshop_pull_hash_create($project_node) != $hash) { + $message = "Security code $hash is not valid!"; + $status = DEVSHOP_PULL_STATUS_INVALID_CODE; + } + // Make sure the project has pull callback enabled + elseif ($project_node->pull_method != DEVSHOP_PULL_CALLBACK){ + $message = "Project $project is NOT configured to use Pull Code URL callback!"; } - // Make sure the client's IP address is on the list - if (!in_array(ip_address(), $acl)) { + else if (!in_array(ip_address(), $allowed_ips)) { $message = ip_address() . " is not authorized to invoke a Pull Code request."; - watchdog('devshop', $message, array(), WATCHDOG_ERROR); - print "$message
"; - return; + $status = DEVSHOP_PULL_STATUS_ACCESS_DENIED; } - - // Log It - $message = "[DEVSHOP] Commit Received! invoked by " . ip_address(); - watchdog('devshop', $message, array(), WATCHDOG_INFO); - print "$message
"; - - - if (strlen($project) < 1 || strlen($hash) != 32) { - $message = "Invalid/missing parameters in URL!"; - watchdog('devshop', $message, array(), WATCHDOG_ERROR); - print "$message
"; - return; + // All checks pass! Server is allowed to trigger tasks! + else { + $message = "Commit Received! Invoked by " . ip_address(); + $status = DEVSHOP_PULL_STATUS_OK; + + // This does the hard work of figuring out what tasks to create. + devshop_pull_project($project_node->nid); } - // Based on the project name, get the node ID for the project node. - // Is need remove the prefix for find the nid. - $project_nid = db_result(db_query( - "SELECT nid FROM {hosting_context} WHERE name = '%s'", str_replace('project_', '', $project))); - - // Load the entire node - if (!$project_node = node_load($project_nid)) { - $message = "Unable to load project node!"; - watchdog('devshop', $message, array(), WATCHDOG_ERROR); - print "$message
"; - return; + // Log It, only if there is a status + if (isset($status)){ + $record = new stdClass; + $record->project_nid = $project_node->nid; + $record->pull_method = $project_node->pull_method; + $record->last_pull = time(); + $record->last_pull_status = $status; + $record->last_pull_ip = ip_address(); + + drupal_write_record('hosting_devshop_pull_projects', $record, array('project_nid')); } - // Make sure the security code is valid - if (_devshop_pull_hash_create($project_node) != $hash) { - $message = "Security code $hash is not valid!"; - watchdog('devshop', $message, array(), WATCHDOG_ERROR); - print "$message
"; - return; - } - - // Make sure the platform has pull callback enabled - if ($project_node->pull_method != DEVSHOP_PULL_CALLBACK){ - $message = "Project is NOT configured to use Pull Code URL callback!"; - watchdog('devshop', $message, array(), WATCHDOG_ERROR); - print "$message
"; - return; - } - - // This does the hard work of figuring out what tasks to create. - devshop_pull_project($project_node->nid); + // Output a message, no matter what. + watchdog('devshop_pull', $message, array(), WATCHDOG_INFO); + print $message; + // Save a variable to help when using the settings page. + variable_set('devshop_pull_last_ip', ip_address()); } /** @@ -110,5 +102,5 @@ function devshop_pull_project($project_nid) { hosting_add_task($project_nid, 'devshop-pull', $args); // Put timestamp - db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 1, $project_nid); + db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), DEVSHOP_PULL_STATUS_OK, $project_nid); } diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index 61a82f060..ffe61f196 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -30,6 +30,11 @@ function devshop_pull_schema() { 'not null' => TRUE, 'default' => 0, ), + 'last_pull_ip' => array( + 'type' => 'varchar', + 'length' => 15, + 'not null' => TRUE, + ), ), 'primary key' => array('project_nid'), ); @@ -83,6 +88,14 @@ function devshop_pull_uninstall() { */ function devshop_pull_update_6000(){ $ret = array(); - db_add_field($ret, 'hosting_devshop_pull_platforms', 'project_nid', array('type' => 'int', 'not null' => TRUE, 'default' => 0)); + db_add_field($ret, 'hosting_devshop_pull_platforms', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '')); + return $ret; +} +/** + * Adds project_nid column to our table. + */ +function devshop_pull_update_6001(){ + $ret = array(); + db_add_field($ret, 'hosting_devshop_pull_projects', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '', 'length' => 15)); return $ret; } \ No newline at end of file diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 1df697d4a..fd1bba52e 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -16,6 +16,12 @@ define('DEVSHOP_PULL_DISABLED', 0); define('DEVSHOP_PULL_QUEUE', 1); define('DEVSHOP_PULL_CALLBACK', 2); +define('DEVSHOP_PULL_STATUS_OK', 1); +define('DEVSHOP_PULL_STATUS_ACCESS_DENIED', 2); +define('DEVSHOP_PULL_STATUS_INVALID_CODE', 3); + +// These are github's Webhook callback IPs. +// This list grows occaisonally... define('DEVSHOP_PULL_DEFAULT_ALLOWED_IPS', "207.97.227.253 50.57.128.197 108.171.174.178 @@ -157,13 +163,18 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { $url = _devshop_pull_callback_url($node); $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); - $last = (int) $node->last_pull; - if ($last == 0){ - $output = t('No commit notifications received. Setup your Git host to ping'); - $output .= strtr(" ", array('!url' => $url)); + $status = (int) $node->last_pull_status; + if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ + $output = t('Commit notification recieved from %ip, but the IP is not allowed to trigger tasks. See !link.', array( + '!link' => l(t('DevShop Pull Settings'), 'admin/hosting/devshop_pull'), + '%ip' => $node->last_pull_ip, + )); - } else { + } elseif ($status == DEVSHOP_PULL_STATUS_OK) { $output = hosting_format_interval($node->last_pull); + } else { + $output = t('No commit notifications received. Setup your Git host to ping'); + $output .= strtr(" ", array('!url' => $url)); } $node->content['info']['last_pull'] = array( '#type' => 'item', From dd93c630b9a98cf301c26ab2124709e460cff460 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 20:49:15 -0400 Subject: [PATCH 0494/3476] better UI for the settings form --- devshop_pull/devshop_pull.settings.inc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index ba8a67dee..63e0c9b11 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -17,7 +17,22 @@ function devshop_pull_settings_form() { '#title' => t('Control Access by IP'), '#default_value' => variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS), '#rows' => 6, - '#description' => t('Enter the IP addresses that are allowed to trigger a "Pull Code" task. GitHub post-receive callback servers are: !github_ips. Your IP address is !ip', array('!github_ips' =>variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS), '!ip' => $_SERVER['REMOTE_ADDR'])), ); + + // we have a few bullet points + $items = array(); + $items[] = t('Enter the IP addresses that are allowed to trigger a "Pull Code" task.'); + $items[] = t('GitHub post-receive callback servers are: %github_ips.', array('%github_ips' => variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); + $items[] = t('Your local computer\'s IP address is %ip. ', array('%ip' => $_SERVER['REMOTE_ADDR'])); + + // If there was a last ip logged, display it here so its easy to add to the list. + $last_ip = variable_get('devshop_pull_last_ip', ''); + if ($last_ip){ + $items[] = t('The last IP to attempt a commit notification was %ip', array('%ip' => $last_ip)); + } else { + $items[] = t('No requests ever detected. If you add the trigger URL for a project to your git repo host, the IP will be logged and displayed here.'); + } + $form['devshop_pull_ip_acl']['#description'] = theme('item_list', $items); + return system_settings_form($form); } From fe8000e76708f38c4b663414b15fc80f63ca9c58 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 22:42:06 -0400 Subject: [PATCH 0495/3476] refactor of node api calls --- devshop_pull/devshop_pull.inc | 30 ++++-- devshop_pull/devshop_pull.module | 175 +++++++++++++++++++++---------- 2 files changed, 141 insertions(+), 64 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index d0a6b7c61..9ec28b907 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -15,8 +15,15 @@ function devshop_pull_callback($project, $hash) { $project_node = hosting_context_load(str_replace('project_', '', $project)); $allowed_ips = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); array_filter(array_map('trim', $allowed_ips)); - + // Check for environments set to pull + $environments_to_pull = array(); + foreach ($project_node->settings as $env => $settings) { + if ($settings['pull_enabled']) { + $environments_to_pull[] = $env; + } + } + // Make sure we got the project. if (!$project_node){ $message = "Project $project not found."; @@ -30,6 +37,10 @@ function devshop_pull_callback($project, $hash) { elseif ($project_node->pull_method != DEVSHOP_PULL_CALLBACK){ $message = "Project $project is NOT configured to use Pull Code URL callback!"; } + // Make sure the project has platforms with pull enabled. + elseif (empty($environments_to_pull)){ + $message = "Project $project has no environments configured to Pull Code on commit!"; + } // Make sure the client's IP address is on the list else if (!in_array(ip_address(), $allowed_ips)) { $message = ip_address() . " is not authorized to invoke a Pull Code request."; @@ -40,8 +51,11 @@ function devshop_pull_callback($project, $hash) { $message = "Commit Received! Invoked by " . ip_address(); $status = DEVSHOP_PULL_STATUS_OK; - // This does the hard work of figuring out what tasks to create. - devshop_pull_project($project_node->nid); + // Create the hosting task + // @TODO: We maybe don't need to pass args here? its saved in the context + $args = array(); + $args['environments'] = implode(' ', $environments_to_pull); + hosting_add_task($project_nid, 'devshop-pull', $args); } // Log It, only if there is a status @@ -99,8 +113,10 @@ function devshop_pull_project($project_nid) { while ($info = db_fetch_object($results)){ $args['environments'] .= $info->environment .' '; } - hosting_add_task($project_nid, 'devshop-pull', $args); - - // Put timestamp - db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), DEVSHOP_PULL_STATUS_OK, $project_nid); + $args['environments'] = trim($args['environments']); + if (!empty($args['environments'])){ + hosting_add_task($project_nid, 'devshop-pull', $args); + } else { + print "No environments configured to pull! Aborting."; + } } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index fd1bba52e..4cd884115 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -146,6 +146,20 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { 'onclick' => 'this.select()', ), ); + + // @TODO: is there a better way to save certain values? We lose data without these. + $form['git_settings']['last_pull'] = array( + '#type' => 'value', + '#value' => $node->last_pull, + ); + $form['git_settings']['last_pull_status'] = array( + '#type' => 'value', + '#value' => $node->last_pull_status, + ); + $form['git_settings']['last_pull_ip'] = array( + '#type' => 'value', + '#value' => $node->last_pull_ip, + ); } } @@ -153,66 +167,99 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { * Implements hook_nodeapi() */ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { - if ($node->type == 'project' || $node->type == 'platform') { - switch ($op) { - case 'view': - if (!$a3) { //!teaser - if($node->type == 'project') { - if ($node->pull_method == DEVSHOP_PULL_CALLBACK) { - module_load_include('inc', 'devshop_pull'); - $url = _devshop_pull_callback_url($node); - $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); - - $status = (int) $node->last_pull_status; - if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ - $output = t('Commit notification recieved from %ip, but the IP is not allowed to trigger tasks. See !link.', array( - '!link' => l(t('DevShop Pull Settings'), 'admin/hosting/devshop_pull'), - '%ip' => $node->last_pull_ip, - )); + + // PROJECTS + if ($node->type == 'project'){ + + // View Project + if ($op == 'view' && $node->pull_method == DEVSHOP_PULL_CALLBACK){ + module_load_include('inc', 'devshop_pull'); + $url = _devshop_pull_callback_url($node); + $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); + + $status = (int) $node->last_pull_status; + + // If access denied, provide link to settings page + if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ + $output = '' . t('Access Denied') . '
'; + $output .= '' . hosting_format_interval($node->last_pull) . '
'; + $output .= t('Commit notification recieved from %ip, but the IP is not allowed to trigger tasks. See !link.', array( + '!link' => l(t('DevShop Pull Settings'), 'admin/hosting/devshop_pull'), + '%ip' => $node->last_pull_ip, + )); - } elseif ($status == DEVSHOP_PULL_STATUS_OK) { - $output = hosting_format_interval($node->last_pull); - } else { - $output = t('No commit notifications received. Setup your Git host to ping'); - $output .= strtr(" ", array('!url' => $url)); - } - $node->content['info']['last_pull'] = array( - '#type' => 'item', - '#title' => t('Last Commit'), - '#weight' => 32, - '#value' => $output, - ); - } - } - } - break; - case 'load': - if ($node->type == 'project') { - $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); - if (!empty($data)) { - return $data; - } } - elseif ($node->type == 'platform') { - $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); - if (!empty($data)){ - return $data; - } + // If OK, show how much time has passed. + elseif ($status == DEVSHOP_PULL_STATUS_OK) { + $output = hosting_format_interval($node->last_pull); } - break; - case 'insert': - - break; - case 'update': - if ($node->type == 'project') { - db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status) VALUES (%d, %d, %d, %d)', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status); + // Otherwise, we assume no commit notification recieved. + else { + $output .= t('No commit notifications received. Setup your Git host to ping'); + $output .= strtr(" ", array('!url' => $url)); } - elseif ($node->type == 'platform') { - db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); + $node->content['info']['last_pull'] = array( + '#type' => 'item', + '#title' => t('Last Commit'), + '#weight' => 32, + '#value' => $output, + ); + } + + // Load Project + elseif ($op == 'load'){ + $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); + if (!empty($data)) { + unset($data['project_nid']); + return $data; } - break; + } + + // Insert Project + elseif ($op == 'insert'){ + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, "%s")', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); + + } + // Update Project + elseif ($op == 'update'){ + // We can't update because devshop_pull might have been enabled after + // project exists + db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, "%s", %d)', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); + + } + // Delete Project + elseif ($op == 'delete'){ + db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); + + } + } + + // PLATFORMS + elseif ($node->type == 'platform'){ + + // Load Platform + if ($node->type == 'platform' && $op == 'load'){ + $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); + if (!empty($data)){ + return $data; + } + } + + // Insert Platform + elseif ($op == 'insert'){ + db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); + } + + // Update Platform + elseif ($op == 'update'){ + db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); + db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); + } + + // Delete Project + elseif ($op == 'delete'){ + db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); } } } @@ -257,7 +304,21 @@ function hosting_pull_queue($count) { $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); while ($project = db_fetch_object($result)) { - devshop_pull_project($project->project_nid); - module_invoke_all('devshop_pull', $project); + $project_node = node_load($project->project_nid); + + // Create the hosting task + // @TODO: We maybe don't need to pass args here? its saved in the context + // Check for environments set to pull + $environments_to_pull = array(); + foreach ($project_node->settings as $env => $settings) { + if ($settings['pull_enabled']) { + $environments_to_pull[] = $env; + } + } + $args = array(); + $args['environments'] = implode(' ', $environments_to_pull); + hosting_add_task($project_node->nid, 'devshop-pull', $args); + + module_invoke_all('devshop_pull', $project_node); } } From 4faed8e5213128dc14bcf33760441445583a8abe Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 1 Apr 2013 22:49:32 -0400 Subject: [PATCH 0496/3476] bad query --- devshop_pull/devshop_pull.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 4cd884115..e0cb468fd 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -217,7 +217,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { // Insert Project elseif ($op == 'insert'){ - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, "%s")', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); + db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, %d, "%s")', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); } // Update Project From cc09cf8e7f9942d9793d4f462557d4d13c8af850 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 21:45:44 -0400 Subject: [PATCH 0497/3476] adding title --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 2d4d80e51..2ff7809cc 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -11,7 +11,7 @@ * a table. */ function devshop_projects_projects_page() { - + drupal_set_title(t('Projects')); $header = array( 'Name', 'Profile', From 2caa0bbc273fb4201368d5365f589235135fdc83 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 22:19:34 -0400 Subject: [PATCH 0498/3476] Fixing "No next button" --- devshop_projects/inc/create-wizard.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f662dce35..03a7b3f1d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -430,6 +430,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#type' => 'value', '#value' => TRUE, ); + $project->no_next = TRUE; + // Add code to reload the page when complete. devshop_form_reloader($form, 'project'); return; @@ -492,8 +494,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#default_value' => $project->branch_platforms, '#options' => $branch_options ); - $form_state['no buttons'] = TRUE; - } @@ -834,7 +834,7 @@ function devshop_form_reloader(&$form, $type = 'platform'){ // Add JS that reloads the page when tasks finish. $form[$element_id] = array( '#type' => 'item', - '#value' => 'Waiting on verification...', + '#value' => '', '#prefix' => '
', '#suffix' => '
', '#weight' => 10 From ff5889a5ae9cc76981a6993b6f47fb464ad67d78 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 22:33:11 -0400 Subject: [PATCH 0499/3476] tiny cleanings --- devshop_projects/inc/nodes.inc | 2 +- devshop_projects/inc/ui.inc | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3ceda8f9d..918e98e78 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -55,7 +55,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On Load: load this object's project and environment type if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT project_nid, environment, n.title as project, git_url, git_branch FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); return $data; } diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 2ff7809cc..968efa754 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -167,8 +167,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $row = array(); if ($site->site_status != -2) { - - //$row[] = l($url, $url, array('attributes' => array('target' => '_blank'))); $branch = "$site->git_branch"; $row[] = devshop_hosting_site_goto_link($site) . $branch; $row[] = l('Site Dashboard', "node/$site->nid"); @@ -196,7 +194,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->content['sites'] = array( '#type' => 'fieldset', - '#title' => t('Project Environments'), + '#title' => t('Environments'), '#weight' => 12, '#attributes' => array( 'class' => 'project-environments', @@ -214,7 +212,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { //Tasks $node->content['tasks_view'] = array( '#type' => 'item', - //'#value' => devshop_projects_hosting_task_table($node), '#value' => hosting_task_table($node), '#prefix' => '
', '#suffix' => '
', From 5126b3d123165f159e819137398b6dc3adc3a647 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 22:33:27 -0400 Subject: [PATCH 0500/3476] don't accidentally reset project_nid --- devshop_pull/devshop_pull.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index e0cb468fd..a45f0a9ae 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -242,6 +242,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'platform' && $op == 'load'){ $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); if (!empty($data)){ + unset($data['project_nid']); return $data; } } From addf988e2b9cd3aa70cbd44deb92fcfa4c14af7e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 22:52:56 -0400 Subject: [PATCH 0501/3476] improved sync form --- devshop_projects/inc/tasks.inc | 60 ++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 2bf4544f7..e75efbb5c 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -260,54 +260,64 @@ function hosting_task_devshop_sync_form($node) { $form = array(); devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment.'), 'destination', 'Destination'); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment. Data from this environment will be DESTROYED.'), 'destination', 'Destination'); + $form['source']['#prefix'] = '
'; + $form['source']['#suffix'] = '
'; + $form['destination']['#prefix'] = '
'; + $form['destination']['#suffix'] = '
'; - $form['database'] = array( - '#title' => t('Sync database from destination to source.'), + $form['to_sync'] = array( + '#type' => 'fieldset', + '#title' => t('What to sync'), + ); + $form['to_sync']['database'] = array( + '#title' => t('Copy database from source to destination.'), '#type' => 'checkbox', '#default_value' => 1, ); - $form['files'] = array( - '#title' => t('Sync files folder from destination to source.'), + $form['to_sync']['files'] = array( + '#title' => t('Copy files folder from source to destination.'), '#type' => 'checkbox', '#default_value' => 0, ); - $form['note'] = array( - '#value' => '

'. t('This will DESTROY the database for Destination and replace it with the database for the selected Source.', array('!site' => l($node->title, "node/$nid"))) . '

', - '#type' => 'markup', - '#weight' => 100, + + $form['actions'] = array( + '#type' => 'fieldset', + '#title' => t('Actions'), ); - $form['pull'] = array( - '#title' => t('Pull code on Destination before content sync?'), + + $form['actions']['pull'] = array( + '#title' => t('Pull code on Destination'), '#type' => 'checkbox', '#default_value' => 1, ); - $form['update'] = array( - '#title' => t('Run update.php on Destination after content sync?'), + $form['actions']['update'] = array( + '#title' => t('Run update.php on Destination'), '#type' => 'checkbox', '#default_value' => 1, ); if (_devshop_projects_project_has_module($node, 'features')){ - $form['revert'] = array( - '#title' => t('Revert all features on Destination after content sync?'), + $form['actions']['revert'] = array( + '#title' => t('Revert all features on Destination'), '#type' => 'checkbox', '#default_value' => $has_features, '#access' => $has_features, ); - }else{ - $form['revert'] = array( - '#title' => t('Revert all features on Destination after content sync?'), - '#type' => 'checkbox', - '#description' => t('This site doesn\'t have features.module enabled. Please enable and then "Verify" the site.'), - '#default_value' => FALSE, - '#disabled' => TRUE, - ); + } else{ + // I think its better UI just to hide it? If they need features it will be enabled! + //$form['actions']['revert'] = array( + // '#title' => t('Revert all features on Destination after content sync?'), + // '#type' => 'checkbox', + // '#description' => t('This site doesn\'t have features.module enabled. Please enable and then "Verify" the site.'), + // '#default_value' => FALSE, + // '#disabled' => TRUE, + // ); } - $form['cache'] = array( - '#title' => t('Clear cache on Destination after content sync?'), + $form['actions']['cache'] = array( + '#title' => t('Clear cache on Destination'), '#type' => 'checkbox', '#default_value' => 1, ); From a2f0061b5a1c536d23309429bafaf2e4a8181745 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 22:53:11 -0400 Subject: [PATCH 0502/3476] improved sync form --- devshop_projects/devshop-style.css | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index a83f63382..1753e53aa 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -44,4 +44,23 @@ fieldset.project-environments { } a.hosting-goto-site-link { display: inline-block; +} + +.sync-envs { + min-height: 100px; +} +.sync-envs > div { + width: 49%; + margin-right: 1%; + float: left; +} + +form .sync-envs div.form-item-labeled { + padding-left: 20px; +} + +form .sync-envs div.form-item-labeled label { + font-size: 11px; + position: relative; + left: 0px; } \ No newline at end of file From b86e69ee0cfcd0e341ffea7106b8a67f2263d1c1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 23:18:54 -0400 Subject: [PATCH 0503/3476] oops, tree defaults to true in D6 apparently --- devshop_projects/inc/tasks.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index e75efbb5c..778d93c88 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -268,6 +268,7 @@ function hosting_task_devshop_sync_form($node) { $form['destination']['#suffix'] = ''; $form['to_sync'] = array( + '#tree' => FALSE, '#type' => 'fieldset', '#title' => t('What to sync'), ); @@ -285,6 +286,7 @@ function hosting_task_devshop_sync_form($node) { $form['actions'] = array( + '#tree' => FALSE, '#type' => 'fieldset', '#title' => t('Actions'), ); From b4d26afe808a5d5d7b189c09f3b386476c50d1be Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Apr 2013 23:36:58 -0400 Subject: [PATCH 0504/3476] fake it till you make it... aegir does funky stuff with forms data --- devshop_projects/inc/tasks.inc | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 778d93c88..a2ff4fd1e 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -267,42 +267,34 @@ function hosting_task_devshop_sync_form($node) { $form['destination']['#prefix'] = '
'; $form['destination']['#suffix'] = '
'; - $form['to_sync'] = array( - '#tree' => FALSE, - '#type' => 'fieldset', - '#title' => t('What to sync'), - ); - $form['to_sync']['database'] = array( + $form['database'] = array( '#title' => t('Copy database from source to destination.'), '#type' => 'checkbox', '#default_value' => 1, + '#prefix' => '
What to sync', ); - $form['to_sync']['files'] = array( + $form['files'] = array( '#title' => t('Copy files folder from source to destination.'), '#type' => 'checkbox', '#default_value' => 0, + '#suffix' => '
', ); - $form['actions'] = array( - '#tree' => FALSE, - '#type' => 'fieldset', - '#title' => t('Actions'), - ); - - $form['actions']['pull'] = array( + $form['pull'] = array( '#title' => t('Pull code on Destination'), '#type' => 'checkbox', '#default_value' => 1, + '#prefix' => '
Actions', ); - $form['actions']['update'] = array( + $form['update'] = array( '#title' => t('Run update.php on Destination'), '#type' => 'checkbox', '#default_value' => 1, ); if (_devshop_projects_project_has_module($node, 'features')){ - $form['actions']['revert'] = array( + $form['revert'] = array( '#title' => t('Revert all features on Destination'), '#type' => 'checkbox', '#default_value' => $has_features, @@ -318,10 +310,11 @@ function hosting_task_devshop_sync_form($node) { // '#disabled' => TRUE, // ); } - $form['actions']['cache'] = array( + $form['cache'] = array( '#title' => t('Clear cache on Destination'), '#type' => 'checkbox', '#default_value' => 1, + '#suffix' => '
', ); $form['#validate'][] = 'hosting_task_devshop_sync_form_validate'; return $form; From ef4c200616b4232961558567153e0902945939fb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 3 Apr 2013 14:05:16 -0400 Subject: [PATCH 0505/3476] big oops! project_nid not available. --- devshop_pull/devshop_pull.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 9ec28b907..414940362 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -55,7 +55,7 @@ function devshop_pull_callback($project, $hash) { // @TODO: We maybe don't need to pass args here? its saved in the context $args = array(); $args['environments'] = implode(' ', $environments_to_pull); - hosting_add_task($project_nid, 'devshop-pull', $args); + hosting_add_task($project_node->nid, 'devshop-pull', $args); } // Log It, only if there is a status From 66e849dbe534d4be16507b4c74c21bc2d4df9407 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 3 Apr 2013 14:07:50 -0400 Subject: [PATCH 0506/3476] adding TODO about checking github payload for branch info. --- devshop_pull/devshop_pull.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 414940362..e790d1f41 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -51,8 +51,12 @@ function devshop_pull_callback($project, $hash) { $message = "Commit Received! Invoked by " . ip_address(); $status = DEVSHOP_PULL_STATUS_OK; + // @TODO: Check Payload Here... If we see a GitHub payload with branches, + // filter out the $environments_to_pull based on those branches. + // Create the hosting task - // @TODO: We maybe don't need to pass args here? its saved in the context + // We need to pass environments so we can control what envs to pull based + // on the data coming back from github! $args = array(); $args['environments'] = implode(' ', $environments_to_pull); hosting_add_task($project_node->nid, 'devshop-pull', $args); From cd824edd812a84bf6374d8ea67a39dd517675576 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 3 Apr 2013 14:30:05 -0400 Subject: [PATCH 0507/3476] forgot to add clear cache option to auto pull --- devshop_pull/devshop_pull.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index e790d1f41..288c2905f 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -59,6 +59,10 @@ function devshop_pull_callback($project, $hash) { // on the data coming back from github! $args = array(); $args['environments'] = implode(' ', $environments_to_pull); + + // Always clear cache... + // @TODO: Should we make this a setting? "What to do on auto-pull?" + $args['cache'] = 1; hosting_add_task($project_node->nid, 'devshop-pull', $args); } From 2fedd6340977b41f6e682c57b6e8de1becee58c4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 3 Apr 2013 15:47:18 -0400 Subject: [PATCH 0508/3476] Yes, we MUST verify! --- devshop_projects/inc/create-wizard.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 03a7b3f1d..9666003ef 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -149,7 +149,6 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->live_domain = $project->live_domain; $node->uid = $user->uid; $node->status = 1; - $node->no_verify = TRUE; node_save($node); // Create the site nodes From e927e5f8d2d73d1170841f40f2e3f36a35cf3871 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 3 Apr 2013 16:04:28 -0400 Subject: [PATCH 0509/3476] better default code path and base url --- devshop_projects/inc/create-wizard.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 9666003ef..f2d3bb5e7 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -334,12 +334,12 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { } // Now that we have the project name, set the defaults for code path and project URL - $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); + $project->code_path = variable_get('devshop_projects_path', '/var/aegir/projects'). '/'. $project_name)); // Setup project url. $base_url = $_SERVER['SERVER_NAME']; - $server = variable_get('devshop_project_default_base_url', $base_url); - $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => strtolower($project_name), '@server' => $server)); + $server = variable_get('devshop_project_master_url', $base_url); + $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => strtolower($project_name), '@server' => server); } /********** From 412437542f4c8581d2ce7503149c79fc103b2e6e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 3 Apr 2013 16:08:56 -0400 Subject: [PATCH 0510/3476] fixed bad code :I" --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f2d3bb5e7..822aaadf2 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -334,12 +334,12 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { } // Now that we have the project name, set the defaults for code path and project URL - $project->code_path = variable_get('devshop_projects_path', '/var/aegir/projects'). '/'. $project_name)); + $project->code_path = variable_get('devshop_projects_path', '/var/aegir/projects'). '/'. $project_name; // Setup project url. $base_url = $_SERVER['SERVER_NAME']; $server = variable_get('devshop_project_master_url', $base_url); - $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => strtolower($project_name), '@server' => server); + $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => strtolower($project_name), '@server' => $server)); } /********** From e831fe0a83b1e05f071d42a696e211176b9ecce7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Apr 2013 00:16:06 -0400 Subject: [PATCH 0511/3476] improving default paths and urls --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 9666003ef..822aaadf2 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -334,11 +334,11 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { } // Now that we have the project name, set the defaults for code path and project URL - $project->code_path = strtr(variable_get('devshop_project_default_code_path', "/var/aegir/projects/@name"), array('@name' => $project_name)); + $project->code_path = variable_get('devshop_projects_path', '/var/aegir/projects'). '/'. $project_name; // Setup project url. $base_url = $_SERVER['SERVER_NAME']; - $server = variable_get('devshop_project_default_base_url', $base_url); + $server = variable_get('devshop_project_master_url', $base_url); $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => strtolower($project_name), '@server' => $server)); } From 18b77b6aabca9c8777a3208d8d85c4d1dc9b727c Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 10 Apr 2013 15:48:35 +0200 Subject: [PATCH 0512/3476] Issue #1958156 by jacintocapote: Exclude in page project overview the sites delete or disable --- devshop_projects/inc/ui.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 968efa754..c23e68493 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -43,7 +43,7 @@ function devshop_projects_projects_page() { $row[] = $platform_node->release->version; // Number of environments - $num = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type='site'", $node->nid)); + $num = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} hdpo LEFT JOIN {hosting_site} hs ON hdpo.object_nid = hs.nid WHERE hdpo.project_nid = %d AND hdpo.object_type='site' AND hs.status=1", $node->nid)); $row[] = format_plural($num, t('1 site'), t('!num sites', array('!num' => $num))); @@ -298,4 +298,4 @@ function devshop_hosting_site_goto_link($node) { $options['attributes']['target'] = '_blank'; $options['attributes']['class'] = 'hosting-goto-site-link'; return l($title, "node/" . $node->nid . "/goto_site", $options); -} \ No newline at end of file +} From b801137e5d35e91efcbad63c806eb761bbf7591a Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 10 Apr 2013 17:37:08 +0200 Subject: [PATCH 0513/3476] Issue #1939654 by jacintocapote: Excluded platforms deleted from verify project --- devshop_projects/devshop_projects.drush.inc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 0f3097071..89cd80437 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -229,9 +229,16 @@ function devshop_projects_post_hosting_verify_task($task, $data) { if ($task->ref->type == 'project' && isset($task->ref->project_objects['platform'])){ $project = node_load($task->ref->nid); $platform_nids = array_keys($project->project_objects['platform']); - foreach ($project->project_objects['platform'] as $nid => $name){ - drush_log('[DEVSHOP] Running verify on project: ' . $name, 'ok'); - hosting_add_task($nid, 'verify'); + foreach ($project->project_objects['platform'] as $nid => $name) { + //Check if a platform was deleted for exclude from verify. + $status = db_result(db_query("SELECT status FROM {hosting_platform} WHERE nid = %d", $nid)); + if ($status != HOSTING_PLATFORM_DELETED) { + drush_log('[DEVSHOP] Running verify on project: ' . $name, 'ok'); + hosting_add_task($nid, 'verify'); + } + else { + drush_log('[DEVSHOP] Can\'t run verify on project: ' . $name, 'ok'); + } } } } From 8ce0c6eb3d0daf7e40ccff72647990191e5f9b19 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 10 Apr 2013 20:29:56 +0200 Subject: [PATCH 0514/3476] Issue #1960242 by jacintocapote: Remove root folder --- devshop_projects/devshop_projects.drush.inc | 17 +++++++++++++---- devshop_projects/inc/nodes.inc | 20 +++++++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 89cd80437..98a407bbd 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -168,14 +168,23 @@ function devshop_projects_post_hosting_delete_task($task, $data) { if ($project->status == 0){ // Don't know if this platform is still here or not... unset($project->project_objects['platform'][$task->ref]); - if (empty($project->project_objects['platform'])){ + + //Check if all platforms were deleted. If is the last platform delete root folder. + $last = TRUE; + if (!empty($node->project_objects['platform'])) { + foreach ($project->project_objects['platform'] as $nid => $type) { + $platform_status = db_result(db_query("SELECT status FROM {hosting_platform} WHERE nid = %d", $nid)); + if ($nid != $task->ref && $platform_status != HOSTING_PLATFORM_DELETED) { + $last = FALSE; + } + } + } + if ($last) { drush_log('[DEVSHOP] Last Platform! Removing code_path folder.', 'ok'); rmdir($project->code_path); } - } - + } } - } /** diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 918e98e78..d64628e09 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -59,11 +59,6 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { return $data; } - // On update or delete, delete the existing records. We will replace below. - if ($op == 'update' || $op == 'delete') { - db_query('DELETE FROM {hosting_devshop_project_object} WHERE object_nid = %d', $node->nid); - } - // On insert or update, insert records saving this objects project and environment if ($op == 'update' || $op == 'insert') { if (!empty($node->project) && !empty($node->environment)) { @@ -71,8 +66,19 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $project = hosting_context_load($node->project); // Save to table - db_query('INSERT INTO {hosting_devshop_project_object} (project_nid, object_nid, object_type, environment, git_branch) VALUES (%d, %d, "%s", "%s", "%s")', $project->nid, $node->nid, $node->type, $node->environment, $node->git_branch); - + $data = new stdClass(); + $data->project_nid = $project->nid; + $data->object_nid = $node->nid; + $data->object_type = $node->type; + $data->environment = $node->environment; + $data->git_branch = $node->git_branch; + if ($op == 'insert') { + drupal_write_record('hosting_devshop_project_object', $data); + } + else { + drupal_write_record('hosting_devshop_project_object', $data, 'object_nid'); + } + //If we are updating or inserting a platform, update all sites with the correct git_branch and environment if ($node->type == 'platform') { $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); From 9b91454e6dc52f417f25e7ff8a036c4d655cdc63 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 12 Apr 2013 20:01:30 +0200 Subject: [PATCH 0515/3476] Issue #1909214 by jacintocapote: Possible create a platforms without a root drupal --- devshop_projects/devshop_projects.drush.inc | 21 ++++++++++++++- devshop_projects/devshop_projects.install | 29 +++++++++++++++++++++ devshop_projects/devshop_projects.module | 17 +++++++++++- devshop_projects/inc/create-wizard.inc | 17 +++++++++++- devshop_projects/inc/forms.inc | 13 +++++++-- devshop_projects/inc/nodes.inc | 8 ++++-- devshop_projects/inc/tasks.inc | 7 ++++- 7 files changed, 104 insertions(+), 8 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 98a407bbd..645562c10 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -88,6 +88,11 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $git_remote = $platform->git_url; $git_branch = $platform->git_branch; + //Remove drupal_path to clone. + if ($platform->drupal_path) { + $root = str_replace($platform->drupal_path, '', $root); + } + // Check if a repo exists if (!is_dir($root) || !drush_shell_cd_and_exec($root, 'git status')){ drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); @@ -106,6 +111,11 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $git_remote = $platform->git_url; $git_branch = $platform->git_branch; + //Remove drupal_path to clone. + if ($platform->drupal_path) { + $root = str_replace($platform->drupal_path, '', $root); + } + // Build the command string $command = "git checkout $git_branch"; } @@ -181,7 +191,14 @@ function devshop_projects_post_hosting_delete_task($task, $data) { } if ($last) { drush_log('[DEVSHOP] Last Platform! Removing code_path folder.', 'ok'); - rmdir($project->code_path); + + //Is the drupal directory isn't empty then we need to delete recursively + if ($project->drupal_path) { + _devshop_rrmdir($project->code_path); + } + else { + rmdir($project->code_path); + } } } } @@ -265,6 +282,7 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['install_profile'] = $task->ref->install_profile; $task->context_options['base_url'] = $task->ref->base_url; $task->context_options['code_path'] = trim($task->ref->code_path, " "); + $task->context_options['drupal_path'] = trim($task->ref->drupal_path, " "); $task->context_options['git_url'] = $task->ref->git_url; $branches = getBranchesAndTags($task->ref->git_url); @@ -294,6 +312,7 @@ function devshop_projects_drush_context_import($context, &$node) { $node->title = $context->project_name; $node->type = $context->type; $node->code_path = $context->code_path; + $node->drupal_path = $context->drupal_path; $node->base_url = $context->base_url; $node->install_profile = $context->install_profile; diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index fa62c9db3..2955cd58d 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -27,6 +27,11 @@ function devshop_projects_schema() { 'size' => 'big', 'not null' => FALSE, ), + 'drupal_path' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), 'base_url' => array( 'type' => 'text', 'size' => 'big', @@ -82,6 +87,11 @@ function devshop_projects_schema() { 'not null' => FALSE, 'description' => 'The current branch of this site or platform.', ), + 'drupal_path' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), ), 'primary key' => array('object_nid'), ); @@ -162,3 +172,22 @@ function devshop_projects_update_5() { $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE git_branch git_branch VARCHAR(128) NULL"); return $ret; } + +/** + * Adds drupal_path column to hosting_devshop_project table. + */ +function devshop_projects_update_6() { + $ret = array(); + db_add_field($ret, 'hosting_devshop_project', 'drupal_path', array('type' => 'text', 'size' => 'big', 'not null' => FALSE)); + return $ret; +} + +/** + * Adds drupal_path column to hosting_devshop_project table. + */ +function devshop_projects_update_7() { + $ret = array(); + db_add_field($ret, 'hosting_devshop_project_object', 'drupal_path', array('type' => 'text', 'size' => 'big', 'not null' => FALSE)); + return $ret; +} + diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 3dafa697b..1812b4be1 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -363,4 +363,19 @@ function _devshop_projects_project_has_module($node, $module) { } return _devshop_projects_site_has_module(node_load($sites['dev']), $module); } - + + /* + * Delete a directory recursively + */ +function _devshop_rrmdir($dir) { + if (is_dir($dir)) { + $objects = scandir($dir); + foreach ($objects as $object) { + if ($object != "." && $object != "..") { + if (filetype($dir."/".$object) == "dir") _devshop_rrmdir($dir."/".$object); else unlink($dir."/".$object); + } + } + reset($objects); + rmdir($dir); + } +} diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 822aaadf2..537739434 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -147,6 +147,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->base_url = $project->base_url; $node->install_profile = $project->install_profile; $node->live_domain = $project->live_domain; + $node->drupal_path = $project->drupal_path; $node->uid = $user->uid; $node->status = 1; node_save($node); @@ -362,6 +363,14 @@ function devshop_project_create_step_settings(&$form, &$form_state) { '#default_value' => $project->code_path, '#maxlength' => 255, ); + $form['drupal_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to Drupal'), + '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), + '#size' => 40, + '#default_Value' => $project->drupal_path, + '#maxlength' => 255, + ); $form['base_url'] = array( '#type' => 'textfield', '#title' => t('Primary Domain'), @@ -398,6 +407,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { // For this step, we just save code path and base url for later saving and // verification. $project->code_path = $form_state['values']['code_path']; + $project->drupal_path = $form_state['values']['drupal_path']; $project->base_url = $form_state['values']['base_url']; } @@ -567,12 +577,17 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform->title = $project->title . '_' . $platform_name; $platform->publish_path = $project->code_path . '/' . $platform_name; + if ($project->drupal_path) { + $platform->publish_path = $project->code_path . '/' . $platform_name . '/' . $project->drupal_path; + } + $servers = hosting_get_servers('http'); $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers)); $platform->git_branch = $branch; $platform->project = $project->title; $platform->environment = $platform_name; + $platform->drupal_path = $project->drupal_path; $platform_node = _devshop_projects_node_create('platform', $platform); $project->platforms[$platform_name] = $platform_node->nid; @@ -845,4 +860,4 @@ function devshop_form_reloader(&$form, $type = 'platform'){ drupal_add_js($settings, 'setting'); drupal_add_js(drupal_get_path('module','devshop_projects') . '/inc/reload.js'); -} \ No newline at end of file +} diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 239fef14e..d82819655 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -117,6 +117,15 @@ function devshop_projects_form(&$node) { '#maxlength' => 255, '#weight' => 2, ); + $form['drupal_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to Drupal'), + '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), + '#size' => 40, + '#default_value' => $node->drupal_path, + '#maxlength' => 255, + '#weight' => 3, + ); $form['base_url'] = array( '#type' => 'textfield', '#title' => t('Primary Domain'), @@ -125,7 +134,7 @@ function devshop_projects_form(&$node) { '#size' => 40, '#default_value' => $node->base_url, '#maxlength' => 255, - '#weight' => 2, + '#weight' => 4, ); // Unchanging Values @@ -172,7 +181,7 @@ function devshop_projects_form(&$node) { // Don't allow editing if ($node->nid) { - $locked = array('title', 'git_url', 'code_path', 'base_url'); + $locked = array('title', 'git_url', 'code_path', 'drupal_path', 'base_url'); foreach ($locked as $field){ $form[$field]['#value'] = $form[$field]['#default_value']; $form[$field]['#type'] = 'value'; diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index d64628e09..7dd99b7c4 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -55,7 +55,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On Load: load this object's project and environment type if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); return $data; } @@ -72,6 +72,8 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $data->object_type = $node->type; $data->environment = $node->environment; $data->git_branch = $node->git_branch; + $data->drupal_path = $node->drupal_path; + if ($op == 'insert') { drupal_write_record('hosting_devshop_project_object', $data); } @@ -83,7 +85,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'platform') { $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); while ($site = db_fetch_object($result)) { - db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $site->nid); + db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); } } } @@ -121,6 +123,7 @@ function devshop_projects_insert($node) { $info->nid = $node->nid; $info->git_url = $node->git_url; $info->code_path = hosting_path_normalize($node->code_path); + $info->drupal_path = hosting_path_normalize($node->drupal_path); $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; $info->live_domain = $node->live_domain; @@ -156,6 +159,7 @@ function devshop_projects_update($node) { $info->nid = $node->nid; $info->git_url = $node->git_url; $info->code_path = hosting_path_normalize($node->code_path); + $info->drupal_path = hosting_path_normalize($node->drupal_path); $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; $info->live_domain = $node->live_domain; diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index a2ff4fd1e..5a4e8a3cf 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -1,5 +1,5 @@ - git_branch = $branch; $platform->project = $project->title; $platform->environment = $platform_name; + $platform->drupal_path = $project->drupal_path; + + if ($project->drupal_path) { + $platform->publish_path = $project->code_path . '/' . $platform_name . '/' . $project->drupal_path; + } watchdog('debug', 'Form state: ' . print_r($form_state['values'],1)); From 878866a8d32fa05b8e9823f1550986de852b33fb Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Mon, 15 Apr 2013 17:58:28 +0200 Subject: [PATCH 0516/3476] Issue #1530638 by jacintocapote: Created first version devshop clients --- devshop_clients/devshop_clients.info | 8 ++ devshop_clients/devshop_clients.install | 46 ++++++++++++ devshop_clients/devshop_clients.module | 98 +++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 devshop_clients/devshop_clients.info create mode 100644 devshop_clients/devshop_clients.install create mode 100644 devshop_clients/devshop_clients.module diff --git a/devshop_clients/devshop_clients.info b/devshop_clients/devshop_clients.info new file mode 100644 index 000000000..41d13c4cc --- /dev/null +++ b/devshop_clients/devshop_clients.info @@ -0,0 +1,8 @@ +name = DevShop Clients +description = Allow DevShop to configure clients. +core = 6.x +package = DevShop + +dependencies[] = hosting +dependencies[] = ctools +dependencies[] = hosting_client diff --git a/devshop_clients/devshop_clients.install b/devshop_clients/devshop_clients.install new file mode 100644 index 000000000..83591e5f7 --- /dev/null +++ b/devshop_clients/devshop_clients.install @@ -0,0 +1,46 @@ + array( + 'pid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'cid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + ); + + return $schema; +} + +/** + * Implementation of hook_install(). + */ +function devshop_clients_install() { + // Create tables. + drupal_install_schema('devshop_clients'); +} + +/** + * Implementation of hook_uninstall(). + */ +function devshop_clients_uninstall() { + drupal_uninstall_schema('devshop_clients'); +} diff --git a/devshop_clients/devshop_clients.module b/devshop_clients/devshop_clients.module new file mode 100644 index 000000000..d573ab9e1 --- /dev/null +++ b/devshop_clients/devshop_clients.module @@ -0,0 +1,98 @@ + 'checkboxes', + '#title' => t('Project access control'), + '#options' => $clients, + '#default_value' => isset($node->clients) ? $node->clients : array(), + '#description' => t('Grant access to this project for relevant clients where required. Leave all checkboxes unchecked to grant all clients access to this project.'), + '#weight' => 99, + ); + } + } +} + +/** + * Implementation of hook_nodeapi(). + */ +function devshop_clients_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { + if ($node->type == 'project') { + switch ($op) { + case 'load': + return devshop_clients_nodeapi_load_project($node); + case 'insert': + case 'update': + // Enter access rights in to the db for clients if any were selected + if (!empty($node->clients)) { + $clients = $node->clients; + foreach ($clients as $cid => $choice) { + if ($choice) { + // Client has been selected, add them to the access table + $existing = db_result(db_query("SELECT cid FROM {hosting_project_client_access} WHERE pid = %d AND cid = %d", $node->nid, $cid)); + if (!$existing) { + db_query("INSERT INTO {hosting_project_client_access} (pid, cid) VALUES (%d, %d)", $node->nid, $cid); + } + } + else { + // Client has not been selected, or unselected + $existing = db_result(db_query("SELECT cid FROM {hosting_project_client_access} WHERE pid = %d AND cid = %d", $node->nid, $cid)); + if ($existing) { + db_query("DELETE FROM {hosting_project_client_access} WHERE pid = %d AND cid = %d", $node->nid, $cid); + } + } + } + + //Now process and add the settings to platforms. + _devshop_clients_add_client_platform($node); + } + break; + case 'delete': + if ($node->type == 'platform') { + db_query("DELETE FROM {hosting_project_client_access} WHERE pid = %d", $node->nid); + } + break; + } + } +} + +/** + * OP load in nodeapi. + */ +function devshop_clients_nodeapi_load_project($node) { + $result = db_query("SELECT cid FROM {hosting_project_client_access} WHERE pid = %d", $node->nid); + + $additions = array(); + + while ($record = db_fetch_object($result)) { + $additions['clients'][$record->cid] = $record->cid; + } + + return $additions; +} + +/** + * Copy client settings to platforms. + */ +function _devshop_clients_add_client_platform($node) { + if ($node->project_objects['platform']) { + foreach ($node->project_objects['platform'] as $nid => $type) { + $platform = node_load($nid); + $platform->clients = $node->clients; + node_save($platform); + } + } +} From d09dbd17eef5bd2c9919acc3dd0c2677bdade0c5 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Mon, 15 Apr 2013 18:39:37 +0200 Subject: [PATCH 0517/3476] Issue #1530638 by jacintocapote: changed to new branch --- devshop_clients/devshop_clients.info | 8 -- devshop_clients/devshop_clients.install | 46 ------------ devshop_clients/devshop_clients.module | 98 ------------------------- 3 files changed, 152 deletions(-) delete mode 100644 devshop_clients/devshop_clients.info delete mode 100644 devshop_clients/devshop_clients.install delete mode 100644 devshop_clients/devshop_clients.module diff --git a/devshop_clients/devshop_clients.info b/devshop_clients/devshop_clients.info deleted file mode 100644 index 41d13c4cc..000000000 --- a/devshop_clients/devshop_clients.info +++ /dev/null @@ -1,8 +0,0 @@ -name = DevShop Clients -description = Allow DevShop to configure clients. -core = 6.x -package = DevShop - -dependencies[] = hosting -dependencies[] = ctools -dependencies[] = hosting_client diff --git a/devshop_clients/devshop_clients.install b/devshop_clients/devshop_clients.install deleted file mode 100644 index 83591e5f7..000000000 --- a/devshop_clients/devshop_clients.install +++ /dev/null @@ -1,46 +0,0 @@ - array( - 'pid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'cid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - ), - ); - - return $schema; -} - -/** - * Implementation of hook_install(). - */ -function devshop_clients_install() { - // Create tables. - drupal_install_schema('devshop_clients'); -} - -/** - * Implementation of hook_uninstall(). - */ -function devshop_clients_uninstall() { - drupal_uninstall_schema('devshop_clients'); -} diff --git a/devshop_clients/devshop_clients.module b/devshop_clients/devshop_clients.module deleted file mode 100644 index d573ab9e1..000000000 --- a/devshop_clients/devshop_clients.module +++ /dev/null @@ -1,98 +0,0 @@ - 'checkboxes', - '#title' => t('Project access control'), - '#options' => $clients, - '#default_value' => isset($node->clients) ? $node->clients : array(), - '#description' => t('Grant access to this project for relevant clients where required. Leave all checkboxes unchecked to grant all clients access to this project.'), - '#weight' => 99, - ); - } - } -} - -/** - * Implementation of hook_nodeapi(). - */ -function devshop_clients_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { - if ($node->type == 'project') { - switch ($op) { - case 'load': - return devshop_clients_nodeapi_load_project($node); - case 'insert': - case 'update': - // Enter access rights in to the db for clients if any were selected - if (!empty($node->clients)) { - $clients = $node->clients; - foreach ($clients as $cid => $choice) { - if ($choice) { - // Client has been selected, add them to the access table - $existing = db_result(db_query("SELECT cid FROM {hosting_project_client_access} WHERE pid = %d AND cid = %d", $node->nid, $cid)); - if (!$existing) { - db_query("INSERT INTO {hosting_project_client_access} (pid, cid) VALUES (%d, %d)", $node->nid, $cid); - } - } - else { - // Client has not been selected, or unselected - $existing = db_result(db_query("SELECT cid FROM {hosting_project_client_access} WHERE pid = %d AND cid = %d", $node->nid, $cid)); - if ($existing) { - db_query("DELETE FROM {hosting_project_client_access} WHERE pid = %d AND cid = %d", $node->nid, $cid); - } - } - } - - //Now process and add the settings to platforms. - _devshop_clients_add_client_platform($node); - } - break; - case 'delete': - if ($node->type == 'platform') { - db_query("DELETE FROM {hosting_project_client_access} WHERE pid = %d", $node->nid); - } - break; - } - } -} - -/** - * OP load in nodeapi. - */ -function devshop_clients_nodeapi_load_project($node) { - $result = db_query("SELECT cid FROM {hosting_project_client_access} WHERE pid = %d", $node->nid); - - $additions = array(); - - while ($record = db_fetch_object($result)) { - $additions['clients'][$record->cid] = $record->cid; - } - - return $additions; -} - -/** - * Copy client settings to platforms. - */ -function _devshop_clients_add_client_platform($node) { - if ($node->project_objects['platform']) { - foreach ($node->project_objects['platform'] as $nid => $type) { - $platform = node_load($nid); - $platform->clients = $node->clients; - node_save($platform); - } - } -} From 271e3034893d92e0bd59cf552734ca10e2c49e2c Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Mon, 15 Apr 2013 19:42:39 +0200 Subject: [PATCH 0518/3476] Issue #1904498 by jacintocapote: Added http server to settings --- devshop_projects/devshop_projects.module | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 1812b4be1..fc6b8da60 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -118,6 +118,18 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ '#type' => 'select', '#options' => $branch_options, ); + + $http_servers = hosting_get_servers('http'); + + if (count($http_servers)) { + $settings['web_server'] = array( + '#title' => t('Web server'), + '#node_type' => 'platform', + '#type' => 'select', + '#options' => $http_servers, + ); + } + return $settings; } From 3ef215cb960d402cf9c6c0aff1122d2ff59bbb1a Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Mon, 15 Apr 2013 20:18:49 +0200 Subject: [PATCH 0519/3476] Issue #1904498 by jacintocapote: Added db server to settings --- devshop_projects/devshop_projects.module | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index fc6b8da60..7478776e1 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -130,6 +130,17 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ ); } + $db_servers = hosting_get_servers('db'); + + if (count($http_servers)) { + $settings['db_server'] = array( + '#title' => t('Database server'), + '#node_type' => 'site', + '#type' => 'select', + '#options' => $db_servers, + ); + } + return $settings; } From f8fef17a061532942697db7aceb102fc61aab167 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Mon, 15 Apr 2013 20:26:56 +0200 Subject: [PATCH 0520/3476] Issue #1904498 by jacintocapote: I added a little bug --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 7478776e1..67c8819dc 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -132,7 +132,7 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ $db_servers = hosting_get_servers('db'); - if (count($http_servers)) { + if (count($db_servers)) { $settings['db_server'] = array( '#title' => t('Database server'), '#node_type' => 'site', From c1d9b51f091153ad86501e4bd5872870440ce4d6 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Mon, 15 Apr 2013 21:31:10 +0200 Subject: [PATCH 0521/3476] Issue #1904498 by jacintocapote: Use import task for db_server --- devshop_projects/inc/forms.inc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index d82819655..a7164ef4a 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -217,6 +217,15 @@ function devshop_projects_submit_settings($form, &$form_state) { $nodes[$nid] = node_load($nid); $nodes[$nid]->no_verify = TRUE; } + + //If changed database then execute migrate task. + if ($setting_name == 'db_server' && $nodes[$nid]->{$setting_name} != $setting_value) { + $args['target_platform'] = $nodes[$nid]->platform; + $args['new_uri'] = $nodes[$nid]->title; + $args['new_db_server'] = $setting_value; + + hosting_add_task($nid, 'migrate', $args); + } $nodes[$nid]->{$setting_name} = $setting_value; } } From 7a28483172d3441b643f499bd1aef43f5caab314 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Wed, 17 Apr 2013 22:07:54 +0200 Subject: [PATCH 0522/3476] Create a new interface for environments. Need work --- devshop_projects/devshop_projects.module | 9 ++ devshop_projects/inc/create-wizard.inc | 105 ++++++++++++++++++++++- devshop_projects/inc/theme.inc | 33 ++++++- 3 files changed, 141 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 67c8819dc..7d836b5d0 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -85,6 +85,15 @@ function devshop_projects_menu() { 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', ); + // Ajax for add new environment in wizard page. + $items['js/devshop/projects/add'] = array( + 'page callback' => 'devshop_projects_add_environment_js', + 'access callback' => 'node_access', + 'access arguments' => array('create', 'project'), + 'file' => 'create-wizard.inc', + 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', + ); + // hosting tasks ajax pages. foreach (hosting_available_tasks('project') as $task => $info){ $path = 'node/%/project_' . $task; diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 537739434..39bd06177 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -425,6 +425,10 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $project_node = node_load($project->project_nid); $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); + + if (isset($project->extra_platform) && !empty($project->extra_platform)) { + $default_platforms = array_merge($default_platforms, $project->extra_platform); + } if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING){ // @TODO: Detect and show errors when they occur. @@ -468,7 +472,50 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; drupal_add_js($path); - $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); + +//New ui + $settings = module_invoke_all('devshop_project_settings', $project_node); + $form['environments'] = array( + '#type' => 'fieldset', + '#title' => t('Environment'), + '#theme' => 'devshop_projects_create_settings_form', + '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', + ); + + foreach ($default_platforms as $env) { + $form['environments'][$env] = array( + '#tree' => TRUE, + '#title' => $env, + '#type' => 'fieldset', + '#theme' => 'devshop_projects_settings_table', + ); + + $form['environments'][$env]['title'] = array( + '#type' => 'textfield', + '#title' => t('Title'), + '#default_value' => $env, + '#size' => 25, + '#maxlength' => 255, + ); + + foreach ($settings as $setting_id => $setting){ + $form['environments'][$env][$setting_id] = $setting; + $form['environments'][$env][$setting_id]['#default_value'] = $project_node->settings[$env][$setting_id]; + } + + //Now add button. + $form['add_environment'] = array( + '#type' => 'submit', + '#value' => t('Add environment'), + '#name' => 'add_environment', + '#submit' => array('devshop_projects_create_wizard_add_new_environment'), + ); + } + +//End new ui +/* $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); $form['default_platforms'] = array( '#type' => 'fieldset', @@ -493,7 +540,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#default_value' => isset($project->default_platforms[$env])? $project->default_platforms[$env]: NULL, '#options' => array(t('Choose a branch...')) + $branch_options, ); - } + }*/ $form['branch_platforms'] = array( '#type' => 'checkboxes', @@ -513,6 +560,10 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) $project = &$form_state['project']; $values = $form_state['values']; + if ($form_state['clicked_button']['#value'] == t('Add environment')) { + return; + } + // Not ready if (isset($values['not_ready'])) { form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); @@ -839,8 +890,6 @@ function devshop_projects_add_status($type = 'platform'){ exit; } - - /** * Helper for adding reloading feature to form */ @@ -861,3 +910,51 @@ function devshop_form_reloader(&$form, $type = 'platform'){ drupal_add_js($settings, 'setting'); drupal_add_js(drupal_get_path('module','devshop_projects') . '/inc/reload.js'); } + +/** + * Add new environment to site. + */ +function devshop_projects_add_environment_js() { + // Get Project Cache + ctools_include('wizard'); + ctools_include('object-cache'); + $project = ctools_object_cache_get('project', NULL); + $count_extra = count($project->extra_platform); + $project->extra_platform['new_platform_'. $count_extra] = 'new platform '. $count_extra; + ctools_object_cache_set('project', NULL, $project); + + $form_state = array( + 'storage' => NULL, + 'submitted' => FALSE, + 'input' => $_POST, + 'method' => 'post', + 'project' => $project, + 'values' => $_POST, + ); + $form_build_id = $_POST['form_build_id']; + $form = form_get_cache($form_build_id, $form_state); + devshop_project_create_step_environments($form, $form_state); + form_set_cache($form_build_id, $form, NULL); + + $env_form = $form['environments']; dpm($env_form); + unset($env_form['#prefix'], $env_form['#suffix']); // Prevent duplicate wrappers. + $javascript = drupal_add_js(NULL, NULL, 'header'); + drupal_json(array( + 'status' => TRUE, + 'data' => theme('status_messages') . drupal_render($env_form), + 'settings' => call_user_func_array('array_merge_recursive', $javascript['setting']), + )); + exit; +} + +function devshop_projects_create_wizard_add_new_environment($form, &$form_state) { + // Get Project Cache + ctools_include('wizard'); + ctools_include('object-cache'); + $project = ctools_object_cache_get('project', NULL); + $count_extra = count($project->extra_platform); + $project->extra_platform['new_platform_'. $count_extra] = 'new platform '. $count_extra; + ctools_object_cache_set('project', NULL, $project); + + drupal_goto('hosting/projects/add/environments'); +} diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 793e9aa1e..8a4bd90d8 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -10,12 +10,18 @@ function devshop_projects_theme(){ 'form' => NULL, ), ), + 'devshop_projects_create_settings_form' => array( + 'arguments' => array( + 'form' => NULL, + ), + ), ); } + /** * Theme function for environments settings */ -function theme_devshop_projects_settings_form($form){ +function theme_devshop_projects_settings_form($form) { $rows = array(); $header = array(); $header[] = t('Environment'); @@ -33,4 +39,27 @@ function theme_devshop_projects_settings_form($form){ } $output = theme('table', $header, $rows, array('id' => 'project-settings-table')); return $output; -} \ No newline at end of file +} + +/** + * Theme function for create environments settings + */ +function theme_devshop_projects_create_settings_form($form) { + $rows = array(); + $header = array(); + foreach (element_children($form) as $env_name) { + $row = array(); + foreach(element_children($form[$env_name]) as $setting){ + if (!isset($header[$setting])){ + $header[$setting] = $form[$env_name][$setting]['#title']; + } + $form[$env_name][$setting]['#title'] = ''; + $row[] = drupal_render($form[$env_name][$setting]); + } + $rows[] = $row; + } + $output = theme('table', $header, $rows, array('id' => 'project-settings-table')); + $output .= '

'. t('Create as many new environments as you would like. "dev", "test", and "live"') .'

'; + + return $output; +} From 605810cf5e6e341712936dfaa1e37897feb63979 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 18 Apr 2013 16:43:05 -0400 Subject: [PATCH 0523/3476] fixing rare case where it reloads and you ALMOST have the platforms --- devshop_projects/inc/create-wizard.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 537739434..f049636d5 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -744,7 +744,8 @@ function devshop_project_create_step_sites(&$form, &$form_state) { elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; $note = '

' . t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; - $note .= '

' . t('NOTE: It is a known bug that this page doesn\'t automatically reload. Please reload the page manually once the task is complete.') . '

'; + + devshop_form_reloader($form, 'platform'); $form['error'] = array( '#type' => 'markup', From 9fc9745b8efd63b52bb3f0a469e40c9738cb205f Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 19 Apr 2013 15:52:06 +0200 Subject: [PATCH 0524/3476] Issue #1974790 by jacintocapote: Deleted right column for step3 --- devshop_projects/devshop-style.css | 6 +- devshop_projects/devshop_projects.install | 10 +- devshop_projects/devshop_projects.module | 14 +- devshop_projects/inc/create-wizard.inc | 176 ++++++---------------- 4 files changed, 63 insertions(+), 143 deletions(-) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 1753e53aa..3e01902a4 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -63,4 +63,8 @@ form .sync-envs div.form-item-labeled label { font-size: 11px; position: relative; left: 0px; -} \ No newline at end of file +} + +.path-hosting-projects-add-environments #right { + display: none; +} diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 2955cd58d..1c1aa2a61 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -77,7 +77,7 @@ function devshop_projects_schema() { 'environment' => array( 'type' => 'varchar', 'not null' => TRUE, - 'length' => 10, + 'length' => 64, 'default' => '', 'description' => 'Environment name. Either dev test live or platform name.', ), @@ -191,3 +191,11 @@ function devshop_projects_update_7() { return $ret; } +/** + * Makes "environment" field larger. + */ +function devshop_projects_update_8() { + $ret = array(); + $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE environment environment VARCHAR(64) NOT NULL"); + return $ret; +} diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 7d836b5d0..f4c95da46 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -84,15 +84,6 @@ function devshop_projects_menu() { 'file' => 'create-wizard.inc', 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', ); - - // Ajax for add new environment in wizard page. - $items['js/devshop/projects/add'] = array( - 'page callback' => 'devshop_projects_add_environment_js', - 'access callback' => 'node_access', - 'access arguments' => array('create', 'project'), - 'file' => 'create-wizard.inc', - 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', - ); // hosting tasks ajax pages. foreach (hosting_available_tasks('project') as $task => $info){ @@ -319,7 +310,7 @@ function devshop_projects_hosting_drush_aliases_name($node) { * Helper function to create a site in a project. * Used by Wizard & "Create Platform" > Post Verify */ -function devshop_projects_create_site($project_node, $platform_node, $platform_name) { +function devshop_projects_create_site($project_node, $platform_node, $platform_name, $db_server = NULL) { global $user; @@ -334,7 +325,8 @@ function devshop_projects_create_site($project_node, $platform_node, $platform_n // @TODO: better client & DB support $node->client = HOSTING_DEFAULT_CLIENT; $servers = hosting_get_servers('db'); - $node->db_server = key($servers); + $server = $db_server ? $db_server : key($servers); + $node->db_server = $server; $node->platform = $platform_node->nid; diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 39bd06177..70ab80b8a 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -155,8 +155,9 @@ function devshop_projects_create_wizard_finish(&$form_state) { // Create the site nodes // @TODO: Can we speed things up here by only running install for the first, // then "Cloning" to create the rest? - foreach ($node->project_objects['platform'] as $nid => $env){ - devshop_projects_create_site($node, node_load($nid), $env); + foreach ($node->project_objects['platform'] as $nid => $env) { + $db_server = $project->environments[$env]['db_server']; + devshop_projects_create_site($node, node_load($nid), $env, $db_server); } ctools_object_cache_clear('project', $form_state['cache name']); @@ -424,13 +425,12 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); - $default_platforms = variable_get('devshop_default_platforms', array('dev', 'test', 'live')); + $default_platforms = variable_get('devshop_default_platforms', array('dev' => 'dev', 'test' => 'test', 'live' => 'live')); if (isset($project->extra_platform) && !empty($project->extra_platform)) { $default_platforms = array_merge($default_platforms, $project->extra_platform); } - if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING){ - + if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) { // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; @@ -450,7 +450,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { return; } // Handle failure - elseif ($project->verify_task_status == HOSTING_TASK_ERROR){ + elseif ($project->verify_task_status == HOSTING_TASK_ERROR) { $type = 'error'; $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, $type)); drupal_set_message($error, 'error'); @@ -472,8 +472,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; drupal_add_js($path); - -//New ui $settings = module_invoke_all('devshop_project_settings', $project_node); $form['environments'] = array( '#type' => 'fieldset', @@ -502,7 +500,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { foreach ($settings as $setting_id => $setting){ $form['environments'][$env][$setting_id] = $setting; - $form['environments'][$env][$setting_id]['#default_value'] = $project_node->settings[$env][$setting_id]; + $form['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; } //Now add button. @@ -513,43 +511,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#submit' => array('devshop_projects_create_wizard_add_new_environment'), ); } - -//End new ui -/* $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); - - $form['default_platforms'] = array( - '#type' => 'fieldset', - '#title' => t('Project Environments'), - '#description' => t('Choose the environments you would like to create for this project. You will be able to create more or change the branch later.'), - '#theme' => 'devshop_project_create_platforms_table', - '#tree' => TRUE, - ); - foreach ($default_platforms as $env) { - $form['default_platforms'][$env] = array( - '#tree' => TRUE, - '#prefix' => '
', - '#suffix' => '
', - ); - $form['default_platforms'][$env]['enabled'] = array( - '#type' => 'checkbox', - '#title' => $env, - '#default_value' => !empty($project->default_platforms[$env]), - ); - $form['default_platforms'][$env]['branch'] = array( - '#type' => 'select', - '#default_value' => isset($project->default_platforms[$env])? $project->default_platforms[$env]: NULL, - '#options' => array(t('Choose a branch...')) + $branch_options, - ); - }*/ - - $form['branch_platforms'] = array( - '#type' => 'checkboxes', - '#multiple' => 'true', - '#title' => t('Branch Environments'), - '#description' => t('Create new environments based on the project\'s git branches.'), - '#default_value' => $project->branch_platforms, - '#options' => $branch_options - ); } @@ -560,45 +521,28 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) $project = &$form_state['project']; $values = $form_state['values']; - if ($form_state['clicked_button']['#value'] == t('Add environment')) { - return; + $default_platforms = variable_get('devshop_default_platforms', array('dev' => 'dev', 'test' => 'test', 'live' => 'live')); + + if (isset($project->extra_platform) && !empty($project->extra_platform)) { + $default_platforms = array_merge($default_platforms, $project->extra_platform); } - // Not ready - if (isset($values['not_ready'])) { - form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); + if ($form_state['clicked_button']['#value'] == t('Add environment')) { return; } - // At least one platform is required - $none_chosen = TRUE; - foreach ($values['default_platforms'] as $env => $data) { - if ($data['enabled']){ - $none_chosen = FALSE; - if ($data['branch']){ - // Save the value - form_set_value($form['default_platforms'][$env], $data['branch'], $form_state); - } else { - // If you chose a default platform, you must choose a branch - form_set_error("default_platforms][$env][branch", t('You must choose a branch to create a platform.')); - } - } else { - // Set a null value. Otherwise this comes through as an array. - form_set_value($form['default_platforms'][$env], NULL, $form_state); + foreach ($default_platforms as $key => $data) { + // Check for illegal chars + if (!preg_match('!^[a-z0-9_]+$!', $data['title'])) { + form_set_error('environments[' . $data['title'] . '][title', t('The environment name must contain only lowercase letters, numbers, and underscores.')); } } - $branch_platforms = array_filter($values['branch_platforms']); - if (!empty($branch_platforms)){ - $none_chosen = FALSE; - } - - if ($none_chosen){ - form_set_error('form', t('You must choose to build at least one platform.')); + // Not ready + if (isset($values['not_ready'])) { + form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); + return; } - - // Filter out branch platforms forms_api so we can keep the submit clean. - form_set_value($form['branch_platforms'], array_filter($values['branch_platforms']), $form_state); } /** @@ -609,51 +553,56 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project = &$form_state['project']; $values = $form_state['values']; - // Get last platforms, in case this form is re-submitted - $old_platforms = array_merge($project->default_platforms, $project->branch_platforms); + $project->environments = $values['environments']; // Get platforms from form values and save into our object - $project->default_platforms = array_filter($values['default_platforms']); - $project->branch_platforms = array_filter($values['branch_platforms']); + $default_platforms = variable_get('devshop_default_platforms', array('dev' => 'dev', 'test' => 'test', 'live' => 'live')); + + if (isset($project->extra_platform) && !empty($project->extra_platform)) { + $default_platforms = array_merge($default_platforms, $project->extra_platform); + } + + $old_platforms = $project->platforms; + $project->default_platforms = $default_platforms; // Create these platforms - $all_platforms = array_merge($project->default_platforms, $project->branch_platforms); - foreach ($all_platforms as $platform_name => $branch) { + foreach ($default_platforms as $name) { + $data = $values['environments'][$name]; // If platform hasn't been created yet, do so now! - if (!empty($branch) && empty($project->platforms[$platform_name])){ + if (empty($project->platforms[$data['title']])) { // Save the damn platform nodes $platform = new stdClass; // hosting_platform fields - $platform->title = $project->title . '_' . $platform_name; - $platform->publish_path = $project->code_path . '/' . $platform_name; + $platform->title = $project->title . '_' . $data['title']; + $platform->publish_path = $project->code_path . '/' . $data['title']; if ($project->drupal_path) { - $platform->publish_path = $project->code_path . '/' . $platform_name . '/' . $project->drupal_path; + $platform->publish_path = $project->code_path . '/' . $data['title'] . '/' . $project->drupal_path; } - $servers = hosting_get_servers('http'); - $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers)); + $platform->web_server = $data['web_server']; - $platform->git_branch = $branch; + $platform->git_branch = $data['git_branch']; + $platform->pull_enable = $data['pull_enabled']; $platform->project = $project->title; - $platform->environment = $platform_name; + $platform->environment = $data['title']; $platform->drupal_path = $project->drupal_path; $platform_node = _devshop_projects_node_create('platform', $platform); - $project->platforms[$platform_name] = $platform_node->nid; + $project->platforms[$data['title']] = $platform_node->nid; } // If the platform already exists, change the platform node branch and save it. // @TODO: only re-save the node if the chosen branch changed. else { - $platform_node = node_load($project->platforms[$platform_name]); - $platform_node->git_branch = $branch; + $platform_node = node_load($project->platforms[$data['title']]); + $platform_node->git_branch = $data['git_branch']; node_save($platform_node); } } // For all removed platforms, trigger a delete task - $removed_platforms = array_diff_key($old_platforms, $all_platforms); + $removed_platforms = array_diff_key($old_platforms, $default_platforms); foreach ($removed_platforms as $platform_name => $branch) { // @TODO: Determine what to do here based on task status... @@ -713,7 +662,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If this platform is no longer in the $project object, we shouldn't show // it here. - if (!isset($project->platforms[$name])){ + if (!isset($project->platforms[$name])) { continue; } @@ -912,48 +861,15 @@ function devshop_form_reloader(&$form, $type = 'platform'){ } /** - * Add new environment to site. + * Functionality for add a new environment. */ -function devshop_projects_add_environment_js() { - // Get Project Cache - ctools_include('wizard'); - ctools_include('object-cache'); - $project = ctools_object_cache_get('project', NULL); - $count_extra = count($project->extra_platform); - $project->extra_platform['new_platform_'. $count_extra] = 'new platform '. $count_extra; - ctools_object_cache_set('project', NULL, $project); - - $form_state = array( - 'storage' => NULL, - 'submitted' => FALSE, - 'input' => $_POST, - 'method' => 'post', - 'project' => $project, - 'values' => $_POST, - ); - $form_build_id = $_POST['form_build_id']; - $form = form_get_cache($form_build_id, $form_state); - devshop_project_create_step_environments($form, $form_state); - form_set_cache($form_build_id, $form, NULL); - - $env_form = $form['environments']; dpm($env_form); - unset($env_form['#prefix'], $env_form['#suffix']); // Prevent duplicate wrappers. - $javascript = drupal_add_js(NULL, NULL, 'header'); - drupal_json(array( - 'status' => TRUE, - 'data' => theme('status_messages') . drupal_render($env_form), - 'settings' => call_user_func_array('array_merge_recursive', $javascript['setting']), - )); - exit; -} - function devshop_projects_create_wizard_add_new_environment($form, &$form_state) { // Get Project Cache ctools_include('wizard'); ctools_include('object-cache'); $project = ctools_object_cache_get('project', NULL); $count_extra = count($project->extra_platform); - $project->extra_platform['new_platform_'. $count_extra] = 'new platform '. $count_extra; + $project->extra_platform['environment'. $count_extra] = 'environment'. $count_extra; ctools_object_cache_set('project', NULL, $project); drupal_goto('hosting/projects/add/environments'); From 8ea94d9db3d379a58e32ae322ffde5c9daddb745 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 19 Apr 2013 18:19:33 +0200 Subject: [PATCH 0525/3476] Issue #1974082 by jacintocapote: Allow skip the second step --- devshop_projects/devshop_projects.module | 11 ++++++- devshop_projects/inc/admin.inc | 37 ++++++++++++++++++++++++ devshop_projects/inc/create-wizard.inc | 14 +++++++-- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 devshop_projects/inc/admin.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index f4c95da46..a3945e5cf 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -49,6 +49,16 @@ function devshop_projects_perm() { * Implementation of hook_menu() */ function devshop_projects_menu() { + //Settings page. + $items['admin/hosting/devshop_projects'] = array( + 'title' => 'Settings projects', + 'description' => 'Default values for use in creation project', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('devshop_projects_settings_form'), + 'access arguments' => array('administer projects'), + 'file' => 'admin.inc', + 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', + ); $items['hosting/projects'] = array( 'title' => t('Projects'), @@ -100,7 +110,6 @@ function devshop_projects_menu() { $items[$path] = array_merge($items[$path], $info); } - return ($items); } diff --git a/devshop_projects/inc/admin.inc b/devshop_projects/inc/admin.inc new file mode 100644 index 000000000..f09fd8e88 --- /dev/null +++ b/devshop_projects/inc/admin.inc @@ -0,0 +1,37 @@ + t('Default Project Path'), + '#type' => 'textfield', + '#description' => t('Used in Code Path'), + '#default_value' => variable_get('devshop_project_default_path', '/var/aegir/projects'), + ); + $form['devshop_project_default_drupal_path'] = array( + '#title' => t('Default Project Drupal Path'), + '#type' => 'textfield', + '#description' => t('Used in Drupal Code Path'), + '#default_value' => variable_get('devshop_project_default_drupal_path', ''), + ); + $form['devshop_project_default_base_url_pattern'] = array( + '#title' => t('Default Primary Domain Pattern'), + '#type' => 'textfield', + '#description' => t('Used in Primary Domain. You can use @project for project name and @hostname for server name.'), + '#default_value' => variable_get('devshop_project_default_base_url_pattern', '@project.@hostname'), + ); + $form['devshop_projects_skip_settings'] = array( + '#title' => t('Skip Settings Page'), + '#description' => t('when starting a new project'), + '#type' => 'checkbox', + '#default_value' => variable_get('devshop_projects_skip_settings', TRUE), + ); + + return system_settings_form($form); +} diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 70ab80b8a..fe706093d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -341,7 +341,7 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { // Setup project url. $base_url = $_SERVER['SERVER_NAME']; $server = variable_get('devshop_project_master_url', $base_url); - $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@name.@server"), array('@name' => strtolower($project_name), '@server' => $server)); + $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => strtolower($project_name), '@hostname' => $server)); } /********** @@ -355,6 +355,16 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { function devshop_project_create_step_settings(&$form, &$form_state) { $project = &$form_state['project']; + //Verify if is need to skip. + $skip = variable_get('devshop_projects_skip_settings', TRUE); + if ($skip) { + $project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'); + $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); + $project->step = 'environments'; + $cache = ctools_object_cache_set('project', $form_state['cache name'], $project); + drupal_goto('hosting/projects/add/environments'); + } + $form['code_path'] = array( '#type' => 'textfield', '#title' => t('Code path'), @@ -562,7 +572,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $default_platforms = array_merge($default_platforms, $project->extra_platform); } - $old_platforms = $project->platforms; + $old_platforms = $project->platforms ? $project->platforms : array(); $project->default_platforms = $default_platforms; // Create these platforms From ef78179c7d88a19a3fdfa4529949a3c399116c2f Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 19 Apr 2013 18:57:24 +0200 Subject: [PATCH 0526/3476] Issue #1974790 by jacintocapote: Added javascript for show in hover description of pull commit --- devshop_projects/inc/environments.js | 54 +++++----------------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/devshop_projects/inc/environments.js b/devshop_projects/inc/environments.js index 47f05743a..6846378da 100644 --- a/devshop_projects/inc/environments.js +++ b/devshop_projects/inc/environments.js @@ -1,49 +1,13 @@ Drupal.behaviors.devshopPlatforms = function() { - - // DEV - // Hide if not checked - if (!$('#edit-default-platforms-dev-enabled').attr('checked')) { - $('#edit-default-platforms-dev-branch-wrapper').hide(); - } - - // ONCLICK: - $('#edit-default-platforms-dev-enabled').change(function(e){ - if ($(this).attr('checked')) { - $('#edit-default-platforms-dev-branch-wrapper').show(); - } else { - $('#edit-default-platforms-dev-branch-wrapper').hide(); + $('.form-option .description').hide(); + $(".form-option").hover( + function() { + $(this).find(".description").show(); + }, + function() { + $(this).find(".description").hide(); } - }); - - // TEST - // Hide if not checked - if (!$('#edit-default-platforms-test-enabled').attr('checked')) { - $('#edit-default-platforms-test-branch-wrapper').hide(); - } - - // ONCLICK: - $('#edit-default-platforms-test-enabled').change(function(e){ - if ($(this).attr('checked')) { - $('#edit-default-platforms-test-branch-wrapper').show(); - } else { - $('#edit-default-platforms-test-branch-wrapper').hide(); - } - }); - - // LIVE - // Hide if not checked - if (!$('#edit-default-platforms-live-enabled').attr('checked')) { - $('#edit-default-platforms-live-branch-wrapper').hide(); - } - - // ONCLICK: - $('#edit-default-platforms-live-enabled').change(function(e){ - if ($(this).attr('checked')) { - $('#edit-default-platforms-live-branch-wrapper').show(); - } else { - $('#edit-default-platforms-live-branch-wrapper').hide(); - } - }); -} \ No newline at end of file + ); +} From 6469248388fb1a0f16bba501b7313ac850827cc6 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 19 Apr 2013 20:48:47 +0200 Subject: [PATCH 0527/3476] Issue #1960244 by jacintocapote: Fixed problem with verify when is deleted a project --- devshop_projects/devshop_projects.drush.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 645562c10..feb95f2f2 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -160,6 +160,7 @@ function devshop_projects_post_hosting_delete_task($task, $data) { // @TODO: Should probably add our own status column // The last step set status = 0 project node $task->ref->status = 0; + $task->ref->no_verify = TRUE; node_save($task->ref); } From 265f3090425cb7b86503a9f0d8762175472add5e Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Mon, 22 Apr 2013 17:52:50 +0200 Subject: [PATCH 0528/3476] Issue #1897932 by jacintocapote: Added option force to pull. And fixed some style guideline --- devshop_projects/devshop_projects.drush.inc | 1 + devshop_projects/inc/tasks.inc | 6 ++++++ devshop_pull/devshop_pull.module | 15 ++++++++------- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index feb95f2f2..acb4a0b10 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -32,6 +32,7 @@ function drush_devshop_projects_pre_hosting_task() { $task->options['update'] = $task->task_args['update']; $task->options['revert'] = !empty($task->task_args['revert']); $task->options['cache'] = $task->task_args['cache']; + $task->options['force'] = $task->task_args['force']; } // Commit diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 5a4e8a3cf..786e001b7 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -237,6 +237,12 @@ function hosting_task_devshop_pull_form($node) { '#type' => 'checkbox', '#default_value' => 1, ); + $form['force'] = array( + '#title' => t('Force'), + '#description' => t('Force to execute update, cache if are enabled'), + '#type' => 'checkbox', + '#default_value' => 1, + ); // Add validator for environments //$form['#validate'] = array('hosting_task_devshop_pull_form'); diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index a45f0a9ae..4448f7536 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -22,13 +22,14 @@ define('DEVSHOP_PULL_STATUS_INVALID_CODE', 3); // These are github's Webhook callback IPs. // This list grows occaisonally... -define('DEVSHOP_PULL_DEFAULT_ALLOWED_IPS', "207.97.227.253 -50.57.128.197 -108.171.174.178 -50.57.231.61 -204.232.175.64 -192.30.252.0 -204.232.175.75 +define('DEVSHOP_PULL_DEFAULT_ALLOWED_IPS'," + 207.97.227.253 + 50.57.128.197 + 108.171.174.178 + 50.57.231.61 + 204.232.175.64 + 192.30.252.0 + 204.232.175.75 "); // The base URL to use for the Post Commit callback. From eecba79ee789f7225eb7a32e74afe622bbb971fd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 23 Apr 2013 10:57:11 -0400 Subject: [PATCH 0529/3476] beginning to refactor environments handling --- devshop_projects/inc/create-wizard.inc | 80 +++++++++++++++----------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 52d021403..cf485017e 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -32,8 +32,7 @@ function devshop_projects_create_wizard($step = NULL){ $project->git_url = ''; $project->project_nid = NULL; $project->title = ''; - $project->default_platforms = array(); - $project->branch_platforms = array(); + $project->environments = array('NEW' => array()); $project->live_domain = ''; // ** set the storage object so its ready for whatever comes next @@ -434,12 +433,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); - - $default_platforms = variable_get('devshop_default_platforms', array('dev' => 'dev', 'test' => 'test', 'live' => 'live')); - - if (isset($project->extra_platform) && !empty($project->extra_platform)) { - $default_platforms = array_merge($default_platforms, $project->extra_platform); - } +dsm($project, 'project in form'); if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) { // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? @@ -483,34 +477,43 @@ function devshop_project_create_step_environments(&$form, &$form_state) { drupal_add_js($path); $settings = module_invoke_all('devshop_project_settings', $project_node); + $form['environments'] = array( '#type' => 'fieldset', - '#title' => t('Environment'), '#theme' => 'devshop_projects_create_settings_form', '#tree' => TRUE, '#prefix' => '
', '#suffix' => '
', ); - foreach ($default_platforms as $env) { + foreach ($project->environments as $env => $env_settings) { + // No platforms exist yet + if ($env == 'NEW') { + $env_title = ''; + } else { + $env_title = $env; + } $form['environments'][$env] = array( '#tree' => TRUE, - '#title' => $env, '#type' => 'fieldset', '#theme' => 'devshop_projects_settings_table', ); $form['environments'][$env]['title'] = array( '#type' => 'textfield', - '#title' => t('Title'), - '#default_value' => $env, + '#title' => t('Environment Name'), + '#default_value' => $env_title, '#size' => 25, '#maxlength' => 255, + '#attributes' => array( + 'placeholder' => t('Name this environment...'), + ), ); + // Add environment settings form elements foreach ($settings as $setting_id => $setting){ $form['environments'][$env][$setting_id] = $setting; - $form['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; + $form['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; } //Now add button. @@ -529,30 +532,37 @@ function devshop_project_create_step_environments(&$form, &$form_state) { */ function devshop_project_create_step_environments_validate(&$form, &$form_state) { $project = &$form_state['project']; - $values = $form_state['values']; - - $default_platforms = variable_get('devshop_default_platforms', array('dev' => 'dev', 'test' => 'test', 'live' => 'live')); - - if (isset($project->extra_platform) && !empty($project->extra_platform)) { - $default_platforms = array_merge($default_platforms, $project->extra_platform); - } + $values = &$form_state['values']; - if ($form_state['clicked_button']['#value'] == t('Add environment')) { + // Project not verified yet. We don't know the branches. + if (isset($values['not_ready'])) { + form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); return; } - - foreach ($default_platforms as $key => $data) { + + // Check environment titles + foreach ($values['environments'] as $env => $env_settings) { // Check for illegal chars - if (!preg_match('!^[a-z0-9_]+$!', $data['title'])) { - form_set_error('environments[' . $data['title'] . '][title', t('The environment name must contain only lowercase letters, numbers, and underscores.')); + if ($env != 'NEW' && !preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { + $form_item = 'environments][' . $env . '][title'; + form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); } } + + // Change NEW environment data to $title + if ($values['environments']['NEW']['title']){ + $new_env = $values['environments']['NEW']['title']; + $new_env_settings = $values['environments']['NEW']; + $values['environments'][$new_env] = $new_env_settings; - // Not ready - if (isset($values['not_ready'])) { - form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); - return; + // Create the next NEW environment + unset($values['environments']['NEW']); + $values['environments']['NEW'] = array(); } + + // Save envs to project + $project->environments = $values['environments']; + } /** @@ -566,7 +576,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project->environments = $values['environments']; // Get platforms from form values and save into our object - $default_platforms = variable_get('devshop_default_platforms', array('dev' => 'dev', 'test' => 'test', 'live' => 'live')); + $default_platforms = array(''); if (isset($project->extra_platform) && !empty($project->extra_platform)) { $default_platforms = array_merge($default_platforms, $project->extra_platform); @@ -878,10 +888,12 @@ function devshop_projects_create_wizard_add_new_environment($form, &$form_state) // Get Project Cache ctools_include('wizard'); ctools_include('object-cache'); - $project = ctools_object_cache_get('project', NULL); - $count_extra = count($project->extra_platform); - $project->extra_platform['environment'. $count_extra] = 'environment'. $count_extra; + + // All we are doing here is saving the project to cache. + // devshop_project_create_step_environments_validate() handles putting data in $project. + $project = &$form_state['project']; ctools_object_cache_set('project', NULL, $project); + // Go back to the same page. drupal_goto('hosting/projects/add/environments'); } From e3bfee3dd0f059153427c7daa61f1a59767b7fca Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 23 Apr 2013 11:10:28 -0400 Subject: [PATCH 0530/3476] environments submit refactor --- devshop_projects/inc/create-wizard.inc | 49 ++++++++++++-------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index cf485017e..5d680a523 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -573,36 +573,32 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project = &$form_state['project']; $values = $form_state['values']; - $project->environments = $values['environments']; - - // Get platforms from form values and save into our object - $default_platforms = array(''); - - if (isset($project->extra_platform) && !empty($project->extra_platform)) { - $default_platforms = array_merge($default_platforms, $project->extra_platform); - } - - $old_platforms = $project->platforms ? $project->platforms : array(); - $project->default_platforms = $default_platforms; + $project->environments = array_filter($values['environments']); + $first_envs = $project->environments ? $project->environments : array(); + $settings = module_invoke_all('devshop_project_settings', $project_node); + // Create these platforms - foreach ($default_platforms as $name) { - $data = $values['environments'][$name]; + foreach ($project->environments as $name => $data) { + // If platform hasn't been created yet, do so now! - if (empty($project->platforms[$data['title']])) { + if (empty($data['platform_nid'])) { // Save the damn platform nodes $platform = new stdClass; - // hosting_platform fields + // Platform name $platform->title = $project->title . '_' . $data['title']; - $platform->publish_path = $project->code_path . '/' . $data['title']; - + + // Platform publish_path if ($project->drupal_path) { $platform->publish_path = $project->code_path . '/' . $data['title'] . '/' . $project->drupal_path; } + else { + $platform->publish_path = $project->code_path . '/' . $data['title']; + } + // Other attributes $platform->web_server = $data['web_server']; - $platform->git_branch = $data['git_branch']; $platform->pull_enable = $data['pull_enabled']; $platform->project = $project->title; @@ -610,30 +606,31 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform->drupal_path = $project->drupal_path; $platform_node = _devshop_projects_node_create('platform', $platform); - $project->platforms[$data['title']] = $platform_node->nid; + $project->environments[$name]['platform_nid'] = $platform_node->nid; } - // If the platform already exists, change the platform node branch and save it. - // @TODO: only re-save the node if the chosen branch changed. + // If the platform already exists, save it again with new settings. else { $platform_node = node_load($project->platforms[$data['title']]); - $platform_node->git_branch = $data['git_branch']; + foreach ($settings as $setting_name => $element){ + $platform_node->{$setting_name} = $data[$setting_name]; + } node_save($platform_node); } } // For all removed platforms, trigger a delete task - $removed_platforms = array_diff_key($old_platforms, $default_platforms); - foreach ($removed_platforms as $platform_name => $branch) { + $removed_environments = array_diff_key($first_envs, $values['environments']); + foreach ($removed_environments as $environment_name => $settings) { // @TODO: Determine what to do here based on task status... // if verify task hasn't even run yet (and has never run) we can just delete // the platform node. // Create 'delete' task for this project - hosting_add_task($project->platforms[$platform_name], 'delete'); + hosting_add_task($project->environments[$environment_name]['platform_nid'], 'delete'); // Remove from project object - unset($project->platforms[$platform_name]); + unset($project->environments[$environment_name]); } } From b3f93c868f6db825d02edb98cbb5c104c195a9c6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 23 Apr 2013 11:20:47 -0400 Subject: [PATCH 0531/3476] Look for environments, not platforms --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 5d680a523..bd827e57f 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -433,7 +433,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); -dsm($project, 'project in form'); + if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) { // @TODO: Detect and show errors when they occur. // @TODO: Pretty this up, figure out how to maybe, display the task in the body? @@ -679,7 +679,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If this platform is no longer in the $project object, we shouldn't show // it here. - if (!isset($project->platforms[$name])) { + if (!isset($project->environments[$name])) { continue; } From 92bcb8cee04afc0a3b55659bb2db231d4e69c46a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 23 Apr 2013 11:49:56 -0400 Subject: [PATCH 0532/3476] adding skip settings code to step 1. --- devshop_projects/inc/create-wizard.inc | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index bd827e57f..99a6e8d7b 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -14,6 +14,16 @@ function devshop_projects_create_wizard($step = NULL){ $form_state = array( 'cache name' => NULL, ); + + // Skip step 2 if needed + $skip = variable_get('devshop_projects_skip_settings', TRUE); + if ($skip) { + $form_info['order'] = array( + 'git_url' => t('Step 1: Source'), + 'environments' => t('Step 2: Environments'), + 'sites' => t('Step 3: Install Profile'), + ); + } // Setup project $project = ctools_object_cache_get('project', NULL); @@ -341,6 +351,15 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $base_url = $_SERVER['SERVER_NAME']; $server = variable_get('devshop_project_master_url', $base_url); $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => strtolower($project_name), '@hostname' => $server)); + + + //Verify if is need to skip. + $skip = variable_get('devshop_projects_skip_settings', TRUE); + if ($skip) { + $project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'); + $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); + $project->step = 'environments'; + } } /********** @@ -354,15 +373,6 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { function devshop_project_create_step_settings(&$form, &$form_state) { $project = &$form_state['project']; - //Verify if is need to skip. - $skip = variable_get('devshop_projects_skip_settings', TRUE); - if ($skip) { - $project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'); - $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); - $project->step = 'environments'; - $cache = ctools_object_cache_set('project', $form_state['cache name'], $project); - drupal_goto('hosting/projects/add/environments'); - } $form['code_path'] = array( '#type' => 'textfield', From 355c7eda6df800962cd28796b70c1715b36e53c3 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 23 Apr 2013 17:46:53 +0200 Subject: [PATCH 0533/3476] New feature for clone a environment --- devshop_projects/devshop_projects.drush.inc | 73 ++++++++++++++++----- devshop_projects/devshop_projects.install | 17 +++++ devshop_projects/devshop_projects.module | 3 +- devshop_projects/inc/nodes.inc | 7 +- devshop_projects/inc/tasks.inc | 63 +++++++++++++++--- devshop_projects/inc/ui.inc | 6 +- 6 files changed, 138 insertions(+), 31 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index acb4a0b10..359c682c4 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -1,4 +1,5 @@ publish_path; $git_remote = $platform->git_url; $git_branch = $platform->git_branch; + $create_branch = FALSE; + drush_log(print_r($platform, 1)); + //If is set site nid clone from old site and later create a new branch. + if (isset($platform->clone_nid)) { + $site_source = node_load($platform->clone_nid); + $git_branch = $site_source->git_branch; + $create_branch = TRUE; + } //Remove drupal_path to clone. if ($platform->drupal_path) { @@ -122,24 +131,19 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ } // Execute - if (!empty($command)){ - drush_log('[DEVSHOP] Running: ' . $command); + $output = _devshop_projects_git_execute($command, $root); - // @TODO: Create a d()->server->shell_cd_and_exec() function - // server->shell_exec() uses escapeshellcmd(), so we cannot cd_and_exec! - // So instead, this is the code from d()->server->shell_exec - // d()->server->shell_exec($cmd); - if (provision_is_local_host(d()->server->remote_host)) { - drush_shell_cd_and_exec($root, escapeshellcmd($command)); - } - else { - drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); - } + if ($create_branch) { + //Create branch. + $command = "git checkout -b $platform->git_branch"; + $output .= _devshop_projects_git_execute($command, $root); - $output = drush_shell_exec_output(); - drush_log('Shell Output: ' . implode("\n", $output) , 'warning'); - return $output; + //Push the branch + $command = "git push -u origin $platform->git_branch"; + $output .= _devshop_projects_git_execute($command, $root); } + + return $output; } /** @@ -248,9 +252,19 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // live. Let's create a site based off of this platform. drush_log('[DEVSHOP] Platform verified. Creating your site.'); - $site_node = devshop_projects_create_site($project, $platform, $platform->environment); - drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); + //Check if clone or create a new site. + if ($platform->clone_nid) { + $servers = hosting_get_servers('db'); + $args['target_platform'] = $platform->nid; + $args['new_uri'] = $platform->environment .'.'. $project->base_url; + $args['new_db_server'] = key($servers); + hosting_add_task($platform->clone_nid, 'clone', $args); + } + else { + $site_node = devshop_projects_create_site($project, $platform, $platform->environment); + drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); + } } // When a project is verified, queue a verification for all platforms @@ -409,3 +423,28 @@ function devshop_projects_hosting_platform_context_options(&$task) { } } + +/** + * Utility for execute git commands. + */ +function _devshop_projects_git_execute($command, $root) { + // Execute + if (!empty($command)){ + drush_log('[DEVSHOP] Running: ' . $command); + + // @TODO: Create a d()->server->shell_cd_and_exec() function + // server->shell_exec() uses escapeshellcmd(), so we cannot cd_and_exec! + // So instead, this is the code from d()->server->shell_exec + // d()->server->shell_exec($cmd); + if (provision_is_local_host(d()->server->remote_host)) { + drush_shell_cd_and_exec($root, escapeshellcmd($command)); + } + else { + drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); + } + + $output = drush_shell_exec_output(); + drush_log('Shell Output: ' . implode("\n", $output) , 'warning'); + return $output; + } +} diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 1c1aa2a61..1e55efe99 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -92,6 +92,12 @@ function devshop_projects_schema() { 'size' => 'big', 'not null' => FALSE, ), + 'clone_nid' => array( + 'type' => 'int', + 'not null' => FALSE, + 'default' => 0, + 'unsigned' => TRUE, + ), ), 'primary key' => array('object_nid'), ); @@ -197,5 +203,16 @@ function devshop_projects_update_7() { function devshop_projects_update_8() { $ret = array(); $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE environment environment VARCHAR(64) NOT NULL"); + + return $ret; +} + +/* + * Add clone_nid column to hosting_devshop_project_object. + */ +function devshop_projects_update_9() { + $ret = array(); + $ret[] = db_add_field($ret, 'hosting_devshop_project_object', 'clone_nid', array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'unsigned' => TRUE)); + return $ret; } diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a3945e5cf..3766a73e9 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -310,7 +310,7 @@ function devshop_project_status($node){ * See http://drupal.org/project/hosting_drush_aliases */ function devshop_projects_hosting_drush_aliases_name($node) { - if (isset($node->project_name)){ + if (isset($node->project_name)) { return $node->project_name .".". $node->project_environment; } } @@ -320,7 +320,6 @@ function devshop_projects_hosting_drush_aliases_name($node) { * Used by Wizard & "Create Platform" > Post Verify */ function devshop_projects_create_site($project_node, $platform_node, $platform_name, $db_server = NULL) { - global $user; // Create the site nodes diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 7dd99b7c4..8494fb06c 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -55,7 +55,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On Load: load this object's project and environment type if ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); + $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); return $data; } @@ -74,6 +74,11 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $data->git_branch = $node->git_branch; $data->drupal_path = $node->drupal_path; + //Site used for cloned the new site or platorm + if (isset($node->clone_nid)) { + $data->clone_nid = $node->clone_nid; + } + if ($op == 'insert') { drupal_write_record('hosting_devshop_project_object', $data); } diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 786e001b7..6270c3572 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -78,13 +78,51 @@ function devshop_projects_tasks_add_environment_to_form(&$form, $node, $descript */ function hosting_task_devshop_create_form($node) { $branch_options = array_combine($node->git_branches, $node->git_branches); - $form['branch'] = array( - '#title' => t('Branch'), - '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, re-verify your project.'), - '#type' => 'select', - '#options' => $branch_options, - '#required' => TRUE, - ); + + $site_nid = arg(3); + $site = NULL; + + if ($site_nid && is_numeric($site_nid)) { + //Sanitize data. + $site_nid = check_plain($site_nid); + + //Check is a valid project site. + if (!empty($node->project_objects['site'])) { + foreach($node->project_objects['site'] as $nid => $alias) { + if ($nid == $site_nid) { + $site = node_load($nid); + } + } + } + } + + if ($site) { + $form['branch'] = array( + '#type' => 'hidden', + '#value' => $site->git_branch, + ); + $form['clone_nid'] = array( + '#type' => 'hidden', + '#value' => $site->nid, + ); + $form['new_branch'] = array( + '#title' => t('New branch name'), + '#description' => t('Choose the new Git branch you with to use for this new platform.'), + '#type' => 'textfield', + '#size' => 60, + '#maxlength' => 128, + '#required' => TRUE, + ); + } + else { + $form['branch'] = array( + '#title' => t('Branch'), + '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, re-verify your project.'), + '#type' => 'select', + '#options' => $branch_options, + '#required' => TRUE, + ); + } // @TODO: Add "create branch" functionality. $form['platform_name'] = array( '#title' => t('Environment Name'), @@ -115,6 +153,10 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ if (!preg_match('!^[a-z0-9_]+$!', $params['platform_name'])) { form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); } + + if (isset($params['new_branch']) && !preg_match('!^[a-z0-9_]+$!', $params['platform_name'])) { + form_set_error('new_branch', t('The new branch name must contain only lowercase letters, numbers, and underscores.')); + } } /** @@ -144,7 +186,12 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $platform->publish_path = $project->code_path . '/' . $platform_name . '/' . $project->drupal_path; } - watchdog('debug', 'Form state: ' . print_r($form_state['values'],1)); + //Check if is need to clone and create a new branch + if (isset($form_state['values']['parameters']['clone_nid'])) { + $platform->clone_nid = $form_state['values']['parameters']['clone_nid']; + $platform->git_branch = $form_state['values']['parameters']['new_branch']; + $platform->old_branch = $branch; + } watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index c23e68493..add0aface 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -91,6 +91,7 @@ HTML; * Project Page Display */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { + global $user; // Check to see if this project is still in the wizard ctools_include('object-cache'); @@ -155,8 +156,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } - - $rows = array(); if (isset($node->project_objects['site'])) { foreach($node->project_objects['site'] as $nid => $env) { @@ -169,6 +168,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($site->site_status != -2) { $branch = "$site->git_branch"; $row[] = devshop_hosting_site_goto_link($site) . $branch; + $row[] = l(t('Clone environment'), 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid)))); $row[] = l('Site Dashboard', "node/$site->nid"); } else { @@ -200,7 +200,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'class' => 'project-environments', ), ); - global $user; + $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('attributes' => array('class' => 'create-new-environment'), 'query' => array('token' => drupal_get_token($user->uid)))); $node->content['sites']['table'] = array( '#type' => 'item', From 5cd581c9bfd6284fcb43c3ffbc46d6f7efbf9190 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 23 Apr 2013 11:55:47 -0400 Subject: [PATCH 0534/3476] check that we have at least one env --- devshop_projects/inc/create-wizard.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 99a6e8d7b..b4ac6b8f2 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -573,6 +573,10 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // Save envs to project $project->environments = $values['environments']; + // Reject if empty + if (count($project->environments) - 1 < 1){ + form_set_error('environments][NEW][title', t('You must add at least one environment.')); + } } /** From 17949ee98b299457e096b80db0c262bca7a2edbd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 23 Apr 2013 11:55:53 -0400 Subject: [PATCH 0535/3476] prevent php notice --- devshop_projects/devshop_projects.module | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a3945e5cf..d63687514 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -118,7 +118,11 @@ function devshop_projects_menu() { * Implements hook_devshop_project_settings */ function devshop_projects_devshop_project_settings($project_node = NULL){ - $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); + if (!empty($project_node->git_branches)){ + $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); + } else { + $branch_options = array(); + } $settings = array(); $settings['git_branch'] = array( From 519bbad1be59d7f495f061c3c00b31bb4a9ffa0e Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 23 Apr 2013 19:02:37 +0200 Subject: [PATCH 0536/3476] Change to secondary menu settings for log, projects and pull --- devshop_log/devshop_log.module | 7 ++++--- devshop_projects/devshop_projects.module | 10 ++++++++-- devshop_pull/devshop_pull.module | 7 ++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 20fc4d60d..a8ac1b988 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -60,13 +60,14 @@ function _devshop_log_access_callback($nid) { */ function devshop_log_menu() { - $items['admin/hosting/gitlog'] = array( - 'title' => 'DevShop Commit Log', + $items['admin/hosting/devshop/gitlog'] = array( + 'title' => 'Commit Log', 'description' => 'Configure Commit Log View', 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_git_log_settings'), - 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer hosting settings'), + 'tab_parent' => 'admin/hosting/devshop', + 'type' => MENU_LOCAL_TASK, ); $items['node/%/logs/commits'] = array( diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 18a14643e..ec3b1a904 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -50,16 +50,22 @@ function devshop_projects_perm() { */ function devshop_projects_menu() { //Settings page. - $items['admin/hosting/devshop_projects'] = array( - 'title' => 'Settings projects', + $items['admin/hosting/devshop'] = array( + 'title' => 'DevShop', 'description' => 'Default values for use in creation project', 'page callback' => 'drupal_get_form', 'page arguments' => array('devshop_projects_settings_form'), 'access arguments' => array('administer projects'), 'file' => 'admin.inc', 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', + 'type' => MENU_LOCAL_TASK, ); + $items['admin/hosting/devshop/projects'] = $items['admin/hosting/devshop']; + $items['admin/hosting/devshop/projects']['title'] = 'Settings projects'; + $items['admin/hosting/devshop/projects']['type'] = MENU_DEFAULT_LOCAL_TASK; + $items['admin/hosting/devshop/projects']['tab_parent'] = 'admin/hosting/devshop'; + $items['hosting/projects'] = array( 'title' => t('Projects'), 'description' => 'Display a list of all projects', diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 4448f7536..a89305a68 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -48,13 +48,14 @@ function devshop_pull_perm() { * Implementation of hook_menu() */ function devshop_pull_menu() { - $items['admin/hosting/devshop_pull'] = array( - 'title' => 'DevShop Pull Settings', + $items['admin/hosting/devshop/pull'] = array( + 'title' => 'Pull Settings', 'description' => 'Configure Pull Code URL callback ', 'page callback' => 'devshop_pull_settings_page', - 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer hosting settings'), 'file' => 'devshop_pull.settings.inc', + 'tab_parent' => 'admin/hosting/devshop', + 'type' => MENU_LOCAL_TASK, ); $items[DEVSHOP_PULL_CALLBACK_URL] = array( 'page callback' => 'devshop_pull_callback', From 02993bfb52951c632ed0ba40224daab1a75e09f8 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Tue, 23 Apr 2013 19:07:07 +0200 Subject: [PATCH 0537/3476] Change to secondary menu settings for log, projects and pull --- devshop_projects/devshop_projects.module | 2 +- devshop_pull/devshop_pull.module | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index ec3b1a904..b31d5aea1 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -62,7 +62,7 @@ function devshop_projects_menu() { ); $items['admin/hosting/devshop/projects'] = $items['admin/hosting/devshop']; - $items['admin/hosting/devshop/projects']['title'] = 'Settings projects'; + $items['admin/hosting/devshop/projects']['title'] = 'Projects'; $items['admin/hosting/devshop/projects']['type'] = MENU_DEFAULT_LOCAL_TASK; $items['admin/hosting/devshop/projects']['tab_parent'] = 'admin/hosting/devshop'; diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index a89305a68..ab421a93d 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -49,7 +49,7 @@ function devshop_pull_perm() { */ function devshop_pull_menu() { $items['admin/hosting/devshop/pull'] = array( - 'title' => 'Pull Settings', + 'title' => 'Pull', 'description' => 'Configure Pull Code URL callback ', 'page callback' => 'devshop_pull_settings_page', 'access arguments' => array('administer hosting settings'), From aafdfe1231fa449caaf85421bd8b5ef3697793b6 Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Thu, 25 Apr 2013 17:46:07 +0200 Subject: [PATCH 0538/3476] Issue #1979618 by jacintocapote: Fixed problem with deleted platform or sites --- devshop_projects/inc/forms.inc | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index a7164ef4a..8e8190289 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -156,24 +156,22 @@ function devshop_projects_form(&$node) { ); $settings = module_invoke_all('devshop_project_settings', $node); - foreach ($node->project_objects['platform'] as $nid => $environment_name){ + foreach ($node->project_objects['platform'] as $nid => $environment_name) { $platform = node_load($nid); - $form['settings'][$environment_name] = array( - '#tree' => TRUE, - '#title' => $name, - '#type' => 'fieldset', - '#theme' => 'devshop_projects_settings_table', - ); - //$form['settings'][$name]['git_branch'] = array( - // '#type' => 'select', - // '#title' => t('Branch'), - // '#default_value' => isset($platform->git_branch)? $platform->git_branch: NULL, - // '#options' => $branch_options, - // '#description' => t('Choose the branch that this platform should track.'), - //); - foreach ($settings as $setting_id => $setting){ - $form['settings'][$environment_name][$setting_id] = $setting; - $form['settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; + $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); + + if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { + $form['settings'][$environment_name] = array( + '#tree' => TRUE, + '#title' => $name, + '#type' => 'fieldset', + '#theme' => 'devshop_projects_settings_table', + ); + + foreach ($settings as $setting_id => $setting){ + $form['settings'][$environment_name][$setting_id] = $setting; + $form['settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; + } } } $form['#submit'] = array('devshop_projects_submit_settings'); From 4e533a4cc095afea0b0d5d1e6db3f891d8c7fac5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 25 Apr 2013 17:54:32 -0400 Subject: [PATCH 0539/3476] fixing bad code path --- devshop_projects/inc/create-wizard.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index b4ac6b8f2..95b52ffad 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -345,19 +345,17 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { } // Now that we have the project name, set the defaults for code path and project URL - $project->code_path = variable_get('devshop_projects_path', '/var/aegir/projects'). '/'. $project_name; + $project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $project_name; // Setup project url. $base_url = $_SERVER['SERVER_NAME']; $server = variable_get('devshop_project_master_url', $base_url); $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => strtolower($project_name), '@hostname' => $server)); - + $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); //Verify if is need to skip. $skip = variable_get('devshop_projects_skip_settings', TRUE); if ($skip) { - $project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'); - $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); $project->step = 'environments'; } } From 1168f4cfcaf8265d49c19b1c73e562bc82277440 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 25 Apr 2013 18:04:50 -0400 Subject: [PATCH 0540/3476] oops wrong featture :D --- hosting.feature.devshop.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting.feature.devshop.inc b/hosting.feature.devshop.inc index 34a6b7019..e193821d9 100644 --- a/hosting.feature.devshop.inc +++ b/hosting.feature.devshop.inc @@ -30,11 +30,11 @@ function devshop_hosting_hosting_feature() { 'module' => 'devshop_testing', 'group' => 'devshop', ); - $features['devshop_logs'] = array( + $features['devshop_log'] = array( 'title' => t('DevShop Logs'), 'description' => t('Provides error and commit logs to DevShop.'), 'status' => HOSTING_FEATURE_DISABLED, - 'module' => 'devshop_logs', + 'module' => 'devshop_log', 'group' => 'devshop', ); $features['devshop_live'] = array( From 661043eaf259c502c77022bfd5d177f75253d207 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 25 Apr 2013 18:08:31 -0400 Subject: [PATCH 0541/3476] renaming link to "Fork Environment" --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index add0aface..68cce3d3e 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -168,7 +168,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($site->site_status != -2) { $branch = "$site->git_branch"; $row[] = devshop_hosting_site_goto_link($site) . $branch; - $row[] = l(t('Clone environment'), 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid)))); + $row[] = l(t('Fork Environment'), 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid)))); $row[] = l('Site Dashboard', "node/$site->nid"); } else { From f8075be0cab6e98eaab407b081bcfb9a2b6e5477 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 25 Apr 2013 18:24:40 -0400 Subject: [PATCH 0542/3476] not isset!!! --- devshop_projects/devshop_projects.drush.inc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 359c682c4..f6450f6c5 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -90,9 +90,9 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $git_remote = $platform->git_url; $git_branch = $platform->git_branch; $create_branch = FALSE; - drush_log(print_r($platform, 1)); + //If is set site nid clone from old site and later create a new branch. - if (isset($platform->clone_nid)) { + if ($platform->clone_nid) { $site_source = node_load($platform->clone_nid); $git_branch = $site_source->git_branch; $create_branch = TRUE; @@ -238,8 +238,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { return; } - // If the project has a site already, trigger verification, then bail. - $sites = array_flip($project->project_objects['site']); + // If the platform has a site already, trigger verification, then bail. $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); if (!empty($sites)){ drush_log('[DEVSHOP] Platform already has a site.', 'notice'); From 2af0b6a1131e1afe93b91f8c72e31b06db4f18ab Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 25 Apr 2013 18:48:10 -0400 Subject: [PATCH 0543/3476] fixing environment name check: don't include deleted platforms --- devshop_projects/inc/tasks.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 6270c3572..116be92cc 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -144,7 +144,8 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ $params = $form_state['values']['parameters']; // Check for existing environments for this project. - $object_nid = db_result(db_query('SELECT object_nid FROM {hosting_devshop_project_object} WHERE project_nid = %d AND object_type = "%s" AND environment = "%s"', $params['project_nid'], 'platform', $params['platform_name'])); + $object_nid = db_result(db_query('SELECT object_nid FROM {hosting_devshop_project_object} d LEFT JOIN {hosting_platform} h ON d.object_nid = h.nid WHERE project_nid = %d AND object_type = "%s" AND environment = "%s" AND status != %d', $params['project_nid'], 'platform', $params['platform_name'], HOSTING_PLATFORM_DELETED)); + if ($object_nid) { form_set_error('platform_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['platform_name']))); } From 6e5d30cf68f3cb3c0d67cd9946d63d8bdc2d5a38 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 25 Apr 2013 19:11:44 -0400 Subject: [PATCH 0544/3476] reverting part of 265f309 --- devshop_pull/devshop_pull.module | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index ab421a93d..d1429bfd0 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -23,13 +23,13 @@ define('DEVSHOP_PULL_STATUS_INVALID_CODE', 3); // These are github's Webhook callback IPs. // This list grows occaisonally... define('DEVSHOP_PULL_DEFAULT_ALLOWED_IPS'," - 207.97.227.253 - 50.57.128.197 - 108.171.174.178 - 50.57.231.61 - 204.232.175.64 - 192.30.252.0 - 204.232.175.75 +207.97.227.253 +50.57.128.197 +108.171.174.178 +50.57.231.61 +204.232.175.64 +192.30.252.0 +204.232.175.75 "); // The base URL to use for the Post Commit callback. From f9b3d1a4c0ba8375a423111f9f837a5a882425dd Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 26 Apr 2013 14:43:18 +0200 Subject: [PATCH 0545/3476] Jacinto. Menu with ctools --- devshop_projects/inc/ui.inc | 41 ++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index add0aface..71ca7d64f 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -165,29 +165,50 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { continue; } $row = array(); + $row_menu = array(); if ($site->site_status != -2) { $branch = "$site->git_branch"; $row[] = devshop_hosting_site_goto_link($site) . $branch; - $row[] = l(t('Clone environment'), 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid)))); - $row[] = l('Site Dashboard', "node/$site->nid"); + $row_menu[] = array( + 'title' => t('Clone environment'), + 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))) + ); + $row_menu[] = array( + 'title' => t('Site Dashboard'), + 'href' => "node/$site->nid", + ); } else { // Site if deleted, just show the platform name $row[] = $platform->title; $row[] = ''; } - $row[] = l('Platform Dashboard', "node/$site->platform"); - - if (module_exists('devshop_log')){ - $row[] = l('Commit Log', "node/$site->nid/logs/commits"); + $row_menu[] = array( + 'title' => t('Platform Dashboard'), + 'href' => "node/$site->platform", + ); + + if (module_exists('devshop_log')) { + $row_menu[] = array( + 'title' => t('Commit Log'), + 'href' => "node/$site->nid/logs/commits", + ); } - if (module_exists('hosting_logs')){ - $row[] = l('Error Log', "node/$site->nid/logs/errors"); + if (module_exists('hosting_logs')) { + $row_menu[] = array( + 'title' => t('Error Log'), + 'href' => "node/$site->nid/logs/errors", + ); } - if (module_exists('hosting_filemanager')){ - $row[] = l('Files', "node/$site->nid/files/platform"); + if (module_exists('hosting_filemanager')) { + $row_menu[] = array( + 'title' => t('Files'), + 'href' => "node/$site->nid/files/platform", + ); } + $row[] = theme('ctools_dropdown', t('Actions'), $row_menu); $rows[] = $row; +// $rows[] = array($platform->title, theme('ctools_dropdown', $platform->title, $row)); } $header = array(); $table = theme('table', $header, $rows); From c14cfd96e1751743b0aae8aeac87cce19bdfb77f Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 26 Apr 2013 20:42:22 +0200 Subject: [PATCH 0546/3476] Fixed a problem with fork environment --- devshop_projects/devshop_projects.drush.inc | 16 ++++++++-------- devshop_projects/inc/create-wizard.inc | 1 + devshop_projects/inc/nodes.inc | 19 ++++++++++++++----- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index f6450f6c5..b86b1cc51 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -91,20 +91,13 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $git_branch = $platform->git_branch; $create_branch = FALSE; - //If is set site nid clone from old site and later create a new branch. - if ($platform->clone_nid) { - $site_source = node_load($platform->clone_nid); - $git_branch = $site_source->git_branch; - $create_branch = TRUE; - } - //Remove drupal_path to clone. if ($platform->drupal_path) { $root = str_replace($platform->drupal_path, '', $root); } // Check if a repo exists - if (!is_dir($root) || !drush_shell_cd_and_exec($root, 'git status')){ + if (!is_dir($root) || !drush_shell_cd_and_exec($root, 'git status')) { drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); // Build the command string @@ -112,6 +105,13 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ if ($git_branch) { $command .= " --branch $git_branch"; } + + //If is set site nid clone from old site and later create a new branch. + if ($platform->clone_nid) { + $site_source = node_load($platform->clone_nid); + $git_branch = $site_source->git_branch; + $create_branch = TRUE; + } } // If the platform has been verified and has a branch and git url else { diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 95b52ffad..adb3163fe 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -159,6 +159,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { $node->drupal_path = $project->drupal_path; $node->uid = $user->uid; $node->status = 1; + $node->no_verify = TRUE; node_save($node); // Create the site nodes diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 8494fb06c..4e92aeaae 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -41,9 +41,9 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // node yet in devshop_projects_load(). $settings = module_invoke_all('devshop_project_settings', $node); - foreach ($settings as $setting_name => $setting){ + foreach ($settings as $setting_name => $setting) { $setting_node_type = $setting['#node_type']; - foreach ((array) $node->project_objects[$setting_node_type] as $nid => $environment_name){ + foreach ((array) $node->project_objects[$setting_node_type] as $nid => $environment_name) { $object = node_load($nid); $node->settings[$environment_name][$setting_name] = $object->{$setting_name}; } @@ -51,16 +51,25 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { } // Project-enabled platforms get - if ($node->type == 'platform' || $node->type == 'site'){ - + if ($node->type == 'platform' || $node->type == 'site') { // On Load: load this object's project and environment type - if ($op == 'load'){ + if ($op == 'load') { $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); return $data; } // On insert or update, insert records saving this objects project and environment if ($op == 'update' || $op == 'insert') { + //Special case for migrate site. Is need copy the information to the new site. + if ($op == 'insert' && $node->type == 'site' && $node->import && !$node->verified) { + $platform = node_load($node->platform); + $node->project = $platform->project; + $node->project_nid = $platform->project_nid; + $node->environment = $platform->environment; + $node->git_branch = $platform->git_branch; + $node->drupal_path = $platform->drupal_path; + $node->clone_nid = $platform->clone_nid; + } if (!empty($node->project) && !empty($node->environment)) { // Get the project node by name. $project = hosting_context_load($node->project); From 7ffeadf340bc38f1ad1e87ef0096a342addae42d Mon Sep 17 00:00:00 2001 From: Jacinto Capote Robles Date: Fri, 26 Apr 2013 21:03:37 +0200 Subject: [PATCH 0547/3476] Issue #1897932 Removed from front-end --- devshop_projects/devshop_projects.drush.inc | 2 +- devshop_projects/inc/tasks.inc | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index b86b1cc51..848d3d114 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -33,7 +33,7 @@ function drush_devshop_projects_pre_hosting_task() { $task->options['update'] = $task->task_args['update']; $task->options['revert'] = !empty($task->task_args['revert']); $task->options['cache'] = $task->task_args['cache']; - $task->options['force'] = $task->task_args['force']; + $task->options['force'] = FALSE; } // Commit diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 116be92cc..b487b3cbc 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -285,12 +285,6 @@ function hosting_task_devshop_pull_form($node) { '#type' => 'checkbox', '#default_value' => 1, ); - $form['force'] = array( - '#title' => t('Force'), - '#description' => t('Force to execute update, cache if are enabled'), - '#type' => 'checkbox', - '#default_value' => 1, - ); // Add validator for environments //$form['#validate'] = array('hosting_task_devshop_pull_form'); From 752bf1872380cc1b20ff2012d4037e4c9fcb46be Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 13:20:19 -0400 Subject: [PATCH 0548/3476] expose most useful environment links. reorganize code. --- devshop_projects/inc/ui.inc | 64 ++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index b285cb007..cb2355fbb 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -159,56 +159,46 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $rows = array(); if (isset($node->project_objects['site'])) { foreach($node->project_objects['site'] as $nid => $env) { + $site = node_load($nid); $platform = node_load($site->platform); - if ($site->site_status == -2 && $platform->platform_status == -2) { + + // Skip this if it is not enabled. + if ($site->site_status != HOSTING_SITE_ENABLED && $platform->platform_status != HOSTING_PLATFORM_ENABLED) { continue; } + $row = array(); - $row_menu = array(); - if ($site->site_status != -2) { - $branch = "$site->git_branch"; - $row[] = devshop_hosting_site_goto_link($site) . $branch; - $row_menu[] = array( - 'title' => t('Fork environment'), - 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))) - ); - $row_menu[] = array( - 'title' => t('Site Dashboard'), - 'href' => "node/$site->nid", - ); - } - else { - // Site if deleted, just show the platform name - $row[] = $platform->title; - $row[] = ''; - } - $row_menu[] = array( - 'title' => t('Platform Dashboard'), - 'href' => "node/$site->platform", - ); + $row[] = "$site->git_branch";; + $row[] = devshop_hosting_site_goto_link($site); if (module_exists('devshop_log')) { - $row_menu[] = array( - 'title' => t('Commit Log'), - 'href' => "node/$site->nid/logs/commits", - ); + $row[] =l(t('Commits'), "node/$site->nid/logs/commits"); } if (module_exists('hosting_logs')) { - $row_menu[] = array( - 'title' => t('Error Log'), - 'href' => "node/$site->nid/logs/errors", - ); + $row[] = l(t('Errors'), "node/$site->nid/logs/errors"); } if (module_exists('hosting_filemanager')) { - $row_menu[] = array( - 'title' => t('Files'), - 'href' => "node/$site->nid/files/platform", - ); + $row[] = l(t('Files'), "node/$site->nid/files/platform"); } - $row[] = theme('ctools_dropdown', t('Actions'), $row_menu); + + // Create actions dropdown. + $actions = array(); + $actions[] = array( + 'title' => t('Fork environment'), + 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))) + ); + $actions[] = array( + 'title' => t('Site Dashboard'), + 'href' => "node/$site->nid", + ); + $actions[] = array( + 'title' => t('Platform Dashboard'), + 'href' => "node/$site->platform", + ); + + $row[] = theme('ctools_dropdown', t('Actions'), $actions); $rows[] = $row; -// $rows[] = array($platform->title, theme('ctools_dropdown', $platform->title, $row)); } $header = array(); $table = theme('table', $header, $rows); From 0af85c780afb8eb404fb06769a3b0e3824805bc9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 14:16:48 -0400 Subject: [PATCH 0549/3476] adding site tasks, styling environment name --- devshop_projects/inc/ui.inc | 43 +++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index cb2355fbb..fc3dd9127 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -156,6 +156,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } + // Environments $rows = array(); if (isset($node->project_objects['site'])) { foreach($node->project_objects['site'] as $nid => $env) { @@ -169,8 +170,15 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $row = array(); - $row[] = "$site->git_branch";; - $row[] = devshop_hosting_site_goto_link($site); + $row[] = "$site->environment"; + + if ($site->site_status == HOSTING_SITE_DISABLED){ + $row[] = devshop_hosting_site_goto_link($site) . '' . t('Disabled') . ''; + } else { + $row[] = devshop_hosting_site_goto_link($site); + } + $row[] = "$site->git_branch"; + if (module_exists('devshop_log')) { $row[] =l(t('Commits'), "node/$site->nid/logs/commits"); @@ -188,13 +196,40 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'title' => t('Fork environment'), 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))) ); + + + // Aegir Tasks + $site_tasklist = hosting_task_fetch_tasks($site->nid); + $site_actions = array('flush_cache', 'login_reset', 'backup'); + + if (!variable_get('hosting_require_disable_before_delete', TRUE)){ + $site_actions[] = 'delete'; + } else { + if ($site->site_status == HOSTING_SITE_DISABLED){ + $site_actions[] = 'enable'; + $site_actions[] = 'delete'; + } else { + $site_actions[] = 'disable'; + } + } + foreach ($site_actions as $task_name) { + $actions[] = array( + 'title' => $site_tasklist[$task_name]['title'], + 'href' => sprintf("node/%d/%s_%s", $site->nid, $site->type, $task_name), + 'query' => array( + 'token' => drupal_get_token($user->uid), + 'destination' => "node/$node->nid", + ), + ); + } + $actions[] = array( 'title' => t('Site Dashboard'), 'href' => "node/$site->nid", ); $actions[] = array( - 'title' => t('Platform Dashboard'), - 'href' => "node/$site->platform", + 'title' => t('Platform Dashboard'), + 'href' => "node/$site->platform", ); $row[] = theme('ctools_dropdown', t('Actions'), $actions); From 99f925f97dae9469dbd3aa7b2eb0a12703ac7b79 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 14:16:56 -0400 Subject: [PATCH 0550/3476] styling environment name --- devshop_projects/devshop-style.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 3e01902a4..134cfa759 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -22,6 +22,20 @@ span.branch margin: 0px 5px 5px 0px; text-decoration:none; } + +table * span.environment { + color:#777777; + font-weight:bold; + padding:0px 6px; + margin: 0px 5px 5px 0px; + border: 1px solid #dfdfdf; + -moz-border-radius:3px; + -webkit-border-radius:3px; + display:inline-block; + background-color:#fefefe; + font-size: 1.5em; +} + body.aegir #page div.node div.content .branches-list div.form-item { padding-bottom: 2em; } From a4bb7e45d8322f07783fc5105a683f68025fbac6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:04:12 -0400 Subject: [PATCH 0551/3476] - adding backup restore to environment tasks - Only show links to site and platform dashboard if you are a hosting admin --- devshop_projects/inc/ui.inc | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index fc3dd9127..9a19af5e9 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -200,8 +200,12 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Aegir Tasks $site_tasklist = hosting_task_fetch_tasks($site->nid); - $site_actions = array('flush_cache', 'login_reset', 'backup'); - + $site_tasklist['restore']['title'] = t('Restore Backups'); + + // The actions we want + $site_actions = array('flush_cache', 'login_reset', 'backup', 'restore'); + + // Add disable or delete task based on hosting variable. if (!variable_get('hosting_require_disable_before_delete', TRUE)){ $site_actions[] = 'delete'; } else { @@ -212,6 +216,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $site_actions[] = 'disable'; } } + + // Build links to tasks foreach ($site_actions as $task_name) { $actions[] = array( 'title' => $site_tasklist[$task_name]['title'], @@ -223,14 +229,17 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } - $actions[] = array( - 'title' => t('Site Dashboard'), - 'href' => "node/$site->nid", - ); - $actions[] = array( - 'title' => t('Platform Dashboard'), - 'href' => "node/$site->platform", - ); + // Grant access to Aegir pages + if (user_access('administer hosting')){ + $actions[] = array( + 'title' => t('Site Dashboard'), + 'href' => "node/$site->nid", + ); + $actions[] = array( + 'title' => t('Platform Dashboard'), + 'href' => "node/$site->platform", + ); + } $row[] = theme('ctools_dropdown', t('Actions'), $actions); $rows[] = $row; From e5ff4e15c9ec988c40dc934108bd46b6305a3367 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:04:23 -0400 Subject: [PATCH 0552/3476] fixing bad styling of ctools dropdown --- devshop_projects/devshop-style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 134cfa759..3783e64b3 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -36,6 +36,10 @@ table * span.environment { font-size: 1.5em; } +html.js div.ctools-dropdown div.ctools-dropdown-container { + width: 186px; +} + body.aegir #page div.node div.content .branches-list div.form-item { padding-bottom: 2em; } From 21ee0c9f329ef6eb5cd51726ba6afffe8ceae618 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:07:36 -0400 Subject: [PATCH 0553/3476] Check if task_permitted --- devshop_projects/inc/ui.inc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 9a19af5e9..a1f040916 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -219,14 +219,16 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Build links to tasks foreach ($site_actions as $task_name) { - $actions[] = array( - 'title' => $site_tasklist[$task_name]['title'], - 'href' => sprintf("node/%d/%s_%s", $site->nid, $site->type, $task_name), - 'query' => array( - 'token' => drupal_get_token($user->uid), - 'destination' => "node/$node->nid", - ), - ); + if ($site_tasklist[$task_name]['task_permitted']){ + $actions[] = array( + 'title' => $site_tasklist[$task_name]['title'], + 'href' => sprintf("node/%d/%s_%s", $site->nid, $site->type, $task_name), + 'query' => array( + 'token' => drupal_get_token($user->uid), + 'destination' => "node/$node->nid", + ), + ); + } } // Grant access to Aegir pages From d7cbc104de72a7c51b72bcbc41cec6b58623d5b2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:12:25 -0400 Subject: [PATCH 0554/3476] Dialog popups --- devshop_projects/inc/ui.inc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index a1f040916..2fcfe9bd2 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -194,7 +194,10 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $actions = array(); $actions[] = array( 'title' => t('Fork environment'), - 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))) + 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))), + 'attributes' => array( + 'class' => 'hosting-button-dialog', + ), ); @@ -227,6 +230,9 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'token' => drupal_get_token($user->uid), 'destination' => "node/$node->nid", ), + 'attributes' => array( + 'class' => $site_tasklist[$task_name]['dialog']? 'hosting-button-dialog': '', + ), ); } } @@ -258,7 +264,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ), ); - $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('attributes' => array('class' => 'create-new-environment'), 'query' => array('token' => drupal_get_token($user->uid)))); + $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('attributes' => array('class' => 'create-new-environment hosting-button-dialog'), 'query' => array('token' => drupal_get_token($user->uid)))); $node->content['sites']['table'] = array( '#type' => 'item', '#value' => $table, From d0dded37b95bbb43fba6b6fbf1bb15716bc1ed4f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:21:17 -0400 Subject: [PATCH 0555/3476] Add "refresh branches" link to create new environment for --- devshop_projects/inc/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 116be92cc..8c651c88b 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -117,7 +117,7 @@ function hosting_task_devshop_create_form($node) { else { $form['branch'] = array( '#title' => t('Branch'), - '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, re-verify your project.'), + '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, You can !link', array('!link' => l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link modalframe-exclude', 'target' => '_parent'), 'query' => array('token' => drupal_get_token($user->uid)))))), '#type' => 'select', '#options' => $branch_options, '#required' => TRUE, From 4756b771bb0851300855c61f3549c9fa5db83419 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:35:38 -0400 Subject: [PATCH 0556/3476] better looping of environments --- devshop_projects/inc/ui.inc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 2fcfe9bd2..1fdfa5ee0 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -158,11 +158,14 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Environments $rows = array(); - if (isset($node->project_objects['site'])) { - foreach($node->project_objects['site'] as $nid => $env) { + $site_nids = array_flip($node->project_objects['site']); + $platform_nids = array_flip($node->project_objects['site']); + + if (isset($platform_nids)) { + foreach($platform_nids as $env => $nid) { - $site = node_load($nid); - $platform = node_load($site->platform); + $site = node_load($site_nids[$env]); + $platform = node_load($nid); // Skip this if it is not enabled. if ($site->site_status != HOSTING_SITE_ENABLED && $platform->platform_status != HOSTING_PLATFORM_ENABLED) { From fb4acb5bfc11759d18c6a18f9fc5a2d4b920d563 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:36:03 -0400 Subject: [PATCH 0557/3476] renaming form to "environment name" --- devshop_projects/inc/tasks.inc | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 8c651c88b..baf7bde0b 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -97,6 +97,11 @@ function hosting_task_devshop_create_form($node) { } if ($site) { + $form['branch_source'] = array( + '#type' => 'item', + '#title' => t('Fork from'), + '#value' => t('!env environment on branch !branch', array('!env' => "$site->environment", '!branch' => "$site->git_branch")), + ); $form['branch'] = array( '#type' => 'hidden', '#value' => $site->git_branch, @@ -124,7 +129,7 @@ function hosting_task_devshop_create_form($node) { ); } // @TODO: Add "create branch" functionality. - $form['platform_name'] = array( + $form['environment_name'] = array( '#title' => t('Environment Name'), '#type' => 'textfield', '#description' => t('Enter a system name for your environment. For consistency, you should make this match the branch name.'), @@ -144,18 +149,18 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ $params = $form_state['values']['parameters']; // Check for existing environments for this project. - $object_nid = db_result(db_query('SELECT object_nid FROM {hosting_devshop_project_object} d LEFT JOIN {hosting_platform} h ON d.object_nid = h.nid WHERE project_nid = %d AND object_type = "%s" AND environment = "%s" AND status != %d', $params['project_nid'], 'platform', $params['platform_name'], HOSTING_PLATFORM_DELETED)); + $object_nid = db_result(db_query('SELECT object_nid FROM {hosting_devshop_project_object} d LEFT JOIN {hosting_platform} h ON d.object_nid = h.nid WHERE project_nid = %d AND object_type = "%s" AND environment = "%s" AND status != %d', $params['project_nid'], 'platform', $params['environment_name'], HOSTING_PLATFORM_DELETED)); if ($object_nid) { - form_set_error('platform_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['platform_name']))); + form_set_error('environment_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['environment_name']))); } // Check for illegal chars - if (!preg_match('!^[a-z0-9_]+$!', $params['platform_name'])) { + if (!preg_match('!^[a-z0-9_]+$!', $params['environment_name'])) { form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); } - if (isset($params['new_branch']) && !preg_match('!^[a-z0-9_]+$!', $params['platform_name'])) { + if (isset($params['new_branch']) && !preg_match('!^[a-z0-9_]+$!', $params['environment_name'])) { form_set_error('new_branch', t('The new branch name must contain only lowercase letters, numbers, and underscores.')); } } @@ -168,23 +173,23 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ function hosting_task_devshop_create_form_submit($form, &$form_state) { $project = node_load($form_state['values']['nid']); - $platform_name = $form_state['values']['parameters']['platform_name']; + $environment_name = $form_state['values']['parameters']['environment_name']; $branch = $form_state['values']['parameters']['branch']; $servers = hosting_get_servers('http'); $server = variable_get('devshop_projects_default_web_server', key($servers)); // hosting_platform fields $platform = new stdClass; - $platform->title = $project->title . '_' . $platform_name; - $platform->publish_path = $project->code_path . '/' . $platform_name; + $platform->title = $project->title . '_' . $environment_name; + $platform->publish_path = $project->code_path . '/' . $environment_name; $platform->web_server = $server; $platform->git_branch = $branch; $platform->project = $project->title; - $platform->environment = $platform_name; + $platform->environment = $environment_name; $platform->drupal_path = $project->drupal_path; if ($project->drupal_path) { - $platform->publish_path = $project->code_path . '/' . $platform_name . '/' . $project->drupal_path; + $platform->publish_path = $project->code_path . '/' . $environment_name . '/' . $project->drupal_path; } //Check if is need to clone and create a new branch From ea54d83b41209ff52f990d579bbdc38f4e989d62 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:36:25 -0400 Subject: [PATCH 0558/3476] style any span class=environment --- devshop_projects/devshop-style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 3783e64b3..5b5219f33 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -23,6 +23,7 @@ span.branch text-decoration:none; } +span.environment, table * span.environment { color:#777777; font-weight:bold; From 96979ebd826797917a17c73e60ec27049806b7dc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:37:35 -0400 Subject: [PATCH 0559/3476] better description --- devshop_projects/inc/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index baf7bde0b..68c6224be 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -20,7 +20,7 @@ function devshop_projects_hosting_tasks() { ); $tasks['project']['devshop-create'] = array( 'title' => t('Create New Environment'), - 'description' => t('Creates a new site & platform within this project.'), + 'description' => t('Creates a new environment within this project.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, 'hidden' => TRUE, From 8d69cc479634e30d5d53ece2dc49f2ac4dba4442 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 15:58:48 -0400 Subject: [PATCH 0560/3476] only load enabled sites & platforms --- devshop_projects/inc/nodes.inc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 4e92aeaae..2f018f01d 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -217,9 +217,14 @@ function devshop_projects_load($node) { $objects = array(); while($project_object = db_fetch_object($query)) { // Only load if site or platform is enabled. - // @TODO: We should load all. Let other code hide by status. - $site_node = node_load($project_object->object_nid); - $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; + $object_node = node_load($project_object->object_nid); + + if (($object_node->type == 'site' && $object_node->site_status == HOSTING_SITE_ENABLED) || + ($object_node->type == 'platform' && $object_node->platform_status == HOSTING_PLATFORM_ENABLED) + ){ + $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; + + } } $additions['project_objects'] = $objects; From d2b8eb3586e159a9d13db697e418a03c52312b49 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 16:05:48 -0400 Subject: [PATCH 0561/3476] adding title attribute to branch --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 1fdfa5ee0..4df77d597 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -180,7 +180,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } else { $row[] = devshop_hosting_site_goto_link($site); } - $row[] = "$site->git_branch"; + $row[] = "$site->git_branch"; if (module_exists('devshop_log')) { From 9cbfb9606ef65a8771f73fa8c5ada341546f119f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 16:05:57 -0400 Subject: [PATCH 0562/3476] adding cursor: help to branch display --- devshop_projects/devshop-style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 5b5219f33..fc1377613 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -23,6 +23,10 @@ span.branch text-decoration:none; } +span.branch.environment-branch { + cursor: help; +} + span.environment, table * span.environment { color:#777777; From 28c3608c9cb4186f558fa7b44c9f59045ece2bdb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 16:10:20 -0400 Subject: [PATCH 0563/3476] Adding title attribute to settings forms --- devshop_projects/inc/create-wizard.inc | 3 ++- devshop_projects/inc/forms.inc | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index adb3163fe..bb71e983d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -522,7 +522,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // Add environment settings form elements foreach ($settings as $setting_id => $setting){ $form['environments'][$env][$setting_id] = $setting; - $form['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; + $form['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; + $form['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } //Now add button. diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 8e8190289..1450a586b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -171,6 +171,7 @@ function devshop_projects_form(&$node) { foreach ($settings as $setting_id => $setting){ $form['settings'][$environment_name][$setting_id] = $setting; $form['settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; + $form['settings'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } } } From ffeeba64152fdaa35f308ce838eed6516b802beb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 16:10:32 -0400 Subject: [PATCH 0564/3476] modifying setting description --- devshop_pull/devshop_pull.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index d1429bfd0..5913214f4 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -91,7 +91,7 @@ function devshop_pull_devshop_project_settings(){ '#title' => t('Pull on Commit'), '#node_type' => 'platform', '#type' => 'checkbox', - '#description' => t('When DevShop receives commit notification, run Pull Code task.'), + '#description' => t('When DevShop receives commit notification, Pull Code.'), ), // Pull reset won't work until we are storing the settings in the project drush context. //'pull_reset' => array( From 6b029d4e4dd37ad8a2c96ec3b01a3f39f32199a4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 29 Apr 2013 16:16:24 -0400 Subject: [PATCH 0565/3476] making separate logs item hidden, and adding description of visible error logs. --- devshop_log/devshop_log.module | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index a8ac1b988..623e1e837 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -22,14 +22,16 @@ function devshop_log_devshop_project_settings($project_node = NULL){ if (module_exists('hosting_logs')){ return array( 'logs_enabled' => array( - '#title' => t('Separate Error Logs'), + '#title' => '', '#node_type' => 'site', - '#type' => 'checkbox', + '#type' => 'hidden', + '#value' => TRUE, ), 'logs_available' => array( '#title' => t('Visible Error Logs'), '#node_type' => 'site', '#type' => 'checkbox', + '#description' => t('Make error logs available at http://mysite/error.log'), ), ); } From ca85ca651587092bf0f8fd716b1a64e76ccfdf30 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 30 Apr 2013 15:33:13 -0400 Subject: [PATCH 0566/3476] use "create project" access arguments --- devshop_projects/devshop_projects.module | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index b31d5aea1..ef6af9786 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -85,8 +85,7 @@ function devshop_projects_menu() { 'title callback' => 'check_plain', 'page callback' => 'devshop_projects_add', 'page arguments' => array(3), - 'access callback' => 'node_access', - 'access arguments' => array('create', 'project'), + 'access arguments' => array('create project'), 'description' => 'Start a new Drupal website project.', 'file' => 'create-wizard.inc', 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', From 36a957fa3912c91d8d263f4efac42749609a540d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 30 Apr 2013 15:39:39 -0400 Subject: [PATCH 0567/3476] adding hook_Access() --- devshop_projects/devshop_projects.module | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index ef6af9786..a19316343 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -45,6 +45,26 @@ function devshop_projects_perm() { ); } +/** + * Implementation of hook_access() + */ +function devshop_projects_access($op, $node, $account) { + switch ($op) { + case 'create': + return user_access('create project', $account); + break; + case 'update': + return user_access('edit project', $account); + break; + case 'delete': + return user_access('delete projects', $account); + break; + case 'view': + return user_access('view project', $account); + break; + } +} + /** * Implementation of hook_menu() */ From a32f75ba465930599522ee67c7b13b55aaa2a6a1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 30 Apr 2013 15:48:03 -0400 Subject: [PATCH 0568/3476] bad node info? --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2f018f01d..2f6476ede 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -12,7 +12,7 @@ */ function devshop_projects_node_info() { $types["project"] = array( - "type" => 'devshop_project', + "type" => 'project', "name" => 'DevShop Project', "module" => 'devshop_projects', "has_title" => TRUE, From 3f41b8b5be12466072fd113b16ecc821b5f5b5f9 Mon Sep 17 00:00:00 2001 From: Herman van Rink Date: Fri, 3 May 2013 12:52:48 +0200 Subject: [PATCH 0569/3476] Fixed update hook, removing old code. --- devshop_pull/devshop_pull.install | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index ffe61f196..176966a83 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -84,18 +84,17 @@ function devshop_pull_uninstall() { } /** - * Adds project_nid column to our table. + * no-op: code fixed in devshop_pull_update_6001() */ function devshop_pull_update_6000(){ $ret = array(); - db_add_field($ret, 'hosting_devshop_pull_platforms', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '')); return $ret; } /** - * Adds project_nid column to our table. + * Add last_pull_ip column to our hosting_devshop_pull_projects table. */ function devshop_pull_update_6001(){ $ret = array(); db_add_field($ret, 'hosting_devshop_pull_projects', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '', 'length' => 15)); return $ret; -} \ No newline at end of file +} From 37bddbe52245a93887e657796c755d48bcdf8784 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 7 May 2013 20:25:47 -0400 Subject: [PATCH 0570/3476] adding @todo --- devshop_projects/inc/create-wizard.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index bb71e983d..36122e534 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -54,6 +54,7 @@ function devshop_projects_create_wizard($step = NULL){ } // Check verification status + // @TODO: We should be able to get the error messages... $project_node = node_load($project->project_nid); if (!empty($project_node->nid)){ $tasks = hosting_task_fetch_tasks($project_node->nid); From 72470897a026c897c237b58a4e5e4a5f8d734cb2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 7 May 2013 21:39:53 -0400 Subject: [PATCH 0571/3476] setting attribute of form --- devshop_projects/inc/create-wizard.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 36122e534..417a29e8b 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -524,7 +524,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { foreach ($settings as $setting_id => $setting){ $form['environments'][$env][$setting_id] = $setting; $form['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; - $form['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['environments'][$env][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['environments'][$env][$setting_id]['#description'] = ''; } //Now add button. From 56d1458851a4a7cce5993459d716c40fa407b56a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 8 May 2013 20:49:38 +0000 Subject: [PATCH 0572/3476] save an alias on all domains. --- devshop_live/inc/nodes.inc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/devshop_live/inc/nodes.inc b/devshop_live/inc/nodes.inc index b9c0840d1..e067b8c16 100644 --- a/devshop_live/inc/nodes.inc +++ b/devshop_live/inc/nodes.inc @@ -13,15 +13,13 @@ function devshop_live_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { if ($node->type == 'project') { if (in_array($op, array('insert', 'update'))) { - //Load all information + // Load all information... @TODO: Would it be better to use devshop_project_load() directly? $node_p = node_load($node->nid); - - if ($node->live_domain && !isset($node_p->project_objects['site'])) { - $live_nid = array_search('live', $node_p->project_objects['site']); - - if ($live_nid) { - $live_node = node_load($live_nid); - devshop_live_add_domain($live_node, $node->live_domain); + if ($node->live_domain && isset($node_p->project_objects['site'])) { + foreach ($node_p->project_objects['site'] as $nid => $env){ + $site = node_load($nid); + drupal_set_message('looping for '.$env .$site->nid); + devshop_live_add_domain($site, $env . '.' . $node->live_domain); } } } From 894cd54444f2749b402071d7595524b4996c40db Mon Sep 17 00:00:00 2001 From: Herman van Rink Date: Thu, 9 May 2013 20:26:43 +0200 Subject: [PATCH 0573/3476] Avoid a notice --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2f6476ede..966db1c06 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -160,7 +160,7 @@ function devshop_projects_insert($node) { */ function devshop_projects_update($node) { - if (!$node->no_verify) { + if (!isset($node->no_verify) || $node->no_verify == FALSE) { hosting_add_task($node->nid, 'verify'); } $data = array(); From 7e2a9c4c1a6aafffeeb2de33ea9548c553559578 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 12:05:34 -0400 Subject: [PATCH 0574/3476] Improving "wait" screens --- devshop.css | 5 ----- devshop_projects/devshop-style.css | 6 ++++++ devshop_projects/inc/create-wizard.inc | 2 -- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/devshop.css b/devshop.css index 0575f4946..ce92f3a46 100644 --- a/devshop.css +++ b/devshop.css @@ -51,11 +51,6 @@ textarea#rsa { display: none; } -p.wait { - text-align: center; - padding: 30px 0px 0px; -} - .form-submit { margin-top: 10px; } diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index fc1377613..4d179a6b0 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -91,3 +91,9 @@ form .sync-envs div.form-item-labeled label { .path-hosting-projects-add-environments #right { display: none; } + +p.wait { + text-align: center; + padding: 32px 0px 52px; + background: bottom center no-repeat url(images/loading.gif); +} \ No newline at end of file diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 417a29e8b..c7eb5974d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -881,8 +881,6 @@ function devshop_form_reloader(&$form, $type = 'platform'){ $form[$element_id] = array( '#type' => 'item', '#value' => '', - '#prefix' => '
', - '#suffix' => '
', '#weight' => 10 ); $settings['devshopReload'] = array( From 7000bbf9fb6390e3ebecf3e76d07a0fd9bd38531 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 12:23:09 -0400 Subject: [PATCH 0575/3476] Also reload if there was an error --- devshop_projects/inc/create-wizard.inc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index c7eb5974d..c7359280d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -62,15 +62,16 @@ function devshop_projects_create_wizard($step = NULL){ if (isset($tasks['verify']['nid'])){ } + dsm($tasks, 'tasks'); $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; $project->verify_task_nid = $tasks['verify']['nid']; // If project verification failed, we need to ask for a new git url. if ($project->verify_task_status === HOSTING_TASK_ERROR && !empty($project_node->nid)){ - drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); + //drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); // If not on the first step, go to it. if ($step != current(array_keys($form_info['order']))){ - drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); + //drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); } } @@ -443,11 +444,9 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); - if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) { - // @TODO: Detect and show errors when they occur. - // @TODO: Pretty this up, figure out how to maybe, display the task in the body? $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; + $project->no_next = TRUE; $form['note'] = array( '#type' => 'markup', @@ -457,8 +456,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#type' => 'value', '#value' => TRUE, ); - $project->no_next = TRUE; - // Add code to reload the page when complete. devshop_form_reloader($form, 'project'); return; @@ -468,13 +465,14 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $type = 'error'; $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, $type)); drupal_set_message($error, 'error'); - $note = '

'. t('Something Failed. Please check the error message, and retry verification.') . '

'; + + $note = '

'. $error . t('Something Failed. Please check the error message, and retry verification.') . '

'; + $project->no_next = TRUE; $form['note'] = array( '#type' => 'markup', '#value' => $note, ); - $form['not_ready'] = array( '#type' => 'value', '#value' => TRUE, @@ -863,7 +861,7 @@ function devshop_projects_add_status($type = 'platform'){ // Check verification task for all nids foreach ($nids as $nid){ $task = hosting_get_most_recent_task($nid, 'verify'); - if ($task->task_status != HOSTING_TASK_SUCCESS) { + if ($task->task_status != HOSTING_TASK_SUCCESS && $task->task_status != HOSTING_TASK_ERROR) { $good_to_go = FALSE; } } From ca819ff99b45a303dc26899ee8db7abd6783c061 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 12:34:45 -0400 Subject: [PATCH 0576/3476] removing our "silly hack" --- devshop_projects/inc/tasks.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 08e87f4b6..b3f08e1de 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -16,7 +16,6 @@ function devshop_projects_hosting_tasks() { 'provision_save' => TRUE, 'access callback' => 'devshop_hosting_task_menu_access', 'hidden' => TRUE, - 'nid' => NULL, // This silly hack hides verify... hidden won't work! ); $tasks['project']['devshop-create'] = array( 'title' => t('Create New Environment'), From 06fc87f69c3930deefb87f69112ebfa1c3a41898 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 12:45:06 -0400 Subject: [PATCH 0577/3476] Better error handling --- devshop_projects/inc/create-wizard.inc | 34 +++++++++----------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index c7359280d..73fbdb754 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -62,16 +62,15 @@ function devshop_projects_create_wizard($step = NULL){ if (isset($tasks['verify']['nid'])){ } - dsm($tasks, 'tasks'); $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; $project->verify_task_nid = $tasks['verify']['nid']; // If project verification failed, we need to ask for a new git url. - if ($project->verify_task_status === HOSTING_TASK_ERROR && !empty($project_node->nid)){ - //drupal_set_message(t('Unable to connect to the git url: !git. Please check the git URL and try again.', array('!git' => $project->git_url))); + if ($project->verify_task_status == HOSTING_TASK_ERROR && !empty($project_node->nid)){ + $project->verify_error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, 'error')); // If not on the first step, go to it. if ($step != current(array_keys($form_info['order']))){ - //drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); + drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); } } @@ -224,6 +223,14 @@ function devshop_projects_create_wizard_cancel(&$form_state) { */ function devshop_project_create_step_git(&$form, &$form_state) { $project = &$form_state['project']; + + if ($project->verify_error){ + $form['node'] = array( + '#value' => '
' . $project->verify_error . '
', + '#type' => 'markup', + ); + } + $form['git_url'] = array( '#type' => 'textfield', '#required' => 1, @@ -460,25 +467,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { devshop_form_reloader($form, 'project'); return; } - // Handle failure - elseif ($project->verify_task_status == HOSTING_TASK_ERROR) { - $type = 'error'; - $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, $type)); - drupal_set_message($error, 'error'); - - $note = '

'. $error . t('Something Failed. Please check the error message, and retry verification.') . '

'; - $project->no_next = TRUE; - - $form['note'] = array( - '#type' => 'markup', - '#value' => $note, - ); - $form['not_ready'] = array( - '#type' => 'value', - '#value' => TRUE, - ); - return; - } // this JS handles the form element hiding/showing $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; From 7d78c086c711d4b684d9ac3f6348ea7260bc43de Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 12:50:14 -0400 Subject: [PATCH 0578/3476] unset errors when there are none. --- devshop_projects/inc/create-wizard.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 73fbdb754..3fda52513 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -72,6 +72,8 @@ function devshop_projects_create_wizard($step = NULL){ if ($step != current(array_keys($form_info['order']))){ drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); } + } else { + $project->verify_error = NULL; } // All forms can access $form_state['project']; From fef73ad5b24d639ac4b879da91abc383db0fef94 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 13:01:17 -0400 Subject: [PATCH 0579/3476] better create environments forms --- devshop_projects/inc/create-wizard.inc | 4 +++- devshop_projects/inc/theme.inc | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 3fda52513..7a785edd0 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -477,7 +477,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $settings = module_invoke_all('devshop_project_settings', $project_node); $form['environments'] = array( - '#type' => 'fieldset', '#theme' => 'devshop_projects_create_settings_form', '#tree' => TRUE, '#prefix' => '
', @@ -491,6 +490,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { } else { $env_title = $env; } + $form['environments'][$env] = array( '#tree' => TRUE, '#type' => 'fieldset', @@ -522,6 +522,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#value' => t('Add environment'), '#name' => 'add_environment', '#submit' => array('devshop_projects_create_wizard_add_new_environment'), + '#prefix' => '
', + '#suffix' => '
', ); } } diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 8a4bd90d8..e09568d61 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -59,7 +59,7 @@ function theme_devshop_projects_create_settings_form($form) { $rows[] = $row; } $output = theme('table', $header, $rows, array('id' => 'project-settings-table')); - $output .= '

'. t('Create as many new environments as you would like. "dev", "test", and "live"') .'

'; + $output .= '

'. t('Create as many new environments as you would like. For example: "dev", "test", and "live"') .'

'; return $output; } From 1fea0446099a79b069339f4e1515999b6553afb5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 13:01:26 -0400 Subject: [PATCH 0580/3476] better create environments forms --- devshop_projects/devshop-style.css | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 4d179a6b0..72518a96b 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -92,8 +92,27 @@ form .sync-envs div.form-item-labeled label { display: none; } +.path-hosting-projects-add-environments #main { + width: 98%; +} + +.path-hosting-projects-add-environments div#page div.limiter { + background: #fff; +} p.wait { text-align: center; padding: 32px 0px 52px; background: bottom center no-repeat url(images/loading.gif); -} \ No newline at end of file +} + +#project-settings-table { + margin-top: 20px; +} + +.project-add-environment-button { + margin: 0px auto; + text-align: center; +} +#edit-add-environment { + background: #999; +} From 8378444114626334fed2eb62463debe4e8dac7c4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 13:06:36 -0400 Subject: [PATCH 0581/3476] ensure blank environment names don't trigger platform creation --- devshop_projects/inc/create-wizard.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 7a785edd0..8da83827e 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -561,12 +561,16 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) unset($values['environments']['NEW']); $values['environments']['NEW'] = array(); } + // Unset empty "NEW" environment + else { + unset($values['environments']['NEW']); + } // Save envs to project $project->environments = $values['environments']; // Reject if empty - if (count($project->environments) - 1 < 1){ + if (count($project->environments) < 1){ form_set_error('environments][NEW][title', t('You must add at least one environment.')); } } From 398f7adeb8241b4f934de3b28319619d862d449d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 13:57:50 -0400 Subject: [PATCH 0582/3476] Ensure a blank row exists (happens when using 'Back' button) --- devshop_projects/inc/create-wizard.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 8da83827e..a3dd478f7 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -483,6 +483,12 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#suffix' => '
', ); + // Ensure a blank row exists (happens when using 'Back' button) + if (empty($project->environments['NEW'])){ + $project->environments['NEW'] = array(); + } + + foreach ($project->environments as $env => $env_settings) { // No platforms exist yet if ($env == 'NEW') { From b8f3a515642dc2f78816e3e3370ca3b80f0c6629 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 14:10:24 -0400 Subject: [PATCH 0583/3476] Adding environments array. --- devshop_projects/inc/nodes.inc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2f6476ede..572c5739b 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -231,6 +231,17 @@ function devshop_projects_load($node) { // Set status $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node))); + // Environments Array + // @TODO: Remove the above code, after refactoring out of the rest of the system. + $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d ORDER BY environment", $node->nid); + + $environments = array(); + while ($obj = db_fetch_object($query)) { + $object_node = node_load($obj->object_nid); + $environments[$obj->environment][$obj->object_type] = $obj->object_nid; + } + $additions['environments'] = $environments; + // Settings are added in devshop_projects_nodeapi return $additions; From e6b5694d993f421afd6508caaf3bd745693fb755 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 14:10:49 -0400 Subject: [PATCH 0584/3476] using $project->environments array instead of "project objects" --- devshop_projects/inc/create-wizard.inc | 39 +++++++++++--------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index a3dd478f7..614ff29a1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -663,6 +663,10 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project_node = node_load($project->project_nid); // OUTPUT + // @TODO: Clean this up!! + + // @TODO: This is old code that is never seen. User ends up on project page. + // Perhaps we should keep people on this page until the sites are all installed? No, because we want to free up the wizard for another project create... instead lets fix the project node page to display proper status. if ($project_node->project_status == 'sites_installing') { $form['note'] = array( '#type' => 'item', @@ -671,13 +675,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { ); return $form; } - elseif ($project_node->project_status == 'preparing-project') { - // @TODO: Not yet, needs more logic - // drupal_goto('hosting/projects/add'); - } - elseif ($project_node->project_status == 'sites-installed') { - return; - } $profiles = array(); $available_profiles = array(); @@ -691,22 +688,18 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Display the platforms $rows = array(); $header = array(t('Name'), t('Branch'), t('Version'), t('Verification Status'), t('Installation Profiles')); - foreach ($project_node->project_objects['platform'] as $nid => $name){ - - // If this platform is no longer in the $project object, we shouldn't show - // it here. - if (!isset($project->environments[$name])) { - continue; - } - - // Get verification task for this platform. - $task = hosting_get_most_recent_task($nid, 'verify'); - $platform = node_load($nid); + foreach ($project_node->environments as $name => $environment){ + + // Get platform and latest verify task. + $platform_nid = $environment['platform']; + $platform = node_load($platform_nid); + $task = hosting_get_most_recent_task($platform_nid, 'verify'); + // Build a table. $row = array(); $row[] = $name; $row[] = $platform->git_branch; - $row[] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('Pending'); + $row[] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); $row[] = _hosting_parse_error_code($task->task_status); // If platform verified successfully: @@ -734,12 +727,12 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { $completed = FALSE; - $row[] = t('Platform verification failed! Check the task log for more details. You can !link', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))); + $row[] = t('Platform verification failed! !link to check your environment branch or git repository.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))); } // If platform is still processing: elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { $completed = FALSE; - $row[] = t('Platform download & verification in process.'); + $row[] = '...'; } // If platform has NOT verified successfully, mark process not complete. @@ -764,7 +757,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project->no_finish = TRUE; $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; - devshop_form_reloader($form, 'platform'); + //devshop_form_reloader($form, 'platform'); $form['help'] = array( @@ -776,7 +769,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If no available profiles: elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; - $note = '

' . t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; + $note = '

' . t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; devshop_form_reloader($form, 'platform'); From 4ce97aa58d3c2b77ac3dbcd13c802f1b8abde459 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 14:33:34 -0400 Subject: [PATCH 0585/3476] Checking for hanging tasks, alerting the user to check their task queue if it is over two minutes --- devshop_projects/inc/create-wizard.inc | 43 +++++++++++++++++++++----- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 614ff29a1..698daf098 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -457,6 +457,16 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; $project->no_next = TRUE; + // If we are all queued, show a friendly message + //devshop_form_reloader($form, 'platform'); + if ($project->verify_task_status == HOSTING_TASK_QUEUED){ + $task = node_load($project->verify_task_nid); + $time_ago = time() - $task->created; + if ($time_ago > 60) { + $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; + } + } + $form['note'] = array( '#type' => 'markup', '#value' => $note, @@ -467,6 +477,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); // Add code to reload the page when complete. devshop_form_reloader($form, 'project'); + + return; } @@ -687,7 +699,8 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Display the platforms $rows = array(); - $header = array(t('Name'), t('Branch'), t('Version'), t('Verification Status'), t('Installation Profiles')); + $header = array(t('Name'), t('Branch'), t('Version'), t('Installation Profiles'), t('Verification Status')); + $all_tasks_queued = TRUE; foreach ($project_node->environments as $name => $environment){ // Get platform and latest verify task. @@ -700,7 +713,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $row[] = $name; $row[] = $platform->git_branch; $row[] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); - $row[] = _hosting_parse_error_code($task->task_status); // If platform verified successfully: if ($task->task_status == HOSTING_TASK_SUCCESS) { @@ -734,18 +746,26 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $completed = FALSE; $row[] = '...'; } - // If platform has NOT verified successfully, mark process not complete. else { $completed = FALSE; $row[] = t('Pending'); } + + // If a single task is not queued, $all_tasks_queued == FALSE + if ($task->task_status != HOSTING_TASK_QUEUED){ + $all_tasks_queued = FALSE; + } + + // Add hosting task status. + $row[] = _hosting_parse_error_code($task->task_status); // Store rows for display $rows[] = $row; } // end foreach platform + // Output our table. $form['platforms'] = array( '#type' => 'markup', @@ -757,13 +777,20 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project->no_finish = TRUE; $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; - //devshop_form_reloader($form, 'platform'); - + // If we are all queued, show a friendly message + + $time_ago = time() - $task->created; + if ($all_tasks_queued && $time_ago > 120) { + $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; + } $form['help'] = array( '#type' => 'markup', '#value' => $note, ); + + devshop_form_reloader($form, 'platform'); + return $form; } // If no available profiles: @@ -848,19 +875,19 @@ function devshop_projects_add_status($type = 'platform'){ // When checking platforms... if ($type == 'platform') { - foreach ($project_node->project_objects['platform'] as $nid => $name){ - $nids[] = $nid; + foreach ($project_node->environments as $name => $environment){ + $nids[] = $environment['platform']; } } // Check verification task for all nids foreach ($nids as $nid){ $task = hosting_get_most_recent_task($nid, 'verify'); + $return['tasks'][$nid] = _hosting_parse_error_code($task->task_status); if ($task->task_status != HOSTING_TASK_SUCCESS && $task->task_status != HOSTING_TASK_ERROR) { $good_to_go = FALSE; } } - $return['reload'] = $good_to_go; drupal_json($return); exit; From 2f27a13312709250810f168927610ab050a5ff04 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 14:36:34 -0400 Subject: [PATCH 0586/3476] adding @todo about actual error messages. --- devshop_projects/inc/create-wizard.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 698daf098..20cb173d5 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -740,6 +740,9 @@ function devshop_project_create_step_sites(&$form, &$form_state) { elseif ($task->task_status == HOSTING_TASK_ERROR) { $completed = FALSE; $row[] = t('Platform verification failed! !link to check your environment branch or git repository.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))); + + // @TODO: Get task log error message + } // If platform is still processing: elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { From 0998e4c604d9472867c0977072b3cdbc7b3fa4f5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 14:51:42 -0400 Subject: [PATCH 0587/3476] Improving display and logic of failed vs running tasks. --- devshop_projects/inc/create-wizard.inc | 46 ++++++++++++-------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 20cb173d5..dc2241be7 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -701,6 +701,8 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $rows = array(); $header = array(t('Name'), t('Branch'), t('Version'), t('Installation Profiles'), t('Verification Status')); $all_tasks_queued = TRUE; + $tasks_failed = FALSE; + foreach ($project_node->environments as $name => $environment){ // Get platform and latest verify task. @@ -710,9 +712,9 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Build a table. $row = array(); - $row[] = $name; - $row[] = $platform->git_branch; - $row[] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); + $row['name'] = $name; + $row['branch'] = $platform->git_branch; + $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); // If platform verified successfully: if ($task->task_status == HOSTING_TASK_SUCCESS) { @@ -721,25 +723,26 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); - $row[] = implode(', ', $profiles[$name]); + $row['profiles'] = implode(', ', $profiles[$name]); } else { $profiles[$name] = array(); } - // If no available profiles found yet, this becomes the available list. - if (empty($available_profiles)){ - $available_profiles = $profiles[$name]; - } - // If we have available profiles, only keep the ones we found again. - else { - // @TODO: This joins profiles by name, not profile name. + // If no tasks have failed, save available profiles + if (!$tasks_failed){ $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); } } // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { - $completed = FALSE; - $row[] = t('Platform verification failed! !link to check your environment branch or git repository.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))); + $completed = TRUE; + $tasks_failed = TRUE; + $available_profiles = array(); + + $row['version'] = array( + 'data' => t('Platform verification failed! !link to check your environment branch or git repository.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))), + 'colspan' => 2, + ); // @TODO: Get task log error message @@ -747,12 +750,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If platform is still processing: elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { $completed = FALSE; - $row[] = '...'; - } - // If platform has NOT verified successfully, mark process not complete. - else { - $completed = FALSE; - $row[] = t('Pending'); + $row['version'] = '...'; } // If a single task is not queued, $all_tasks_queued == FALSE @@ -761,21 +759,19 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } // Add hosting task status. - $row[] = _hosting_parse_error_code($task->task_status); + $row['status'] = _hosting_parse_error_code($task->task_status); // Store rows for display $rows[] = $row; } // end foreach platform - - // Output our table. $form['platforms'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), ); - // Provide something for people while they wait. + // Not completed means show all tasks are not completed (or errored) if (!$completed){ $project->no_finish = TRUE; $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; @@ -799,7 +795,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If no available profiles: elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; - $note = '

' . t('WARNING: No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; + $note = '

' . t('No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; devshop_form_reloader($form, 'platform'); @@ -809,6 +805,8 @@ function devshop_project_create_step_sites(&$form, &$form_state) { ); return $form; } else { + dsm($available_profiles); + $project->no_finish = FALSE; // Install Profile From dd3828fd9de5618f19521a1a828e827ea56d3009 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 15:02:31 -0400 Subject: [PATCH 0588/3476] Better error reporting, properly adding the form reloader --- devshop_projects/inc/create-wizard.inc | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index dc2241be7..47db08f5c 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -62,6 +62,8 @@ function devshop_projects_create_wizard($step = NULL){ if (isset($tasks['verify']['nid'])){ } + + // Get "verify" task status for the project $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; $project->verify_task_nid = $tasks['verify']['nid']; @@ -699,7 +701,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Display the platforms $rows = array(); - $header = array(t('Name'), t('Branch'), t('Version'), t('Installation Profiles'), t('Verification Status')); + $header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); $all_tasks_queued = TRUE; $tasks_failed = FALSE; @@ -739,8 +741,10 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $tasks_failed = TRUE; $available_profiles = array(); + $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); + $row['version'] = array( - 'data' => t('Platform verification failed! !link to check your environment branch or git repository.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))), + 'data' => t('Platform verification failed: %error', array('%error' => $error)), 'colspan' => 2, ); @@ -788,16 +792,14 @@ function devshop_project_create_step_sites(&$form, &$form_state) { '#value' => $note, ); - devshop_form_reloader($form, 'platform'); + devshop_form_reloader($form, 'platform'); return $form; } // If no available profiles: elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; - $note = '

' . t('No common profile was found in all of your platforms. Please check your branches and code and try again. You must !link to change your branches.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; - - devshop_form_reloader($form, 'platform'); + $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; $form['error'] = array( '#type' => 'markup', @@ -866,7 +868,7 @@ function devshop_projects_add_status($type = 'platform'){ $project_node = node_load($project->project_nid); - $good_to_go = TRUE; + $all_tasks_completed = TRUE; $nids = array(); // When checking project... @@ -885,11 +887,16 @@ function devshop_projects_add_status($type = 'platform'){ foreach ($nids as $nid){ $task = hosting_get_most_recent_task($nid, 'verify'); $return['tasks'][$nid] = _hosting_parse_error_code($task->task_status); - if ($task->task_status != HOSTING_TASK_SUCCESS && $task->task_status != HOSTING_TASK_ERROR) { - $good_to_go = FALSE; + + // If task is not completed, mark all tasks not complete. + if ($task->task_status == HOSTING_TASK_SUCCESS || $task->task_status == HOSTING_TASK_ERROR) { + continue; + } else { + $all_tasks_completed = FALSE; } + } - $return['reload'] = $good_to_go; + $return['tasks_complete'] = $all_tasks_completed; drupal_json($return); exit; } From c5e147d227716080fd5ffe937e30322dc5bb9702 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 16:08:03 -0400 Subject: [PATCH 0589/3476] Fixing up environments on create form. --- devshop_projects/inc/create-wizard.inc | 109 ++++++++++++++----------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 47db08f5c..300f988f4 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -455,6 +455,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); + if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) { $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; $project->no_next = TRUE; @@ -498,11 +499,10 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); // Ensure a blank row exists (happens when using 'Back' button) - if (empty($project->environments['NEW'])){ + if (!is_array($project->environments['NEW'])){ $project->environments['NEW'] = array(); } - foreach ($project->environments as $env => $env_settings) { // No platforms exist yet if ($env == 'NEW') { @@ -562,36 +562,43 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) return; } - // Check environment titles - foreach ($values['environments'] as $env => $env_settings) { - // Check for illegal chars - if ($env != 'NEW' && !preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { - $form_item = 'environments][' . $env . '][title'; - form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); - } - } - - // Change NEW environment data to $title + // Changes NEW environment data to $title if ($values['environments']['NEW']['title']){ $new_env = $values['environments']['NEW']['title']; $new_env_settings = $values['environments']['NEW']; $values['environments'][$new_env] = $new_env_settings; - // Create the next NEW environment + // Create the next NEW environment. Unset makes sure its always last. unset($values['environments']['NEW']); $values['environments']['NEW'] = array(); - } - // Unset empty "NEW" environment - else { + } else { unset($values['environments']['NEW']); } - // Save envs to project - $project->environments = $values['environments']; + // Reset project environments + $project->environments = array(); + + // Check environment titles + foreach ($values['environments'] as $env => $env_settings) { + // Check for illegal chars + if ($env != 'NEW'){ + if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { + $form_item = 'environments][' . $env . '][title'; + form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); + } else { + // If all is well, save to project cache + $project->environments[$env] = $env_settings; + } + } + } // Reject if empty if (count($project->environments) < 1){ - form_set_error('environments][NEW][title', t('You must add at least one environment.')); + if ($form_state['clicked_button']['#name'] == 'add_environment'){ + form_set_error('environments][NEW][title', t('Name this environment before you add another.')); + } else { + form_set_error('environments][NEW][title', t('You must add at least one environment.')); + } } } @@ -601,51 +608,60 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Get project and reset properties.. $project = &$form_state['project']; + $project_node = node_load($project->project_nid); $values = $form_state['values']; - - $project->environments = array_filter($values['environments']); + dsm($values, 'values'); $first_envs = $project->environments ? $project->environments : array(); $settings = module_invoke_all('devshop_project_settings', $project_node); - // Create these platforms - foreach ($project->environments as $name => $data) { - + // Create these platforms, if they don't exist yet + foreach ($project->environments as $name => $environment) { + $platform_nid = $project_node->environments[$name]['platform']; + + // If the platform already exists, save it again with new settings. + if ($platform_nid) { + drupal_set_message("Updating platform for $name"); + $platform_node = node_load($platform_nid); + foreach ($settings as $setting_name => $element){ + $platform_node->{$setting_name} = $environment[$setting_name]; + } + node_save($platform_node); + } // If platform hasn't been created yet, do so now! - if (empty($data['platform_nid'])) { + elseif (empty($platform_nid)) { + drupal_set_message("Creating new platform for $name"); + // Save the damn platform nodes $platform = new stdClass; - + // Platform name - $platform->title = $project->title . '_' . $data['title']; + $platform->title = $project->title . '_' . $environment['title']; // Platform publish_path if ($project->drupal_path) { - $platform->publish_path = $project->code_path . '/' . $data['title'] . '/' . $project->drupal_path; + $platform->publish_path = $project->code_path . '/' . $environment['title'] . '/' . $project->drupal_path; } else { - $platform->publish_path = $project->code_path . '/' . $data['title']; + $platform->publish_path = $project->code_path . '/' . $environment['title']; } - + // Other attributes - $platform->web_server = $data['web_server']; - $platform->git_branch = $data['git_branch']; - $platform->pull_enable = $data['pull_enabled']; + $platform->web_server = $environment['web_server']; + $platform->git_branch = $environment['git_branch']; + $platform->pull_enable = $environment['pull_enabled']; $platform->project = $project->title; - $platform->environment = $data['title']; + $platform->environment = $environment['title']; $platform->drupal_path = $project->drupal_path; + + foreach ($settings as $setting_name => $element){ + $platform->{$setting_name} = $environment[$setting_name]; + } + dsm($platform, 'CREATEING NEW PLATFORM!'); $platform_node = _devshop_projects_node_create('platform', $platform); $project->environments[$name]['platform_nid'] = $platform_node->nid; } - // If the platform already exists, save it again with new settings. - else { - $platform_node = node_load($project->platforms[$data['title']]); - foreach ($settings as $setting_name => $element){ - $platform_node->{$setting_name} = $data[$setting_name]; - } - node_save($platform_node); - } } // For all removed platforms, trigger a delete task @@ -657,10 +673,8 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // the platform node. // Create 'delete' task for this project - hosting_add_task($project->environments[$environment_name]['platform_nid'], 'delete'); - - // Remove from project object - unset($project->environments[$environment_name]); + dsm($environment_name, '@TODO: hosting delete'); + //hosting_add_task($project_node->environments[$environment_name]['platform'], 'delete'); } } @@ -755,6 +769,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { $completed = FALSE; $row['version'] = '...'; + $row['profiles'] = '...'; } // If a single task is not queued, $all_tasks_queued == FALSE @@ -792,7 +807,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { '#value' => $note, ); - devshop_form_reloader($form, 'platform'); +// devshop_form_reloader($form, 'platform'); return $form; } From 5973a89243bdf9e4df856853c7a0ae6f0bfece31 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 16:09:10 -0400 Subject: [PATCH 0590/3476] JS if tasks_complete --- devshop_projects/inc/reload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/reload.js b/devshop_projects/inc/reload.js index ae93738fa..d9a5aa82a 100644 --- a/devshop_projects/inc/reload.js +++ b/devshop_projects/inc/reload.js @@ -9,7 +9,7 @@ var devshopCheckProject = function(){ var devshopReloadPage = function(data){ - if (data.reload){ + if (data.tasks_complete){ document.location.reload(); } else { setTimeout("devshopCheckProject()", Drupal.settings.devshopReload.delay); From c09bb3bf76811dc4547c926eb545c9a4574b6fd4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 16:09:19 -0400 Subject: [PATCH 0591/3476] removing dsms --- devshop_projects/inc/create-wizard.inc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 300f988f4..3f0d80d3c 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -610,7 +610,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); $values = $form_state['values']; - dsm($values, 'values'); $first_envs = $project->environments ? $project->environments : array(); $settings = module_invoke_all('devshop_project_settings', $project_node); @@ -621,7 +620,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // If the platform already exists, save it again with new settings. if ($platform_nid) { - drupal_set_message("Updating platform for $name"); $platform_node = node_load($platform_nid); foreach ($settings as $setting_name => $element){ $platform_node->{$setting_name} = $environment[$setting_name]; @@ -630,7 +628,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } // If platform hasn't been created yet, do so now! elseif (empty($platform_nid)) { - drupal_set_message("Creating new platform for $name"); // Save the damn platform nodes $platform = new stdClass; @@ -657,8 +654,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { foreach ($settings as $setting_name => $element){ $platform->{$setting_name} = $environment[$setting_name]; } - dsm($platform, 'CREATEING NEW PLATFORM!'); - $platform_node = _devshop_projects_node_create('platform', $platform); $project->environments[$name]['platform_nid'] = $platform_node->nid; } @@ -673,8 +668,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // the platform node. // Create 'delete' task for this project - dsm($environment_name, '@TODO: hosting delete'); - //hosting_add_task($project_node->environments[$environment_name]['platform'], 'delete'); + hosting_add_task($project_node->environments[$environment_name]['platform'], 'delete'); } } From efb3452de2cc321e9c44dc43cdceff059f35976b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 16:44:25 -0400 Subject: [PATCH 0592/3476] better logic for all tasks succeeding --- devshop_projects/inc/create-wizard.inc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 3f0d80d3c..320b0af75 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -711,7 +711,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $rows = array(); $header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); $all_tasks_queued = TRUE; - $tasks_failed = FALSE; + $all_tasks_succeeded = TRUE; foreach ($project_node->environments as $name => $environment){ @@ -739,14 +739,18 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } // If no tasks have failed, save available profiles - if (!$tasks_failed){ - $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + if ($all_tasks_succeeded){ + if (empty($available_profiles)){ + $available_profiles = $profiles[$name]; + } else { + $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + } } } // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { $completed = TRUE; - $tasks_failed = TRUE; + $all_tasks_succeeded = FALSE; $available_profiles = array(); $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); @@ -783,7 +787,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { '#type' => 'markup', '#value' => theme('table', $header, $rows), ); - + // Not completed means show all tasks are not completed (or errored) if (!$completed){ $project->no_finish = TRUE; @@ -806,6 +810,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { return $form; } // If no available profiles: + elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; @@ -816,8 +821,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { ); return $form; } else { - dsm($available_profiles); - $project->no_finish = FALSE; // Install Profile From f13732cf4c3f65cbc4aae026015b519d06cb3ab0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 16:46:13 -0400 Subject: [PATCH 0593/3476] adding back reloader. --- devshop_projects/inc/create-wizard.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 320b0af75..ad24aa016 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -805,8 +805,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { '#value' => $note, ); -// devshop_form_reloader($form, 'platform'); - + devshop_form_reloader($form, 'platform'); return $form; } // If no available profiles: From b7848f9551ca5ef40b462dd94581d45a0dc06de2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 16:48:15 -0400 Subject: [PATCH 0594/3476] use project environments --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index ad24aa016..05c6abf15 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -668,7 +668,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // the platform node. // Create 'delete' task for this project - hosting_add_task($project_node->environments[$environment_name]['platform'], 'delete'); + hosting_add_task($project->environments[$environment_name]['platform_nid'], 'delete'); } } From 68db73f64ec5e3ad44d453ef3a9e148b8abf3c9e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 17:03:09 -0400 Subject: [PATCH 0595/3476] fighting NEW again. --- devshop_projects/inc/create-wizard.inc | 29 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 05c6abf15..5c10f63bc 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -570,7 +570,11 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // Create the next NEW environment. Unset makes sure its always last. unset($values['environments']['NEW']); - $values['environments']['NEW'] = array(); + + // If "add environment" button clicked, add another row. + if ($form_state['clicked_button']['#name'] == 'add_environment'){ + $values['environments']['NEW'] = array(); + } } else { unset($values['environments']['NEW']); } @@ -581,7 +585,7 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // Check environment titles foreach ($values['environments'] as $env => $env_settings) { // Check for illegal chars - if ($env != 'NEW'){ + if ($env != 'NEW' && !empty($env_settings['title'])){ if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { $form_item = 'environments][' . $env . '][title'; form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); @@ -592,6 +596,8 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) } } + + // Reject if empty if (count($project->environments) < 1){ if ($form_state['clicked_button']['#name'] == 'add_environment'){ @@ -611,7 +617,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project_node = node_load($project->project_nid); $values = $form_state['values']; - $first_envs = $project->environments ? $project->environments : array(); $settings = module_invoke_all('devshop_project_settings', $project_node); // Create these platforms, if they don't exist yet @@ -655,20 +660,26 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform->{$setting_name} = $environment[$setting_name]; } $platform_node = _devshop_projects_node_create('platform', $platform); - $project->environments[$name]['platform_nid'] = $platform_node->nid; + $project->environments[$name]['platform_nid'] = $platform_node->nid; } } // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($first_envs, $values['environments']); + $removed_environments = array_diff_key($values['environments'], $project->environments); + + dsm($project, 'project'); + dsm($removed_environments, 'envs to remove'); + foreach ($removed_environments as $environment_name => $settings) { - // @TODO: Determine what to do here based on task status... // if verify task hasn't even run yet (and has never run) we can just delete // the platform node. - // Create 'delete' task for this project - hosting_add_task($project->environments[$environment_name]['platform_nid'], 'delete'); + // Create 'delete' task for the removed platform + $platform_nid = $project->environments[$environment_name]['platform_nid']; + if ($platform_nid){ + hosting_add_task($project->environments[$environment_name]['platform_nid'], 'delete'); + } } } @@ -805,7 +816,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { '#value' => $note, ); - devshop_form_reloader($form, 'platform'); + //devshop_form_reloader($form, 'platform'); return $form; } // If no available profiles: From 8e4530b358d1dcc58ad92d37582f5a82339e88af Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 17:07:50 -0400 Subject: [PATCH 0596/3476] deleting platforms in wizard --- devshop_projects/inc/create-wizard.inc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 5c10f63bc..986a59e76 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -665,10 +665,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($values['environments'], $project->environments); - - dsm($project, 'project'); - dsm($removed_environments, 'envs to remove'); + $removed_environments = array_diff_key($project_node->environments, $values['environments']); foreach ($removed_environments as $environment_name => $settings) { // @TODO: Determine what to do here based on task status... @@ -676,9 +673,9 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // the platform node. // Create 'delete' task for the removed platform - $platform_nid = $project->environments[$environment_name]['platform_nid']; + $platform_nid = $settings['platform']; if ($platform_nid){ - hosting_add_task($project->environments[$environment_name]['platform_nid'], 'delete'); + hosting_add_task($platform_nid, 'delete'); } } } From 1d90941db8214144d29a98f834836fb746d0d489 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 17:10:22 -0400 Subject: [PATCH 0597/3476] only load site and platform into environment list if site or platform is not deleted --- devshop_projects/inc/nodes.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 572c5739b..07f15df9b 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -238,7 +238,12 @@ function devshop_projects_load($node) { $environments = array(); while ($obj = db_fetch_object($query)) { $object_node = node_load($obj->object_nid); - $environments[$obj->environment][$obj->object_type] = $obj->object_nid; + + if (($object_node->type == 'site' && $object_node->site_status != HOSTING_SITE_DELETED) || + ($object_node->type == 'platform' && $object_node->platform_status != HOSTING_PLATFORM_DELETED) + ){ + $environments[$obj->environment][$obj->object_type] = $obj->object_nid; + } } $additions['environments'] = $environments; From 97a913c42f1f0f9926fb4c715fafef46952a33cc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 17:14:53 -0400 Subject: [PATCH 0598/3476] fixing project page to use $node->environments --- devshop_projects/inc/ui.inc | 208 ++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 105 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 4df77d597..bd34d8048 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -158,122 +158,120 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Environments $rows = array(); - $site_nids = array_flip($node->project_objects['site']); - $platform_nids = array_flip($node->project_objects['site']); - - if (isset($platform_nids)) { - foreach($platform_nids as $env => $nid) { - - $site = node_load($site_nids[$env]); - $platform = node_load($nid); - - // Skip this if it is not enabled. - if ($site->site_status != HOSTING_SITE_ENABLED && $platform->platform_status != HOSTING_PLATFORM_ENABLED) { - continue; - } - - $row = array(); - $row[] = "$site->environment"; - - if ($site->site_status == HOSTING_SITE_DISABLED){ - $row[] = devshop_hosting_site_goto_link($site) . '' . t('Disabled') . ''; - } else { - $row[] = devshop_hosting_site_goto_link($site); - } - $row[] = "$site->git_branch"; - + foreach($node->environments as $env => $environment) { + + $site_nid = $environment['site']; + $platform_nid = $environment['platform']; - if (module_exists('devshop_log')) { - $row[] =l(t('Commits'), "node/$site->nid/logs/commits"); - } - if (module_exists('hosting_logs')) { - $row[] = l(t('Errors'), "node/$site->nid/logs/errors"); - } - if (module_exists('hosting_filemanager')) { - $row[] = l(t('Files'), "node/$site->nid/files/platform"); - } - - // Create actions dropdown. - $actions = array(); - $actions[] = array( - 'title' => t('Fork environment'), - 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))), - 'attributes' => array( - 'class' => 'hosting-button-dialog', - ), - ); - - - // Aegir Tasks - $site_tasklist = hosting_task_fetch_tasks($site->nid); - $site_tasklist['restore']['title'] = t('Restore Backups'); + $site = node_load($site_nid); + $platform = node_load($platform_nid); + + // Skip this if it is not enabled. + if ($site->site_status != HOSTING_SITE_ENABLED && $platform->platform_status != HOSTING_PLATFORM_ENABLED) { + continue; + } + + $row = array(); + $row[] = "$site->environment"; + + if ($site->site_status == HOSTING_SITE_DISABLED){ + $row[] = devshop_hosting_site_goto_link($site) . '' . t('Disabled') . ''; + } else { + $row[] = devshop_hosting_site_goto_link($site); + } + $row[] = "$site->git_branch"; + + + if (module_exists('devshop_log')) { + $row[] =l(t('Commits'), "node/$site->nid/logs/commits"); + } + if (module_exists('hosting_logs')) { + $row[] = l(t('Errors'), "node/$site->nid/logs/errors"); + } + if (module_exists('hosting_filemanager')) { + $row[] = l(t('Files'), "node/$site->nid/files/platform"); + } + + // Create actions dropdown. + $actions = array(); + $actions[] = array( + 'title' => t('Fork environment'), + 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))), + 'attributes' => array( + 'class' => 'hosting-button-dialog', + ), + ); + + + // Aegir Tasks + $site_tasklist = hosting_task_fetch_tasks($site->nid); + $site_tasklist['restore']['title'] = t('Restore Backups'); - // The actions we want - $site_actions = array('flush_cache', 'login_reset', 'backup', 'restore'); + // The actions we want + $site_actions = array('flush_cache', 'login_reset', 'backup', 'restore'); - // Add disable or delete task based on hosting variable. - if (!variable_get('hosting_require_disable_before_delete', TRUE)){ + // Add disable or delete task based on hosting variable. + if (!variable_get('hosting_require_disable_before_delete', TRUE)){ + $site_actions[] = 'delete'; + } else { + if ($site->site_status == HOSTING_SITE_DISABLED){ + $site_actions[] = 'enable'; $site_actions[] = 'delete'; } else { - if ($site->site_status == HOSTING_SITE_DISABLED){ - $site_actions[] = 'enable'; - $site_actions[] = 'delete'; - } else { - $site_actions[] = 'disable'; - } - } - - // Build links to tasks - foreach ($site_actions as $task_name) { - if ($site_tasklist[$task_name]['task_permitted']){ - $actions[] = array( - 'title' => $site_tasklist[$task_name]['title'], - 'href' => sprintf("node/%d/%s_%s", $site->nid, $site->type, $task_name), - 'query' => array( - 'token' => drupal_get_token($user->uid), - 'destination' => "node/$node->nid", - ), - 'attributes' => array( - 'class' => $site_tasklist[$task_name]['dialog']? 'hosting-button-dialog': '', - ), - ); - } + $site_actions[] = 'disable'; } - - // Grant access to Aegir pages - if (user_access('administer hosting')){ - $actions[] = array( - 'title' => t('Site Dashboard'), - 'href' => "node/$site->nid", - ); + } + + // Build links to tasks + foreach ($site_actions as $task_name) { + if ($site_tasklist[$task_name]['task_permitted']){ $actions[] = array( - 'title' => t('Platform Dashboard'), - 'href' => "node/$site->platform", + 'title' => $site_tasklist[$task_name]['title'], + 'href' => sprintf("node/%d/%s_%s", $site->nid, $site->type, $task_name), + 'query' => array( + 'token' => drupal_get_token($user->uid), + 'destination' => "node/$node->nid", + ), + 'attributes' => array( + 'class' => $site_tasklist[$task_name]['dialog']? 'hosting-button-dialog': '', + ), ); } - - $row[] = theme('ctools_dropdown', t('Actions'), $actions); - $rows[] = $row; } - $header = array(); - $table = theme('table', $header, $rows); - - $node->content['sites'] = array( - '#type' => 'fieldset', - '#title' => t('Environments'), - '#weight' => 12, - '#attributes' => array( - 'class' => 'project-environments', - ), - ); - - $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('attributes' => array('class' => 'create-new-environment hosting-button-dialog'), 'query' => array('token' => drupal_get_token($user->uid)))); - $node->content['sites']['table'] = array( - '#type' => 'item', - '#value' => $table, - '#suffix' => $link, - ); + + // Grant access to Aegir pages + if (user_access('administer hosting')){ + $actions[] = array( + 'title' => t('Site Dashboard'), + 'href' => "node/$site->nid", + ); + $actions[] = array( + 'title' => t('Platform Dashboard'), + 'href' => "node/$site->platform", + ); + } + + $row[] = theme('ctools_dropdown', t('Actions'), $actions); + $rows[] = $row; } + $header = array(); + $table = theme('table', $header, $rows); + + $node->content['sites'] = array( + '#type' => 'fieldset', + '#title' => t('Environments'), + '#weight' => 12, + '#attributes' => array( + 'class' => 'project-environments', + ), + ); + + $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('attributes' => array('class' => 'create-new-environment hosting-button-dialog'), 'query' => array('token' => drupal_get_token($user->uid)))); + $node->content['sites']['table'] = array( + '#type' => 'item', + '#value' => $table, + '#suffix' => $link, + ); //Tasks $node->content['tasks_view'] = array( From e01821465380345c1d0ac6d93b17828d9e5702a8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 17:16:50 -0400 Subject: [PATCH 0599/3476] making tasks_table a variable... @todo: hide verify project task --- devshop_projects/inc/ui.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index bd34d8048..3a019a2c6 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -274,9 +274,10 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); //Tasks + $tasks_table = hosting_task_table($node); $node->content['tasks_view'] = array( '#type' => 'item', - '#value' => hosting_task_table($node), + '#value' => $tasks_table, '#prefix' => '
', '#suffix' => '
', '#weight' => 10 From 9c5625e3dfcea2c5bc00d2745419ee6015109283 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 19:01:58 -0400 Subject: [PATCH 0600/3476] comment edit --- devshop_projects/devshop_projects.module | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a19316343..4d8de3e2e 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -243,20 +243,25 @@ function devshop_hosting_project_tab_title($default, $node){ } /** - * Task access controls + * Access Callback for Aegir Tasks * - * This function defines which tasks should be showed to the user but - * especially which will be accessible to him, in the permissions system. + * This function defines access to the various aegir tasks. * * @arg $node object - * the node object we're trying to access + * The node object is running the task. (Site, Platform, Server) * * @arg $task string - * the task type we're trying to do on the $node + * The type of the task that is running. * - * @see hosting_task_menu() + * @see hosting_task_menu_access() + * + * @TODO: This NEVER runs for verify! Only for devshop-xyz tasks. + * project verify task is defined in devshop_projects_hosting_tasks() in + * inc/tasks.inc, and has this function as it's access callback. But this + * function seems to never run. */ function devshop_hosting_task_menu_access($node, $task) { + // If we are passed the nid by mistake if (!isset($node->nid)){ $node = node_load($node); } From c9302b375ed299db6374f367401b79f42614e47d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 20:10:43 -0400 Subject: [PATCH 0601/3476] New Task status block --- devshop_hosting.module | 81 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 6c40d9737..1d6c17524 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -83,6 +83,12 @@ function devshop_hosting_form_user_login_block_alter(&$form){ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { switch ($op) { case 'list': + $blocks['devshop_tasks'] = array( + 'info' => t('DevShop Tasks'), + 'enabled' => 1, + 'region' => 'header', + 'weight' => 0, + ); $blocks['built'] = array( 'info' => t('Created by THINKDROP'), 'weight' => '10', @@ -103,11 +109,82 @@ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { $image_path = drupal_get_path('module', 'devshop_hosting') . '/logo-td.png'; $block['subject'] = t('Created by'); // Don't display a title $block['content'] = l(theme('image', $image_path), 'http://thinkdrop.net/', array('html' => TRUE)); - } elseif ($delta == 'driven') { + } + elseif ($delta == 'driven') { $image_path = drupal_get_path('module', 'devshop_hosting') . '/logo-aegir.png'; $block['subject'] = t('Powered by'); // Don't display a title $block['content'] = l(theme('image', $image_path), 'http://aegirproject.org/', array('html' => TRUE)); } + elseif ($delta = 'devshop_tasks') { + $block['subject'] = ''; + $block['content'] = devshop_hosting_task_queue_block(); + } return $block; } -} \ No newline at end of file +} + +function devshop_hosting_task_queue_block() { + drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js'); + + $settings['hostingTaskRefresh'] = array( + 'queueBlock' => 1, + ); + drupal_add_js($settings, 'setting'); + + $tasks['queued'] = hosting_get_tasks('task_status', HOSTING_TASK_QUEUE, 5); + $tasks['processing'] = hosting_get_tasks('task_status', HOSTING_TASK_PROCESSING, 5); + + $total_tasks = count($tasks['queued']) + count($tasks['processing']); + $status = format_plural($total_tasks, '1 active task.', '@count active tasks.'); + + if ($total_tasks == 0){ + $status_class = 'inactive'; + } else { + $status_class = 'active'; + } + + + // $class = hosting_task_status_class($node->task_status); + foreach ($tasks as $task_status => $nodes){ + if (!empty($nodes)){ + $rows[] = array( + 'data' => $task_status, + 'class' => 'task-status', + ); + } + foreach ($nodes as $node) { + $row = array(); + $row['type'] = array( + 'data' => drupal_ucfirst(str_replace('_', ' ', $node->task_type)) . ' ' . _hosting_node_link($node->rid), + 'class' => 'hosting-status', + ); + $row['actions'] = array( + 'data' => $log_button, + 'class' => 'hosting-actions', + ); + $class = hosting_task_status_class($node->task_status); + $rows[] = array( + 'data' => $row, + 'class' => $class, + ); + } + } + + // Build Strings + if (user_access('access task logs')){ + $link = l('Tasks Logs', 'hosting/queues/tasks', array('attributes' => array('class' => 'task-logs-link'))); + } + $table = theme('table', $headers, $rows, array('class' => 'hosting-table')); + + // Output our "template" + return <<$status +
+
$table
+
$link
+
+ +HTML; + +} + From 9e636401b6fc5ae11431008bf3e63eaed8ba6eed Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 20:18:30 -0400 Subject: [PATCH 0602/3476] Output our own tiny footprint queue block --- devshop.css | 62 +++++++++++++++++++++++++++++- devshop_hosting.module | 18 +++------ devshop_projects/devshop-style.css | 5 +++ 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/devshop.css b/devshop.css index ce92f3a46..b75e91467 100644 --- a/devshop.css +++ b/devshop.css @@ -86,4 +86,64 @@ textarea#rsa { body.node-type-project a.hosting-goto-site-link { background: none; padding: inherit; -} \ No newline at end of file +} + +/** + * Task Block! + */ +#block-devshop_hosting-devshop_tasks { + position: absolute; + top: 0px; + margin: 0px auto; + width: 940px; +} + + +#block-devshop_hosting-devshop_tasks .content { + position: absolute; + right: 0px; + top: 40px; +} + +#block-devshop_hosting-devshop_tasks #devshop-task-status { + font: bold 13px/20px "Helvetica Neue",Arial,sans-serif; + border: 0px; + padding: 5px 10px 5px 30px; + color: #fff; + background: #666; + cursor: pointer; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + width: 150px; +} + +#block-devshop_hosting-devshop_tasks #devshop-task-status.active { + background: #666 5px no-repeat url('devshop_projects/images/spinner-white.gif'); +} + +#block-devshop_hosting-devshop_tasks #devshop-task-status.inactive { + background: #666; +} + + +#hosting-task-queue-block { + display: none; + width: 96%; + margin: 0px auto; +} + + +#block-devshop_hosting-devshop_tasks:hover #hosting-task-queue-block { + display: block; +} + + +#block-devshop_hosting-devshop_tasks .task-logs-link { + display: block; + padding: 5px 10px; + background: #999; + color: white; + text-align: center; + font-size: 10pt; + text-transform: uppercase; +} diff --git a/devshop_hosting.module b/devshop_hosting.module index 1d6c17524..0d65cd815 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -123,6 +123,9 @@ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { } } +/** + * Output our own tiny footprint queue block + */ function devshop_hosting_task_queue_block() { drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js'); @@ -131,8 +134,8 @@ function devshop_hosting_task_queue_block() { ); drupal_add_js($settings, 'setting'); - $tasks['queued'] = hosting_get_tasks('task_status', HOSTING_TASK_QUEUE, 5); $tasks['processing'] = hosting_get_tasks('task_status', HOSTING_TASK_PROCESSING, 5); + $tasks['queued'] = hosting_get_tasks('task_status', HOSTING_TASK_QUEUE, 5); $total_tasks = count($tasks['queued']) + count($tasks['processing']); $status = format_plural($total_tasks, '1 active task.', '@count active tasks.'); @@ -143,25 +146,14 @@ function devshop_hosting_task_queue_block() { $status_class = 'active'; } - - // $class = hosting_task_status_class($node->task_status); + // Build our own table foreach ($tasks as $task_status => $nodes){ - if (!empty($nodes)){ - $rows[] = array( - 'data' => $task_status, - 'class' => 'task-status', - ); - } foreach ($nodes as $node) { $row = array(); $row['type'] = array( 'data' => drupal_ucfirst(str_replace('_', ' ', $node->task_type)) . ' ' . _hosting_node_link($node->rid), 'class' => 'hosting-status', ); - $row['actions'] = array( - 'data' => $log_button, - 'class' => 'hosting-actions', - ); $class = hosting_task_status_class($node->task_status); $rows[] = array( 'data' => $row, diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 72518a96b..8945bf4c6 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -105,6 +105,11 @@ p.wait { background: bottom center no-repeat url(images/loading.gif); } +p.error { + text-align: center; + padding: 32px 0px 52px; +} + #project-settings-table { margin-top: 20px; } From aa6bd238a414b1a2515030a6400ecf442dd58a9a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 20:18:43 -0400 Subject: [PATCH 0603/3476] adding images --- devshop_projects/images/loader.gif | Bin 0 -> 1849 bytes devshop_projects/images/loading.gif | Bin 0 -> 3208 bytes devshop_projects/images/spinner-white.gif | Bin 0 -> 1849 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 devshop_projects/images/loader.gif create mode 100755 devshop_projects/images/loading.gif create mode 100644 devshop_projects/images/spinner-white.gif diff --git a/devshop_projects/images/loader.gif b/devshop_projects/images/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4 GIT binary patch literal 1849 zcma*odr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSbGzm0!x7^P##Mnh7t-jP!X0Q zk_SQ}Po-L1tlDK;6l?(>v)e5ZBQx4|Y-Q?nr@Px3?9h(3ZWr3^tj=`TP57gKr87N$ zp2wWee1GRRCwo_xahnw)5cxNPJbCg2L6DV|6`#+yw6v6!mDS$f9-JvFD^n;GQ&UrZ zzh5jCkByB101O60U0q#p_1BM>Cv-vP?&s4@g_((4_1L=L$(a91)0=J91Gas#R{McE znYG^9*0A5YZ>#;~+Wkn(W5B0^yELIYLP!K}mB~<)AM@1&nqekynuaEGqPrzoH|KodRXJy)%+w_fu3nE5>@Bd_b zqC$EQ;{c`T&?EsNO|igL9gC7Ygxv?aQUEXMq?~>wg{EyW;VcJ37CUF#HjrT=KQO_* zS>M9yydXk18D(+QDJ1>r);Lav_uYKp$T?4vr{Q$lTo&pKv^?(>L-)G2*lwH!Ah7k? z7oH<8h-(KTKt5V6$8gF)C7Io&P5=SjTh)=zV=E2EUhQZP##L8S{d%UK>>+y82>+FV+#^BzW7u3F)Bb>=lYQ%%j`F>ASe zo*cw@V#u6T`A2He;70mR(V&iV&-7{qP~=SRf&jm9-T{*ZeZ}$rd0#6c&fLG^xJcf5 z+p<`wJYgW+_s*V{uI$nMB;%8`S_3>PfGOj3Rq}@Cx^+j?rk92fANSFDBYnOqQ>Vdj z)(|$AhP4t&Lb=Gvo2#3Gl%9<=Gv`Mz?Po@P4iLF!x}GUWJICDlFk-hS^Whyh7x~VH z@0vD1>HYD4&e+~yzS*-sFR{9`{QEEZO1zg7>R&7cHts-6j!xHVdA8eI+ZlVzd%`es zJT@$#GX(gvCJ1oJN%yLBK}{V=V;seo;!w|Yte!W1%5qLNFWqvZW>h&IiH+oPT=b@E zPhGzv5=(Un*X>v`>%8h_nj^NdYcE6NHS_ifkCV$*D)Tqrbu`s;<=t<4 zAHNqNV?6(g<1PY-w@#I-WYFViz?9TrkMr)u0g`O`u|>T;k|2sV*YF^punvT;$SuTy{j3Gv)yqD!R_CF>yR)MzmmYS5v+~R zXAdD%ng9?df;wd8GxR#%3O+gz};Vo;)sK%Bj-q>Oq%R7JU-KD?vYu>#2UjaDo z&8$>5xW~?KPD_#XFToU1hIb*VOMidUr6iYiO0N|i-7s`T8!cFT`rN!^1Pt78J93i6 z5HI1wIM$94m{3SLDvISDe6$ZG1;eq_D9RTaaC>=cO{@Bs>$IlPCPJJ$h$)-3vzNUQ6OsN#_zWxey!_9%hxwH2_dEJi=yY|1c7nDm2_Lm!Cof8-R_+9UkS zcBE(o47yE)oMR(Q=dp1a2wTX5KvvGyLqlWTa7V&!A*|w|)ax~1_~aJ0=_Lilg*0iQk7#ZD EAHN$8j{pDw literal 0 HcmV?d00001 diff --git a/devshop_projects/images/loading.gif b/devshop_projects/images/loading.gif new file mode 100755 index 0000000000000000000000000000000000000000..bd6dd974121f17d5c4dd79a8a579db1061166c72 GIT binary patch literal 3208 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 DK$5y3 literal 0 HcmV?d00001 diff --git a/devshop_projects/images/spinner-white.gif b/devshop_projects/images/spinner-white.gif new file mode 100644 index 0000000000000000000000000000000000000000..f27d7cd481405ec3a2d3b63616fa65f3a5cfd56c GIT binary patch literal 1849 zcmb8wdr(tX9tZI2z31lM+(&YVNJFGf2tkvOnD7cwta}qc5LgzdLd&BhVpu}~0~N!z zh~$9~3@@d!xLR?SwkX;HqSNh`hmjej9d~7x*`4l|8ST&=Y~2>@c2>vkx(R>mU)ewQ z_ve{&=6vS+JHI)Zd-oW2SRewCe;`OzRn_z7&+F>yMn^{-4#(o+;^gFHU}b)Oer084 zU|_)K^F4n2cxY(o*|TR$OG`5|Gsyo_(a-6;jD0Vs8S^s|w(GDBfwR&7mZUXUEk~>e zr4HMXLsCn%rK#qy<=}Q(UDaVE`YGU3fnDn0EkPuLJET$+_5wU1=`-L7i|G5p^@FgeRoFWjYX#a|XN zV*KzQb|%OrM==gi@@91+5YiM2Too}W$wSzKpfwrr5(i}T8_6_njSgiw8noCr>yGYp zBROw`zKPDcwOK)!b~Ms#KO`4-rCDM*Dek-d(4KXPB+tXKrl~!s)8F{AHypY*G{v;x zECBxPGfSQmuZCB3hk;zQLWbd#ok}#hi=6-hx}&@~m&carPrcRr=2^q}l+u)tl%^o% zb0$Cm>12kC?3w8FxMvd@)T1v9)#uNJx1+5{xVU=KZ(cP(9`TLH&q2~~ARQI@AE41F@D9csIy{?v=24{+~ zAv9(^m(P;|7*8~rwI=&aCFr>BI3fz zb^BHF&im$te8m|(>3%SDUb4Dh`=gXk(keCZC;_I3)0WF>e%7Wvp)$VScl>lGozw5@ zEH}33^+WoQxzwz;NF2;XM&4fA5}>qirkSZ}1k?Px(hjS7tic20Z`HH-g@zp;s23GD!W0qFyF#n{@Th$VC z%6mrNe>x^4pd|?StvUd3$w>E_WKKn$NM-DW*J4r6Sd5N0eAawXIxE?Bt8!2=9D(&` zb6nJxnHO$e7lli-@i%Q)672Asr;4shyZ|$OIb8&VY~wMHtdbybOP)>35ak4;RCZ|y z63MmJ9On~st?d{B2)5Q*8W)oEiy&z)43PXX@~7YH=T!a?SxUy;Gae_GsZiu@U9{I% zb0ux>d!K$BCS^R|#d22x#2r^klv3!i*I){&r`@@DwVNdCe`r)~j39`@t(82;C(MLU zsZ=loM!a#nBD)uK?5-#i5ZeTqwJD@e9$K?L(CZphr9LTD%&DdoKbn$DMe*Vd6Vi7} zCjPkBawwXYIdIw9Bz|+^QMRtbI-{TUb4AVjM{%&0^zn`K+KIMUx!~@vzOjoLieEUfEqQ-sh($>^uY1e(2EK2vzMYifzwp>eeS+3|bqLZ6?uWOgkO+ z^yG-{0&O{)`sb7P#x}faa2_xxJ**qvLi Date: Tue, 14 May 2013 20:24:39 -0400 Subject: [PATCH 0604/3476] forgot to uncomment form reloader. --- devshop_projects/inc/create-wizard.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 986a59e76..3fb4e9134 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -461,7 +461,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $project->no_next = TRUE; // If we are all queued, show a friendly message - //devshop_form_reloader($form, 'platform'); if ($project->verify_task_status == HOSTING_TASK_QUEUED){ $task = node_load($project->verify_task_nid); $time_ago = time() - $task->created; @@ -813,7 +812,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { '#value' => $note, ); - //devshop_form_reloader($form, 'platform'); + devshop_form_reloader($form, 'platform'); return $form; } // If no available profiles: From aab77c4c213df397602bc22a61a4a874b0c41682 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 May 2013 20:31:59 -0400 Subject: [PATCH 0605/3476] only show table if there rows. --- devshop_hosting.module | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 0d65cd815..a47ec0679 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -166,7 +166,9 @@ function devshop_hosting_task_queue_block() { if (user_access('access task logs')){ $link = l('Tasks Logs', 'hosting/queues/tasks', array('attributes' => array('class' => 'task-logs-link'))); } - $table = theme('table', $headers, $rows, array('class' => 'hosting-table')); + if ($rows){ + $table = theme('table', $headers, $rows, array('class' => 'hosting-table')); + } // Output our "template" return << Date: Tue, 14 May 2013 20:32:31 -0400 Subject: [PATCH 0606/3476] wider status box --- devshop.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop.css b/devshop.css index b75e91467..41257b4f6 100644 --- a/devshop.css +++ b/devshop.css @@ -114,7 +114,7 @@ body.node-type-project a.hosting-goto-site-link { cursor: pointer; -moz-border-radius: 3px; -webkit-border-radius: 3px; - width: 150px; + width: 200px; } #block-devshop_hosting-devshop_tasks #devshop-task-status.active { From 51ec793b51f88caaa53dc2d81d833973addfb564 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 09:36:31 -0400 Subject: [PATCH 0607/3476] Comments cleanup --- devshop_hosting.install | 6 ++-- devshop_hosting.module | 72 ++++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/devshop_hosting.install b/devshop_hosting.install index 02c17460e..5bbc88bcc 100644 --- a/devshop_hosting.install +++ b/devshop_hosting.install @@ -1,9 +1,11 @@ devshop
.')); -} \ No newline at end of file +} diff --git a/devshop_hosting.module b/devshop_hosting.module index a47ec0679..3c0e17f8c 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -1,10 +1,14 @@ ' . t('Please log in.') . '

'; + return drupal_get_form('user_login'); + } +} /** - * Implements hook_menu() + * Implements hook_menu_alter() + * + * Streamlines login and servers pages. */ function devshop_hosting_menu_alter(&$items){ $items['user/password']['type'] = MENU_CALLBACK; unset($items['hosting/sites']); unset($items['hosting/platforms']); - + $items['hosting/servers/add'] = $items['node/add/server']; $items['hosting/servers/add']['title'] = t('Add new Server'); $items['hosting/servers/add']['type'] = MENU_LOCAL_TASK; $items['hosting/servers/add']['page arguments'] = array('server'); - + $items['hosting/servers/list'] = array( 'title' => t('All Servers'), 'weight' => -1, @@ -47,29 +69,16 @@ function devshop_hosting_menu_alter(&$items){ } /** - * Provides a login page or redirects to proejcts - */ -function devshop_hosting_home(){ - if (user_is_logged_in()){ - drupal_goto(variable_get('devshop_frontpage', 'hosting/projects')); - } else { - $output = '

' . t('Please log in.') . '

'; - return drupal_get_form('user_login'); - } -} - -/** - * Implements hook_form_user_login_alter() + * Implements hook_form_alter() for user_login. * Provides some UI enhancements. */ function devshop_hosting_form_user_login_alter(&$form){ $form['pass']['#description'] .= ' ' . l(t('Forgot your Password?'), 'user/password'); } - /** - * Implements hook_form_user_login_block_alter() - * Provides some UI enhancements. + * Implements hook_form_alter() for user_login_block. + * Hides the login block form on "devshop" page. */ function devshop_hosting_form_user_login_block_alter(&$form){ if (arg(0) == 'devshop'){ @@ -79,25 +88,30 @@ function devshop_hosting_form_user_login_block_alter(&$form){ /** * Implements hook_block() + * + * Provides three blocks: + * - DevShop Tasks + * - Created by ThinkDrop + * - Powered by Aegir */ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { switch ($op) { case 'list': $blocks['devshop_tasks'] = array( - 'info' => t('DevShop Tasks'), - 'enabled' => 1, - 'region' => 'header', + 'info' => t('DevShop Tasks'), + 'enabled' => 1, + 'region' => 'header', 'weight' => 0, ); $blocks['built'] = array( - 'info' => t('Created by THINKDROP'), + 'info' => t('Created by THINKDROP'), 'weight' => '10', 'cache' => BLOCK_NO_CACHE, 'status' => 1, 'region' => 'footer', ); $blocks['driven'] = array( - 'info' => t('Driven by Aegir'), + 'info' => t('Driven by Aegir'), 'weight' => '10', 'cache' => BLOCK_NO_CACHE, 'status' => 1, @@ -124,7 +138,8 @@ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { } /** - * Output our own tiny footprint queue block + * DevShop Tasks block. + * @todo: Ajaxify. */ function devshop_hosting_task_queue_block() { drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js'); @@ -151,7 +166,7 @@ function devshop_hosting_task_queue_block() { foreach ($nodes as $node) { $row = array(); $row['type'] = array( - 'data' => drupal_ucfirst(str_replace('_', ' ', $node->task_type)) . ' ' . _hosting_node_link($node->rid), + 'data' => drupal_ucfirst(str_replace('_', ' ', $node->task_type)) . ' ' . _hosting_node_link($node->rid), 'class' => 'hosting-status', ); $class = hosting_task_status_class($node->task_status); @@ -161,7 +176,7 @@ function devshop_hosting_task_queue_block() { ); } } - + // Build Strings if (user_access('access task logs')){ $link = l('Tasks Logs', 'hosting/queues/tasks', array('attributes' => array('class' => 'task-logs-link'))); @@ -169,7 +184,7 @@ function devshop_hosting_task_queue_block() { if ($rows){ $table = theme('table', $headers, $rows, array('class' => 'hosting-table')); } - + // Output our "template" return <<$status @@ -181,4 +196,3 @@ function devshop_hosting_task_queue_block() { HTML; } - From 3c85efe90498743d4bbd642be13b5e14e11bcf83 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 09:38:44 -0400 Subject: [PATCH 0608/3476] Function comments and whitespace --- hosting.feature.devshop.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hosting.feature.devshop.inc b/hosting.feature.devshop.inc index e193821d9..55006cd07 100644 --- a/hosting.feature.devshop.inc +++ b/hosting.feature.devshop.inc @@ -1,7 +1,10 @@ t('DevShop'), 'description' => t('Enables the project-centric DevShop UI.'), @@ -45,4 +48,4 @@ function devshop_hosting_hosting_feature() { 'group' => 'devshop', ); return $features; -} \ No newline at end of file +} From 579be2abf887cb27f19edbb962878e3d62aadf5e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 09:42:15 -0400 Subject: [PATCH 0609/3476] Fixing function comments, using hook_uninstall instead of disable --- devshop_live/devshop_live.install | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/devshop_live/devshop_live.install b/devshop_live/devshop_live.install index 6a7b7ccee..65825104e 100644 --- a/devshop_live/devshop_live.install +++ b/devshop_live/devshop_live.install @@ -6,7 +6,8 @@ */ /** - * Implementation of hook_schema(). + * Implements hook_schema_alter(). + * Adds "live_domain" field to hosting_devshop_project table. */ function devshop_live_schema_alter(&$schema) { $schema['hosting_devshop_project']['fields']['live_domain'] = array( @@ -17,23 +18,19 @@ function devshop_live_schema_alter(&$schema) { } /** - * Implementation of hook_enable(). + * Implements hook_enable(). */ function devshop_live_enable() { $ret = array(); - db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); - return $ret; } /** - * Implementation of hook_disable(). + * Implementation of hook_uninstall(). */ -function devshop_live_disable() { +function devshop_live_uninstall() { $ret = array(); - db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); - return $ret; } From 842d668f5e4d3892e4bc9205c2e808185f2ce6a1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 09:44:52 -0400 Subject: [PATCH 0610/3476] better description --- devshop_live/devshop_live.info | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_live/devshop_live.info b/devshop_live/devshop_live.info index 20e55ef61..2c89b516e 100644 --- a/devshop_live/devshop_live.info +++ b/devshop_live/devshop_live.info @@ -1,5 +1,5 @@ name = DevShop Live -description = A DevShop module to Going Live. +description = Add a live domain to projects. core = 6.x package = DevShop dependencies[] = devshop_projects From 535f31e4aa362c4ed95259c923114753ee6baa03 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 09:55:24 -0400 Subject: [PATCH 0611/3476] better comments, getting rid of uneeded includes --- devshop_live/devshop_live.module | 98 ++++++++++++++++++++++++++------ devshop_live/inc/forms.inc | 48 ---------------- devshop_live/inc/nodes.inc | 30 ---------- 3 files changed, 81 insertions(+), 95 deletions(-) diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index 2f92806ad..0ca941599 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -1,25 +1,13 @@ type == 'site') { @@ -29,12 +17,13 @@ function devshop_live_add_domain($node, $domain) { return TRUE; } } - return FALSE; } /** - * Implement of hook_devshop_projects_page(). + * Implements hook_devshop_projects_page(). + * + * Adds a link to the live domain. */ function devshop_live_devshop_projects_page($rows, $header) { $new_rows = array(); @@ -57,3 +46,78 @@ function devshop_live_devshop_projects_page($rows, $header) { return array('rows' => $new_rows, 'header' => $header); } + +/** + * Implements hook_nodeapi(). + */ +function devshop_live_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { + if ($node->type == 'project') { + if (in_array($op, array('insert', 'update'))) { + //Load all information + $node_p = node_load($node->nid); + + if ($node->live_domain && !isset($node_p->project_objects['site'])) { + $live_nid = array_search('live', $node_p->project_objects['site']); + + if ($live_nid) { + $live_node = node_load($live_nid); + devshop_live_add_domain($live_node, $node->live_domain); + } + } + } + if ($op == 'view' && !empty($node->live_domain)) { + $url = 'http://' . $node->live_domain; + + $node->content['info']['live_domain'] = array( + '#type' => 'item', + '#title' => t('Live Site'), + '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), + '#weight' => -9, + ); + } + } +} + +/** + * Implements hook_form_alter(). + */ +function devshop_live_form_alter(&$form, &$form_state, $form_id) { + //Add field live_domain to edit form + if ($form_id == 'project_node_form') { + $node = $form['#node']; + + $form['live_domain'] = array( + '#type' => 'textfield', + '#title' => t('Live domain'), + '#description' => t('The live domain for this project.'), + '#size' => 40, + '#default_value' => $node->live_domain, + '#maxlenght' => 255, + '#weight' => 3, + ); + } + + if ($form_id == 'devshop_project_create_step_settings') { + $project = $form_state['project']; + + $form['live_domain'] = array( + '#type' => 'textfield', + '#title' => t('Live domain'), + '#description' => t('The live domain for this project.'), + '#size' => 40, + '#default_value' => $project->live_domain, + '#maxlenght' => 255, + ); + + $form['#submit'][] = 'devshop_live_create_form_submit'; + } +} + +/** + * Extra submit handler for Project node form. + */ +function devshop_live_create_form_submit($form, &$form_state) { + $project = &$form_state['project']; + + $project->live_domain = $form_state['values']['live_domain']; +} diff --git a/devshop_live/inc/forms.inc b/devshop_live/inc/forms.inc index ec2efcca8..e69de29bb 100644 --- a/devshop_live/inc/forms.inc +++ b/devshop_live/inc/forms.inc @@ -1,48 +0,0 @@ - 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain. If you still do not know you can input it later.'), - '#size' => 40, - '#default_value' => $node->live_domain, - '#maxlenght' => 255, - '#weight' => 3, - ); - } - - if ($form_id == 'devshop_project_create_step_settings') { - $project = $form_state['project']; - - $form['live_domain'] = array( - '#type' => 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain. If you still do not know you can input it later.'), - '#size' => 40, - '#default_value' => $project->live_domain, - '#maxlenght' => 255, - ); - - $form['#submit'][] = 'devshop_live_create_form_submit'; - } -} - -function devshop_live_create_form_submit($form, &$form_state) { - $project = &$form_state['project']; - - $project->live_domain = $form_state['values']['live_domain']; -} diff --git a/devshop_live/inc/nodes.inc b/devshop_live/inc/nodes.inc index b9c0840d1..e5e456cd5 100644 --- a/devshop_live/inc/nodes.inc +++ b/devshop_live/inc/nodes.inc @@ -7,33 +7,3 @@ * */ -/** - * Implementation of hook_nodeapi(). - */ -function devshop_live_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { - if ($node->type == 'project') { - if (in_array($op, array('insert', 'update'))) { - //Load all information - $node_p = node_load($node->nid); - - if ($node->live_domain && !isset($node_p->project_objects['site'])) { - $live_nid = array_search('live', $node_p->project_objects['site']); - - if ($live_nid) { - $live_node = node_load($live_nid); - devshop_live_add_domain($live_node, $node->live_domain); - } - } - } - if ($op == 'view' && !empty($node->live_domain)) { - $url = 'http://' . $node->live_domain; - - $node->content['info']['live_domain'] = array( - '#type' => 'item', - '#title' => t('Live Site'), - '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), - '#weight' => -9, - ); - } - } -} From b38c2b480a29c1e444860f0e5b9c932b26efaf2b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 09:55:36 -0400 Subject: [PATCH 0612/3476] deleting include files --- devshop_live/inc/forms.inc | 0 devshop_live/inc/nodes.inc | 9 --------- 2 files changed, 9 deletions(-) delete mode 100644 devshop_live/inc/forms.inc delete mode 100644 devshop_live/inc/nodes.inc diff --git a/devshop_live/inc/forms.inc b/devshop_live/inc/forms.inc deleted file mode 100644 index e69de29bb..000000000 diff --git a/devshop_live/inc/nodes.inc b/devshop_live/inc/nodes.inc deleted file mode 100644 index e5e456cd5..000000000 --- a/devshop_live/inc/nodes.inc +++ /dev/null @@ -1,9 +0,0 @@ - Date: Thu, 16 May 2013 10:23:30 -0400 Subject: [PATCH 0613/3476] moving code around, better comments --- devshop_log/devshop_log.module | 59 ++++++++++++++-------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 623e1e837..e25347a54 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -14,9 +14,10 @@ function devshop_log_perm() { ); } - /** - * Implements hook_devshop_project_settings + * Implements hook_devshop_project_settings() + * + * Exposes hosting_logs settings to project settings. */ function devshop_log_devshop_project_settings($project_node = NULL){ if (module_exists('hosting_logs')){ @@ -33,35 +34,14 @@ function devshop_log_devshop_project_settings($project_node = NULL){ '#type' => 'checkbox', '#description' => t('Make error logs available at http://mysite/error.log'), ), - ); + ); } } - -/** - * The access callback for the Git Log menu tab. - * - * Make sure user has perms to veiw and and also that the node we are - * viewing is a platform node. - */ -function _devshop_log_access_callback($nid) { - - if (!user_access('view git commit logs')) { - return FALSE; - } - - $node = node_load($nid); - if (!$node || ($node->type != 'site' && $node->type != 'platform')) { - return FALSE; - } - return TRUE; -} - /** * Implementation of hook_menu() */ function devshop_log_menu() { - $items['admin/hosting/devshop/gitlog'] = array( 'title' => 'Commit Log', 'description' => 'Configure Commit Log View', @@ -71,27 +51,37 @@ function devshop_log_menu() { 'tab_parent' => 'admin/hosting/devshop', 'type' => MENU_LOCAL_TASK, ); - $items['node/%/logs/commits'] = array( 'title' => 'Commit Log', 'description' => 'View commit log entries', 'page callback' => 'devshop_log_page', 'page arguments' => array(1), 'type' => MENU_LOCAL_TASK, - 'access callback' => '_devshop_log_access_callback', + 'access callback' => 'devshop_log_page_access', 'access arguments' => array(1), 'weight' => -4, ); - return ($items); + return $items; } - /** - * View Log Page + * The access callback for the Git Log menu tab. + * + * Make sure user has perms to veiw and and also that the node we are + * viewing is a platform node. */ -function devshop_log_page($nid) { - return check_markup(_devshop_log_load($nid)); +function devshop_log_page_access($nid) { + + if (!user_access('view git commit logs')) { + return FALSE; + } + + $node = node_load($nid); + if (!$node || ($node->type != 'site' && $node->type != 'platform')) { + return FALSE; + } + return TRUE; } /** @@ -112,11 +102,10 @@ function devshop_git_log_settings() { } -/* - * This is a hack which loads the git commit log for the given - * platform node. +/** + * Return output from "git log" for this site or platform. */ -function _devshop_log_load($nid) { +function devshop_log_page($nid) { $node = node_load($nid); if($node->type == 'site') { From 1d79f397897385365d3bb3971933be0523d671b5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:26:29 -0400 Subject: [PATCH 0614/3476] adding readme --- devshop_log/README.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 devshop_log/README.txt diff --git a/devshop_log/README.txt b/devshop_log/README.txt new file mode 100644 index 000000000..6da6e6457 --- /dev/null +++ b/devshop_log/README.txt @@ -0,0 +1,7 @@ +DevShop Log +=========== + +This module simply provides a page on aegir sites or platforms that contains +the git log from that site's codebase. + +@TODO: Move the UI underneath projects, instead of sites/platforms. From 36a912b74b47dd0b9f7f0b6a5c73fdc3741dc980 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:32:26 -0400 Subject: [PATCH 0615/3476] comments and whitespace --- devshop_pull/devshop_pull.module | 88 +++++++++++++------------------- 1 file changed, 35 insertions(+), 53 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 5913214f4..bc5522ea3 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -1,15 +1,12 @@ 'batch', - 'name' => t('Pull queue'), - 'description' => t('Run git pull on projects configured to do so.'), + 'type' => 'batch', + 'name' => t('Pull queue'), + 'description' => t('Run git pull on projects configured to do so.'), 'total_items' => devshop_pull_get_platforms(10), - 'frequency' => strtotime("1 minute", 0), - 'singular' => t('project'), + 'frequency' => strtotime("1 minute", 0), + 'singular' => t('project'), 'plural' => t('projects'), ); return $items; } /** - * Implements hook_devshop_project_settings + * Implements hook_devshop_project_settings() */ function devshop_pull_devshop_project_settings(){ return array( @@ -107,23 +104,23 @@ function devshop_pull_devshop_project_settings(){ * Implements hook_form_alter(). */ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { - + // On Projects, add "Choose Pull Method" if ($form_id == 'project_node_form') { // Get node $node = $form['#node']; - + // Get default value if (is_null($node->pull_method)) { $node->pull_method = DEVSHOP_PULL_DISABLED; } - + //All settings git pull in project page $form['git_settings'] = array( '#type' => 'fieldset', '#title' => t('Git Settings'), ); - + $form['git_settings']['pull_method'] = array( '#title' => 'Automatic Git Pull Method', '#type' => 'radios', @@ -148,7 +145,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { 'onclick' => 'this.select()', ), ); - + // @TODO: is there a better way to save certain values? We lose data without these. $form['git_settings']['last_pull'] = array( '#type' => 'value', @@ -167,20 +164,22 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { /** * Implements hook_nodeapi() + * + * @todo Break this out into aegir's subhook hook_nodeapi_OP_TYPE() */ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { - + // PROJECTS if ($node->type == 'project'){ - + // View Project if ($op == 'view' && $node->pull_method == DEVSHOP_PULL_CALLBACK){ module_load_include('inc', 'devshop_pull'); $url = _devshop_pull_callback_url($node); $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); - + $status = (int) $node->last_pull_status; - + // If access denied, provide link to settings page if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ $output = '' . t('Access Denied') . '
'; @@ -216,7 +215,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { return $data; } } - + // Insert Project elseif ($op == 'insert'){ db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, %d, "%s")', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); @@ -228,18 +227,18 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { // project exists db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, "%s", %d)', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); - + } // Delete Project elseif ($op == 'delete'){ db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - + } } - + // PLATFORMS elseif ($node->type == 'platform'){ - + // Load Platform if ($node->type == 'platform' && $op == 'load'){ $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); @@ -248,18 +247,18 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { return $data; } } - + // Insert Platform elseif ($op == 'insert'){ db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); } - + // Update Platform elseif ($op == 'update'){ db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); } - + // Delete Project elseif ($op == 'delete'){ db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); @@ -269,7 +268,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { /** * Get a number of platforms that have their pull queue enabled. - * + * * @param $limit * Limit to a maximum of this number of platforms. * @return @@ -283,32 +282,15 @@ function devshop_pull_get_platforms($limit = 5) { } /** - * Implementation of hook_hosting_TASK_TYPE_task_rollback(). - */ -function devshop_pull_hosting_devshop_pull_task_rollback($task, $data) { - $project = node_load($task->ref->project_nid); - watchdog('devshop', 'Pull FAILED on project: @alias', array('@alias' => $project->title)); - db_query('UPDATE {hosting_devshop_pull_projects} SET last_pull = %d, last_pull_status = %d WHERE project_nid = %d', time(), 0, $task->ref->project_nid); -} - -/** - * Implementation of hook_post_hosting_TASK_TYPE_task(). - */ -function devshop_pull_post_hosting_devshop_pull_task($task, $data) { - $project = node_load($task->ref->project_nid); - watchdog('devshop', 'Pull SUCCESS on project: @alias', array('@alias' => $project->title)); -} - -/** - * Implementation of hosting_QUEUE_TYPE_queue(). + * Implements hosting_QUEUE_TYPE_queue(). */ function hosting_pull_queue($count) { $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); - + while ($project = db_fetch_object($result)) { $project_node = node_load($project->project_nid); - + // Create the hosting task // @TODO: We maybe don't need to pass args here? its saved in the context // Check for environments set to pull @@ -321,7 +303,7 @@ function hosting_pull_queue($count) { $args = array(); $args['environments'] = implode(' ', $environments_to_pull); hosting_add_task($project_node->nid, 'devshop-pull', $args); - - module_invoke_all('devshop_pull', $project_node); + + module_invoke_all('devshop_pull', $project_node); } } From 352e734f13348b59f1cdc1abd2ab9c55de6549dd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:33:50 -0400 Subject: [PATCH 0616/3476] comments and whitespace --- devshop_pull/devshop_pull.inc | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 288c2905f..95c415d97 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -2,20 +2,15 @@ /** - * Menu callback that is invoked by GitHub WebHook facility to create - * a code pull task. - * - * @TODO: Save "Last Pull" and "Last Pull Status" when git pull task is complete. ... DONE?? seems to be in place + * URL callback that is invoked by git to create a code pull task. */ function devshop_pull_callback($project, $hash) { - - // @TODO: Should we switch to using node ID so we can use hook_menu's autoloader? - + // Load the project node & list of allowed IPs $project_node = hosting_context_load(str_replace('project_', '', $project)); $allowed_ips = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); array_filter(array_map('trim', $allowed_ips)); - + // Check for environments set to pull $environments_to_pull = array(); foreach ($project_node->settings as $env => $settings) { @@ -50,16 +45,16 @@ function devshop_pull_callback($project, $hash) { else { $message = "Commit Received! Invoked by " . ip_address(); $status = DEVSHOP_PULL_STATUS_OK; - + // @TODO: Check Payload Here... If we see a GitHub payload with branches, // filter out the $environments_to_pull based on those branches. - + // Create the hosting task // We need to pass environments so we can control what envs to pull based // on the data coming back from github! $args = array(); $args['environments'] = implode(' ', $environments_to_pull); - + // Always clear cache... // @TODO: Should we make this a setting? "What to do on auto-pull?" $args['cache'] = 1; @@ -74,14 +69,14 @@ function devshop_pull_callback($project, $hash) { $record->last_pull = time(); $record->last_pull_status = $status; $record->last_pull_ip = ip_address(); - + drupal_write_record('hosting_devshop_pull_projects', $record, array('project_nid')); } - // Output a message, no matter what. + // Output a message, no matter what. watchdog('devshop_pull', $message, array(), WATCHDOG_INFO); print $message; - + // Save a variable to help when using the settings page. variable_set('devshop_pull_last_ip', ip_address()); } @@ -106,7 +101,7 @@ function _devshop_pull_hash_create($node) { /** * Prepares a "Pull Code" task for a project. - * + * * @param $project_nid * A project nid. * @@ -115,9 +110,9 @@ function _devshop_pull_hash_create($node) { function devshop_pull_project($project_nid) { // Search platforms with pull enabled for this project $results = db_query("SELECT environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND p.project_nid = %d", $project_nid); - + $args = array('environments' => ''); - + while ($info = db_fetch_object($results)){ $args['environments'] .= $info->environment .' '; } From e4e5d5f4f9a172daf4a7d492605c7334ecf1f921 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:34:05 -0400 Subject: [PATCH 0617/3476] comments and whitespace --- devshop_pull/devshop_pull.settings.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index 63e0c9b11..24e99587e 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -18,13 +18,13 @@ function devshop_pull_settings_form() { '#default_value' => variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS), '#rows' => 6, ); - + // we have a few bullet points $items = array(); $items[] = t('Enter the IP addresses that are allowed to trigger a "Pull Code" task.'); $items[] = t('GitHub post-receive callback servers are: %github_ips.', array('%github_ips' => variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); $items[] = t('Your local computer\'s IP address is %ip. ', array('%ip' => $_SERVER['REMOTE_ADDR'])); - + // If there was a last ip logged, display it here so its easy to add to the list. $last_ip = variable_get('devshop_pull_last_ip', ''); if ($last_ip){ @@ -33,6 +33,6 @@ function devshop_pull_settings_form() { $items[] = t('No requests ever detected. If you add the trigger URL for a project to your git repo host, the IP will be logged and displayed here.'); } $form['devshop_pull_ip_acl']['#description'] = theme('item_list', $items); - + return system_settings_form($form); } From 5ee5921d1410b70f58256aa575caa456f9e18560 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:35:01 -0400 Subject: [PATCH 0618/3476] whitespace --- devshop_pull/devshop_pull.install | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index ffe61f196..bc63d7bd2 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -5,7 +5,7 @@ */ /** - * Implementation of hook_schema(). + * Implements hook_schema(). */ function devshop_pull_schema() { $schema['hosting_devshop_pull_projects'] = array( @@ -70,7 +70,7 @@ function devshop_pull_schema() { /** * Implementation of hook_install(). */ -function devshop_pull_install() { +function devshop_pull_install() { // Create tables. drupal_install_schema('devshop_pull'); } @@ -78,7 +78,7 @@ function devshop_pull_install() { /** * Implementation of hook_uninstall(). */ -function devshop_pull_uninstall() { +function devshop_pull_uninstall() { // Create tables. drupal_uninstall_schema('devshop_pull'); } @@ -98,4 +98,4 @@ function devshop_pull_update_6001(){ $ret = array(); db_add_field($ret, 'hosting_devshop_pull_projects', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '', 'length' => 15)); return $ret; -} \ No newline at end of file +} From d31bbeeecefa49e5dd371c357c6e238164b05806 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:44:08 -0400 Subject: [PATCH 0619/3476] adding readme for devshop live --- devshop_live/README.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 devshop_live/README.txt diff --git a/devshop_live/README.txt b/devshop_live/README.txt new file mode 100644 index 000000000..9eac83331 --- /dev/null +++ b/devshop_live/README.txt @@ -0,0 +1,5 @@ +DevShop Live +=========== + +Tracks a live domain for each project, and automatically creates aliases to that domain for +each environment in the project. From 02cb31f3749453f544b53e7905287bcaf0bf32fc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:44:21 -0400 Subject: [PATCH 0620/3476] adding readme to devshop_pull --- devshop_pull/README.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 devshop_pull/README.txt diff --git a/devshop_pull/README.txt b/devshop_pull/README.txt new file mode 100644 index 000000000..89a4f7b34 --- /dev/null +++ b/devshop_pull/README.txt @@ -0,0 +1,11 @@ +DevShop Pull +============ + +Provides a way for environments to stay up to date with the git repository. + +Each project can configure to Pull on Queue or Pull on URL Callback. + +Pull on Queue will trigger Pull Code tasks on a regular basis using Hosting +Queues. Pull on URL Callback provides a URL that you can add to your git host +to ping on receiving a commit. + From ab6c500838f6b39cff9ca824eedacbbfb95367ba Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 10:53:34 -0400 Subject: [PATCH 0621/3476] adding github instructions --- devshop_pull/README.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devshop_pull/README.txt b/devshop_pull/README.txt index 89a4f7b34..1d64afa10 100644 --- a/devshop_pull/README.txt +++ b/devshop_pull/README.txt @@ -9,3 +9,15 @@ Pull on Queue will trigger Pull Code tasks on a regular basis using Hosting Queues. Pull on URL Callback provides a URL that you can add to your git host to ping on receiving a commit. + +GitHub Setup +------------ + +1. Visit your repos page: http://github.com/YOURNAME/YOURREPO +2. Click "Settings". +3. Click "Service Hooks". +4. Click "WebHook URLs" +5. Copy and paste your project's Git Pull Trigger URL into the URL field of the + WebHook URLs page. +6. Click "Test Hook" to run a test, then check your DevShop project to ensure a + Pull Code task was triggered. From 541c58161f37f453954ef78925df26cb92fc19bc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 May 2013 11:00:41 -0400 Subject: [PATCH 0622/3476] whitespace --- devshop_projects/devshop_projects.module | 74 +++++++++++------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 4d8de3e2e..4c54e8682 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -1,5 +1,4 @@ 'Start New Project', 'type' => MENU_LOCAL_TASK, - 'title' => t('Start a new Project'), - 'title callback' => 'check_plain', - 'page callback' => 'devshop_projects_add', - 'page arguments' => array(3), - 'access arguments' => array('create project'), - 'description' => 'Start a new Drupal website project.', + 'title' => t('Start a new Project'), + 'title callback' => 'check_plain', + 'page callback' => 'devshop_projects_add', + 'page arguments' => array(3), + 'access arguments' => array('create project'), + 'description' => 'Start a new Drupal website project.', 'file' => 'create-wizard.inc', 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', ); - + // Ajax endpoint for reloads $items['hosting/projects/add/status'] = array( 'page callback' => 'devshop_projects_add_status', - 'access callback' => 'node_access', + 'access callback' => 'node_access', 'access arguments' => array('create', 'project'), 'file' => 'create-wizard.inc', 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', ); - + // hosting tasks ajax pages. foreach (hosting_available_tasks('project') as $task => $info){ $path = 'node/%/project_' . $task; @@ -134,8 +131,7 @@ function devshop_projects_menu() { ); $items[$path] = array_merge($items[$path], $info); } - - return ($items); + return $items; } @@ -148,7 +144,7 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ } else { $branch_options = array(); } - + $settings = array(); $settings['git_branch'] = array( '#title' => t('Git Branch'), @@ -158,24 +154,22 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ ); $http_servers = hosting_get_servers('http'); - if (count($http_servers)) { $settings['web_server'] = array( '#title' => t('Web server'), '#node_type' => 'platform', - '#type' => 'select', + '#type' => count($http_servers) == 1? 'hidden': 'select', '#options' => $http_servers, ); } $db_servers = hosting_get_servers('db'); - if (count($db_servers)) { $settings['db_server'] = array( '#title' => t('Database server'), '#node_type' => 'site', '#type' => 'select', - '#options' => $db_servers, + '#options' => count($db_servers) == 1? 'hidden': 'select', ); } @@ -201,7 +195,7 @@ function devshop_projects_add($step = NULL){ * Replacement for hosting_task_confirm_form() * * @TODO: Remove once http://drupal.org/node/1861898 is committed. - */ + */ function devshop_projects_hosting_task_confirm_form_page($nid, $task){ $node = node_load($nid); return drupal_get_form('hosting_task_confirm_form', $node, $task); @@ -221,7 +215,7 @@ function devshop_projects_menu_alter(&$items) { // Make project node pages more user-friendly. $items['node/%node/view']['title callback'] = 'devshop_hosting_project_tab_title'; $items['node/%node/view']['title arguments'] = array('View', 1); - + $items['node/%node/edit']['title callback'] = 'devshop_hosting_project_tab_title'; $items['node/%node/edit']['title arguments'] = array('Edit', 1); @@ -229,15 +223,15 @@ function devshop_projects_menu_alter(&$items) { /** * Tab title replacer - */ + */ function devshop_hosting_project_tab_title($default, $node){ if ($default == 'View' && $node->type == 'project'){ - return t('Dashboard'); + return t('Dashboard'); } if ($default == 'Edit' && $node->type == 'project'){ - return t('Settings'); + return t('Settings'); } - + // Otherwise, just return the page text return t($default); } @@ -272,7 +266,7 @@ function devshop_hosting_task_menu_access($node, $task) { if (user_access("create " . $task . " task")) { if ($node->type == 'project') { - + // If Commit Features task, make sure features module is present if ($task == 'devshop-commit'){ return _devshop_projects_project_has_module($node, 'features'); @@ -328,8 +322,8 @@ function devshop_project_status($node){ } else { $sites_ready = FALSE; } - - + + // @TODO: This is so rough! We can do better. return $sites_ready? 'sites_ready': ( $sites_installing? 'sites_installing': ( @@ -434,14 +428,14 @@ function _devshop_projects_project_has_module($node, $module) { * Delete a directory recursively */ function _devshop_rrmdir($dir) { - if (is_dir($dir)) { - $objects = scandir($dir); - foreach ($objects as $object) { - if ($object != "." && $object != "..") { - if (filetype($dir."/".$object) == "dir") _devshop_rrmdir($dir."/".$object); else unlink($dir."/".$object); - } - } - reset($objects); - rmdir($dir); - } -} + if (is_dir($dir)) { + $objects = scandir($dir); + foreach ($objects as $object) { + if ($object != "." && $object != "..") { + if (filetype($dir."/".$object) == "dir") _devshop_rrmdir($dir."/".$object); else unlink($dir."/".$object); + } + } + reset($objects); + rmdir($dir); + } +} From c7282479c87fb679e2de146efeb83e1c7507e262 Mon Sep 17 00:00:00 2001 From: Herman van Rink Date: Fri, 17 May 2013 12:55:55 +0200 Subject: [PATCH 0623/3476] Fix db_servers list, #options was a strin instead of the array. --- devshop_projects/devshop_projects.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 4c54e8682..87caad9ea 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -168,8 +168,8 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ $settings['db_server'] = array( '#title' => t('Database server'), '#node_type' => 'site', - '#type' => 'select', - '#options' => count($db_servers) == 1? 'hidden': 'select', + '#type' => count($db_servers) == 1? 'hidden': 'select', + '#options' => $db_servers, ); } From e4a95ef6b51d404db307be1d25902baccec539c4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 17 May 2013 15:40:29 -0400 Subject: [PATCH 0624/3476] whitespace --- devshop_projects/inc/create-wizard.inc | 116 ++++++++++++------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 3fb4e9134..2b48291b5 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -14,7 +14,7 @@ function devshop_projects_create_wizard($step = NULL){ $form_state = array( 'cache name' => NULL, ); - + // Skip step 2 if needed $skip = variable_get('devshop_projects_skip_settings', TRUE); if ($skip) { @@ -52,7 +52,7 @@ function devshop_projects_create_wizard($step = NULL){ $project->step = $step; ctools_object_cache_set('project', $form_state['cache name'], $project); } - + // Check verification status // @TODO: We should be able to get the error messages... $project_node = node_load($project->project_nid); @@ -60,16 +60,16 @@ function devshop_projects_create_wizard($step = NULL){ $tasks = hosting_task_fetch_tasks($project_node->nid); } if (isset($tasks['verify']['nid'])){ - + } - + // Get "verify" task status for the project $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; $project->verify_task_nid = $tasks['verify']['nid']; - + // If project verification failed, we need to ask for a new git url. if ($project->verify_task_status == HOSTING_TASK_ERROR && !empty($project_node->nid)){ - $project->verify_error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, 'error')); + $project->verify_error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, 'error')); // If not on the first step, go to it. if ($step != current(array_keys($form_info['order']))){ drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); @@ -180,7 +180,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { // Removing last step session variable. unset($_SESSION['last_step']); - + // Friendly message drupal_set_message(t('Your project has been created. Once installed, your sites will be available.')); } @@ -227,14 +227,14 @@ function devshop_projects_create_wizard_cancel(&$form_state) { */ function devshop_project_create_step_git(&$form, &$form_state) { $project = &$form_state['project']; - + if ($project->verify_error){ $form['node'] = array( '#value' => '
' . $project->verify_error . '
', '#type' => 'markup', ); } - + $form['git_url'] = array( '#type' => 'textfield', '#required' => 1, @@ -264,10 +264,10 @@ function devshop_project_create_step_git(&$form, &$form_state) { $form['title_display'] = $form['title']; $form['title_display']['#type'] = 'item'; } - + // Display helpful tips for connecting. $pubkey = variable_get('devshop_public_key', ''); - + // If we don't yet have the server's public key saved as a variable... if (empty($pubkey)){ $output = t("For convenience, the server's SSH public key will be displayed here, once you run the following command on your server:"); @@ -366,7 +366,7 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $server = variable_get('devshop_project_master_url', $base_url); $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => strtolower($project_name), '@hostname' => $server)); $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); - + //Verify if is need to skip. $skip = variable_get('devshop_projects_skip_settings', TRUE); if ($skip) { @@ -455,11 +455,11 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { function devshop_project_create_step_environments(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); - + if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) { $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; $project->no_next = TRUE; - + // If we are all queued, show a friendly message if ($project->verify_task_status == HOSTING_TASK_QUEUED){ $task = node_load($project->verify_task_nid); @@ -468,7 +468,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; } } - + $form['note'] = array( '#type' => 'markup', '#value' => $note, @@ -479,17 +479,17 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); // Add code to reload the page when complete. devshop_form_reloader($form, 'project'); - - + + return; } - + // this JS handles the form element hiding/showing $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; drupal_add_js($path); $settings = module_invoke_all('devshop_project_settings', $project_node); - + $form['environments'] = array( '#theme' => 'devshop_projects_create_settings_form', '#tree' => TRUE, @@ -509,7 +509,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { } else { $env_title = $env; } - + $form['environments'][$env] = array( '#tree' => TRUE, '#type' => 'fieldset', @@ -538,12 +538,12 @@ function devshop_project_create_step_environments(&$form, &$form_state) { //Now add button. $form['add_environment'] = array( '#type' => 'submit', - '#value' => t('Add environment'), + '#value' => t('Add environment'), '#name' => 'add_environment', '#submit' => array('devshop_projects_create_wizard_add_new_environment'), '#prefix' => '
', '#suffix' => '
', - ); + ); } } @@ -560,7 +560,7 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); return; } - + // Changes NEW environment data to $title if ($values['environments']['NEW']['title']){ $new_env = $values['environments']['NEW']['title']; @@ -569,18 +569,18 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // Create the next NEW environment. Unset makes sure its always last. unset($values['environments']['NEW']); - - // If "add environment" button clicked, add another row. + + // If "add environment" button clicked, add another row. if ($form_state['clicked_button']['#name'] == 'add_environment'){ $values['environments']['NEW'] = array(); } } else { unset($values['environments']['NEW']); } - + // Reset project environments $project->environments = array(); - + // Check environment titles foreach ($values['environments'] as $env => $env_settings) { // Check for illegal chars @@ -594,9 +594,9 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) } } } - - - + + + // Reject if empty if (count($project->environments) < 1){ if ($form_state['clicked_button']['#name'] == 'add_environment'){ @@ -617,7 +617,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $values = $form_state['values']; $settings = module_invoke_all('devshop_project_settings', $project_node); - + // Create these platforms, if they don't exist yet foreach ($project->environments as $name => $environment) { $platform_nid = $project_node->environments[$name]['platform']; @@ -635,10 +635,10 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Save the damn platform nodes $platform = new stdClass; - + // Platform name $platform->title = $project->title . '_' . $environment['title']; - + // Platform publish_path if ($project->drupal_path) { $platform->publish_path = $project->code_path . '/' . $environment['title'] . '/' . $project->drupal_path; @@ -646,7 +646,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { else { $platform->publish_path = $project->code_path . '/' . $environment['title']; } - + // Other attributes $platform->web_server = $environment['web_server']; $platform->git_branch = $environment['git_branch']; @@ -654,7 +654,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform->project = $project->title; $platform->environment = $environment['title']; $platform->drupal_path = $project->drupal_path; - + foreach ($settings as $setting_name => $element){ $platform->{$setting_name} = $environment[$setting_name]; } @@ -665,7 +665,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // For all removed platforms, trigger a delete task $removed_environments = array_diff_key($project_node->environments, $values['environments']); - + foreach ($removed_environments as $environment_name => $settings) { // @TODO: Determine what to do here based on task status... // if verify task hasn't even run yet (and has never run) we can just delete @@ -693,8 +693,8 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // OUTPUT // @TODO: Clean this up!! - - // @TODO: This is old code that is never seen. User ends up on project page. + + // @TODO: This is old code that is never seen. User ends up on project page. // Perhaps we should keep people on this page until the sites are all installed? No, because we want to free up the wizard for another project create... instead lets fix the project node page to display proper status. if ($project_node->project_status == 'sites_installing') { $form['note'] = array( @@ -719,9 +719,9 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); $all_tasks_queued = TRUE; $all_tasks_succeeded = TRUE; - + foreach ($project_node->environments as $name => $environment){ - + // Get platform and latest verify task. $platform_nid = $environment['platform']; $platform = node_load($platform_nid); @@ -759,16 +759,16 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $completed = TRUE; $all_tasks_succeeded = FALSE; $available_profiles = array(); - + $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); - + $row['version'] = array( 'data' => t('Platform verification failed: %error', array('%error' => $error)), 'colspan' => 2, ); - + // @TODO: Get task log error message - + } // If platform is still processing: elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { @@ -776,19 +776,19 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $row['version'] = '...'; $row['profiles'] = '...'; } - + // If a single task is not queued, $all_tasks_queued == FALSE if ($task->task_status != HOSTING_TASK_QUEUED){ $all_tasks_queued = FALSE; } - + // Add hosting task status. $row['status'] = _hosting_parse_error_code($task->task_status); // Store rows for display $rows[] = $row; } // end foreach platform - + // Output our table. $form['platforms'] = array( '#type' => 'markup', @@ -799,9 +799,9 @@ function devshop_project_create_step_sites(&$form, &$form_state) { if (!$completed){ $project->no_finish = TRUE; $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; - + // If we are all queued, show a friendly message - + $time_ago = time() - $task->created; if ($all_tasks_queued && $time_ago > 120) { $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; @@ -877,42 +877,42 @@ function devshop_project_create_step_sites_submit(&$from, &$form_state) { function devshop_projects_add_status($type = 'platform'){ $return = array(); - + // Get Project Cache ctools_include('wizard'); ctools_include('object-cache'); $project = ctools_object_cache_get('project', NULL); - - + + $project_node = node_load($project->project_nid); - + $all_tasks_completed = TRUE; $nids = array(); - + // When checking project... if ($type == 'project') { $nids = array($project_node->nid); } - + // When checking platforms... if ($type == 'platform') { foreach ($project_node->environments as $name => $environment){ $nids[] = $environment['platform']; } } - + // Check verification task for all nids foreach ($nids as $nid){ $task = hosting_get_most_recent_task($nid, 'verify'); $return['tasks'][$nid] = _hosting_parse_error_code($task->task_status); - + // If task is not completed, mark all tasks not complete. if ($task->task_status == HOSTING_TASK_SUCCESS || $task->task_status == HOSTING_TASK_ERROR) { continue; } else { $all_tasks_completed = FALSE; } - + } $return['tasks_complete'] = $all_tasks_completed; drupal_json($return); From fdac41bd0f7555859f2f35f12e18b2209bdcfa2a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 21 May 2013 16:49:41 -0700 Subject: [PATCH 0625/3476] Adding user register link, if allowed. --- devshop_hosting.module | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_hosting.module b/devshop_hosting.module index 3c0e17f8c..268f46e00 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -74,6 +74,11 @@ function devshop_hosting_menu_alter(&$items){ */ function devshop_hosting_form_user_login_alter(&$form){ $form['pass']['#description'] .= ' ' . l(t('Forgot your Password?'), 'user/password'); + + // Add user register link + if (user_register_access()){ + $form['submit']['#suffix'] = t('or') . ' ' . l(t('Signup'), 'user/register'); + } } /** From b0f081506f870879aae61339cbcc0b5afc0bb4ae Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 21 May 2013 17:19:03 -0700 Subject: [PATCH 0626/3476] removing some text from user register form. --- devshop_hosting.module | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/devshop_hosting.module b/devshop_hosting.module index 268f46e00..cbc417952 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -91,6 +91,14 @@ function devshop_hosting_form_user_login_block_alter(&$form){ } } +/** + * Implements hook_form_alter() for user_register. + * Removes the hosting module help. + */ +function devshop_hosting_form_user_register_alter(&$form){ + $form['user_registration_help'] = array(); +} + /** * Implements hook_block() * From 7f105db12c9be60ef57d85bee4209e6fe6ccbbc5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 21 May 2013 17:39:57 -0700 Subject: [PATCH 0627/3476] removing hook form alter for user register form --- devshop_hosting.module | 8 -------- 1 file changed, 8 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index cbc417952..268f46e00 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -91,14 +91,6 @@ function devshop_hosting_form_user_login_block_alter(&$form){ } } -/** - * Implements hook_form_alter() for user_register. - * Removes the hosting module help. - */ -function devshop_hosting_form_user_register_alter(&$form){ - $form['user_registration_help'] = array(); -} - /** * Implements hook_block() * From d946880dad172a896a9194568eec8b1ef1aecd38 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 21 May 2013 17:46:05 -0700 Subject: [PATCH 0628/3476] access check for our task block --- devshop_hosting.module | 74 +++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 268f46e00..7c4285127 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -147,51 +147,51 @@ function devshop_hosting_block($op = 'list', $delta = 0, $edit = NULL) { * @todo: Ajaxify. */ function devshop_hosting_task_queue_block() { - drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js'); + if (user_access('access task logs')){ + drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js'); - $settings['hostingTaskRefresh'] = array( - 'queueBlock' => 1, - ); - drupal_add_js($settings, 'setting'); + $settings['hostingTaskRefresh'] = array( + 'queueBlock' => 1, + ); + drupal_add_js($settings, 'setting'); - $tasks['processing'] = hosting_get_tasks('task_status', HOSTING_TASK_PROCESSING, 5); - $tasks['queued'] = hosting_get_tasks('task_status', HOSTING_TASK_QUEUE, 5); + $tasks['processing'] = hosting_get_tasks('task_status', HOSTING_TASK_PROCESSING, 5); + $tasks['queued'] = hosting_get_tasks('task_status', HOSTING_TASK_QUEUE, 5); - $total_tasks = count($tasks['queued']) + count($tasks['processing']); - $status = format_plural($total_tasks, '1 active task.', '@count active tasks.'); + $total_tasks = count($tasks['queued']) + count($tasks['processing']); + $status = format_plural($total_tasks, '1 active task.', '@count active tasks.'); - if ($total_tasks == 0){ - $status_class = 'inactive'; - } else { - $status_class = 'active'; - } + if ($total_tasks == 0){ + $status_class = 'inactive'; + } else { + $status_class = 'active'; + } - // Build our own table - foreach ($tasks as $task_status => $nodes){ - foreach ($nodes as $node) { - $row = array(); - $row['type'] = array( - 'data' => drupal_ucfirst(str_replace('_', ' ', $node->task_type)) . ' ' . _hosting_node_link($node->rid), - 'class' => 'hosting-status', - ); - $class = hosting_task_status_class($node->task_status); - $rows[] = array( - 'data' => $row, - 'class' => $class, - ); + // Build our own table + foreach ($tasks as $task_status => $nodes){ + foreach ($nodes as $node) { + $row = array(); + $row['type'] = array( + 'data' => drupal_ucfirst(str_replace('_', ' ', $node->task_type)) . ' ' . _hosting_node_link($node->rid), + 'class' => 'hosting-status', + ); + $class = hosting_task_status_class($node->task_status); + $rows[] = array( + 'data' => $row, + 'class' => $class, + ); + } } - } - // Build Strings - if (user_access('access task logs')){ + // Build Strings $link = l('Tasks Logs', 'hosting/queues/tasks', array('attributes' => array('class' => 'task-logs-link'))); - } - if ($rows){ - $table = theme('table', $headers, $rows, array('class' => 'hosting-table')); - } - // Output our "template" - return << 'hosting-table')); + } + + // Output our "template" + return <<$status
$table
@@ -199,5 +199,5 @@ function devshop_hosting_task_queue_block() {
HTML; - + } } From 81f236eb00c52676476ec5324f8db2ee24b080e1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 21 May 2013 17:48:57 -0700 Subject: [PATCH 0629/3476] rewording signup link --- devshop_hosting.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 7c4285127..528ab2ef5 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -77,7 +77,7 @@ function devshop_hosting_form_user_login_alter(&$form){ // Add user register link if (user_register_access()){ - $form['submit']['#suffix'] = t('or') . ' ' . l(t('Signup'), 'user/register'); + $form['submit']['#suffix'] = t('or') . ' ' . l(t('Create an Account'), 'user/register'); } } From 913b756aa82f0de4b580f668f41f0f624f6064f7 Mon Sep 17 00:00:00 2001 From: Aegir user Date: Wed, 22 May 2013 01:12:57 +0000 Subject: [PATCH 0630/3476] removing untested crappy commits --- devshop_projects/devshop_projects.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 4c54e8682..5ec323f71 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -158,7 +158,7 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ $settings['web_server'] = array( '#title' => t('Web server'), '#node_type' => 'platform', - '#type' => count($http_servers) == 1? 'hidden': 'select', + '#type' => 'select', '#options' => $http_servers, ); } @@ -169,7 +169,7 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ '#title' => t('Database server'), '#node_type' => 'site', '#type' => 'select', - '#options' => count($db_servers) == 1? 'hidden': 'select', + '#options' => $db_servers, ); } From ab32ca89c99945d8fe9e9e98d71a25a9b03dd70e Mon Sep 17 00:00:00 2001 From: Herman van Rink Date: Wed, 22 May 2013 14:03:32 +0200 Subject: [PATCH 0631/3476] Re-fix an update_hook in commit 3f41b8b5be12466072fd113b16ecc821b5f5b5f9 --- devshop_pull/devshop_pull.install | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index 31c385deb..626c14895 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -60,6 +60,11 @@ function devshop_pull_schema() { 'not null' => TRUE, 'default' => 0, ), + 'last_pull_ip' => array( + 'type' => 'varchar', + 'length' => 15, + 'not null' => TRUE, + ), ), 'primary key' => array('platform_nid'), ); @@ -84,7 +89,7 @@ function devshop_pull_uninstall() { } /** - * no-op: code fixed in devshop_pull_update_6001() + * no-op: code fixed in devshop_pull_update_6201() */ function devshop_pull_update_6000(){ $ret = array(); @@ -98,3 +103,16 @@ function devshop_pull_update_6001(){ db_add_field($ret, 'hosting_devshop_pull_projects', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '', 'length' => 15)); return $ret; } + +/** + * Add last_pull_ip column to our hosting_devshop_pull_platforms table. + * + * This was broken in devshop_pull_update_6000() and mistakenly removed completely instead of fixed. + */ +function devshop_pull_update_6201() { + $ret = array(); + if (!db_column_exists('hosting_devshop_pull_platforms', 'last_pull_ip')) { + db_add_field($ret, 'hosting_devshop_pull_platforms', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '', 'length' => 15)); + } + return $ret; +} From ebb60af22b3e082c136c34eaf242ec81597b0a81 Mon Sep 17 00:00:00 2001 From: wodenx Date: Thu, 13 Jun 2013 20:51:13 +0000 Subject: [PATCH 0632/3476] Issue #2018299 by wodenx: Added Specify allowed IP addresses using CIDR. --- devshop_pull/devshop_pull.inc | 27 +++++++++++++++++++++++++- devshop_pull/devshop_pull.module | 9 ++------- devshop_pull/devshop_pull.settings.inc | 2 +- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 95c415d97..7e5b29b13 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -37,7 +37,7 @@ function devshop_pull_callback($project, $hash) { $message = "Project $project has no environments configured to Pull Code on commit!"; } // Make sure the client's IP address is on the list - else if (!in_array(ip_address(), $allowed_ips)) { + else if (!devshop_pull_ip_match(ip_address(), $allowed_ips)) { $message = ip_address() . " is not authorized to invoke a Pull Code request."; $status = DEVSHOP_PULL_STATUS_ACCESS_DENIED; } @@ -81,6 +81,31 @@ function devshop_pull_callback($project, $hash) { variable_set('devshop_pull_last_ip', ip_address()); } +/** + * Check whether a given ip address matches a list of allowed ip addresses, some of which + * may be CIDR. + * + * @param $ip + * The ip addy to test. + * @param $list + * The list to test against. + */ +function devshop_pull_ip_match($ip, $list) { + foreach ($list as $cidr) { + if (trim($ip) === trim($cidr)) { + return TRUE; + } + @list($net, $mask) = explode('/', trim($cidr)); + if (isset($mask)) { + $bitmask = ~((1 << (32 - $mask)) - 1); + if ((ip2long($net) & $bitmask) == (ip2long($ip) & $bitmask)) { + return TRUE; + } + } + } + return FALSE; +} + /** * Create the full URL that is displayed in the project node view * and given to the GitHub WebHook to invoke a pull after a commit. diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index bc5522ea3..70c8cfcf6 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -20,13 +20,8 @@ define('DEVSHOP_PULL_STATUS_INVALID_CODE', 3); // These are github's Webhook callback IPs. // This list grows occaisonally, update it as needed. define('DEVSHOP_PULL_DEFAULT_ALLOWED_IPS'," -207.97.227.253 -50.57.128.197 -108.171.174.178 -50.57.231.61 -204.232.175.64 -192.30.252.0 -204.232.175.75 +204.232.175.64/27 +192.30.252.0/22 "); // The base URL to use for the Post Commit callback. diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index 24e99587e..b84c5e789 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -21,7 +21,7 @@ function devshop_pull_settings_form() { // we have a few bullet points $items = array(); - $items[] = t('Enter the IP addresses that are allowed to trigger a "Pull Code" task.'); + $items[] = t('Enter the IP addresses that are allowed to trigger a "Pull Code" task. You may specify address ranges using CIDR notation (e.g. 192.168.1.0/24).'); $items[] = t('GitHub post-receive callback servers are: %github_ips.', array('%github_ips' => variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); $items[] = t('Your local computer\'s IP address is %ip. ', array('%ip' => $_SERVER['REMOTE_ADDR'])); From 34486bf70a0fd917420b85968372f43bc36d6dc3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 26 Oct 2013 11:23:11 -0400 Subject: [PATCH 0633/3476] Display some help for certain errors and check time based on task->updated, not created --- devshop_projects/images/loading.gif | Bin devshop_projects/inc/create-wizard.inc | 10 ++++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) mode change 100755 => 100644 devshop_projects/images/loading.gif diff --git a/devshop_projects/images/loading.gif b/devshop_projects/images/loading.gif old mode 100755 new mode 100644 diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 2b48291b5..cefc28ab2 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -229,10 +229,16 @@ function devshop_project_create_step_git(&$form, &$form_state) { $project = &$form_state['project']; if ($project->verify_error){ - $form['node'] = array( + $form['note'] = array( '#value' => '
' . $project->verify_error . '
', '#type' => 'markup', ); + + // Display some help for certain errors + if ($project->verify_error == '[DEVSHOP] Error retrieving remote information: Host key verification failed. fatal: The remote end hung up unexpectedly'){ + $project->verify_error + } + $form['note']['#value'] .= '
' . t('You might have to authorize the host your are trying to connect to first. Add "StrictHostKeyChecking no" to your ~/.ssh/config file to avoid this for all domains.') . '
'; } $form['git_url'] = array( @@ -463,7 +469,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // If we are all queued, show a friendly message if ($project->verify_task_status == HOSTING_TASK_QUEUED){ $task = node_load($project->verify_task_nid); - $time_ago = time() - $task->created; + $time_ago = time() - $task->updated; if ($time_ago > 60) { $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; } From 02146eb251178216f4c51958ca31c14385b69cf1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 12 Feb 2014 17:56:36 -0500 Subject: [PATCH 0634/3476] removing unneeded patches folder --- patches/1513678-hosting-task-names-1.9.patch | 13 --- ...0962-hosting-task-provision-save-1.9.patch | 98 ------------------- ...778400-hosting-tasks-names-hooks-1.9.patch | 20 ---- patches/README.txt | 3 - 4 files changed, 134 deletions(-) delete mode 100644 patches/1513678-hosting-task-names-1.9.patch delete mode 100644 patches/1760962-hosting-task-provision-save-1.9.patch delete mode 100644 patches/1778400-hosting-tasks-names-hooks-1.9.patch delete mode 100644 patches/README.txt diff --git a/patches/1513678-hosting-task-names-1.9.patch b/patches/1513678-hosting-task-names-1.9.patch deleted file mode 100644 index 587800543..000000000 --- a/patches/1513678-hosting-task-names-1.9.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/modules/hosting/task/hosting_task.module b/modules/hosting/task/hosting_task.module -index f698fe3..b037ab5 100644 ---- a/modules/hosting/task/hosting_task.module -+++ b/modules/hosting/task/hosting_task.module -@@ -388,7 +388,7 @@ function hosting_task_confirm_form($form_state, $node, $task) { - $form['nid'] = array('#type' => 'value', '#value' => $node->nid); - $form['task'] = array('#type' => 'value', '#value' => $task); - $form['parameters'] = array('#tree' => TRUE); -- $func = 'hosting_task_' . $task . '_form'; -+ $func = 'hosting_task_' . str_replace('-', '_', $task) . '_form'; - if (function_exists($func)) { - $form['parameters'] += $func($node); - } diff --git a/patches/1760962-hosting-task-provision-save-1.9.patch b/patches/1760962-hosting-task-provision-save-1.9.patch deleted file mode 100644 index 8e470a1ed..000000000 --- a/patches/1760962-hosting-task-provision-save-1.9.patch +++ /dev/null @@ -1,98 +0,0 @@ -diff --git a/modules/hosting/platform/hosting_platform.module b/modules/hosting/platform/hosting_platform.module -index 91d4319..a4cb8ed 100644 ---- a/modules/hosting/platform/hosting_platform.module -+++ b/modules/hosting/platform/hosting_platform.module -@@ -63,6 +63,7 @@ function hosting_platform_hosting_tasks() { - 'title' => t('Verify'), - 'description' => t('Verify that the platform is correctly installed and working.'), - 'weight' => 10, -+ 'provision_save' => TRUE, - ); - $tasks['platform']['delete'] = array( - 'title' => t('Delete'), -diff --git a/modules/hosting/server/hosting_server.module b/modules/hosting/server/hosting_server.module -index 5edddf7..b879054 100644 ---- a/modules/hosting/server/hosting_server.module -+++ b/modules/hosting/server/hosting_server.module -@@ -53,6 +53,7 @@ function hosting_server_hosting_tasks() { - 'title' => t('Verify'), - 'description' => t('Verify that the server is correctly installed and working.'), - 'weight' => 10, -+ 'provision_save' => TRUE, - ); - - /** -diff --git a/modules/hosting/site/hosting_site.module b/modules/hosting/site/hosting_site.module -index bd77390..c30b8af 100644 ---- a/modules/hosting/site/hosting_site.module -+++ b/modules/hosting/site/hosting_site.module -@@ -208,6 +208,7 @@ function hosting_site_hosting_tasks() { - $tasks['site']['verify'] = array( - 'title' => t('Verify'), - 'description' => t('Confirm that the site has been correctly installed and regenerate all configuration files to match the hosting front end.'), -+ 'provision_save' => TRUE, - ); - - $tasks['site']['disable'] = array( -@@ -243,14 +244,16 @@ function hosting_site_hosting_tasks() { - $tasks['site']['install'] = array( - 'title' => t('Install'), - 'description' => t('Install a site'), -- 'hidden' => TRUE -+ 'hidden' => TRUE, -+ 'provision_save' => TRUE, - ); - - $tasks['site']['import'] = array( - 'title' => t('Import'), - 'description' => t('Import an existing site into Aegir'), -- 'hidden' => TRUE -- ); -+ 'hidden' => TRUE, -+ 'provision_save' => TRUE, -+); - - return $tasks; - } -diff --git a/modules/hosting/task.hosting.inc b/modules/hosting/task.hosting.inc -index 31dd56f..0d0fa5c 100644 ---- a/modules/hosting/task.hosting.inc -+++ b/modules/hosting/task.hosting.inc -@@ -72,6 +72,14 @@ function drush_hosting_task_validate($id, $type = null) { - else { - drush_set_error('HOSTING_INVALID_TASK', t("This task is not valid")); - } -+ -+ // Load Task Info -+ $tasks_info = hosting_available_tasks($task->ref->type); -+ -+ // Find task type and pass through if it needs provision_save -+ if (isset($tasks_info[$task->task_type])){ -+ $task->task_info = $tasks_info[$task->task_type]; -+ } - } - - /** -@@ -90,9 +98,8 @@ function drush_hosting_task() { - - // Make sure argument order is correct - ksort($task->args); -- - // On install/verify, save the named context -- if ($task->task_type === 'install' || $task->task_type === 'verify' || $task->task_type === 'import') { -+ if (!empty($task->task_info['provision_save'])) { - // XXX: we copy module_invoke_all() here because it doesn't pass by - // reference and it breaks under PHP 5.3 - $hook = 'hosting_' . $task->ref->type . '_context_options'; -diff --git a/modules/hosting/task/hosting_task.module b/modules/hosting/task/hosting_task.module -index f698fe3..ca3c863 100644 ---- a/modules/hosting/task/hosting_task.module -+++ b/modules/hosting/task/hosting_task.module -@@ -1149,7 +1149,6 @@ function hosting_task_fetch_tasks($rid) { - - $return[$type] = $task; - } -- - return $return; - } - diff --git a/patches/1778400-hosting-tasks-names-hooks-1.9.patch b/patches/1778400-hosting-tasks-names-hooks-1.9.patch deleted file mode 100644 index 7f0a775ef..000000000 --- a/patches/1778400-hosting-tasks-names-hooks-1.9.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/modules/hosting/task.hosting.inc b/modules/hosting/task.hosting.inc -index 31dd56f..6c95989 100644 ---- a/modules/hosting/task.hosting.inc -+++ b/modules/hosting/task.hosting.inc -@@ -128,7 +128,7 @@ function drush_hosting_task() { - */ - function drush_hosting_hosting_task_rollback() { - $task =& drush_get_context('HOSTING_TASK'); -- module_invoke_all(sprintf("hosting_%s_task_rollback", $task->task_type), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); -+ module_invoke_all(sprintf("hosting_%s_task_rollback", str_replace('-', '_', $task->task_type)), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); - } - - /** -@@ -139,5 +139,5 @@ function drush_hosting_hosting_task_rollback() { - function drush_hosting_post_hosting_task($task) { - $task =& drush_get_context('HOSTING_TASK'); - -- module_invoke_all(sprintf("post_hosting_%s_task", $task->task_type), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); -+ module_invoke_all(sprintf("post_hosting_%s_task", str_replace('-', '_', $task->task_type)), $task, drush_get_context('HOSTING_DRUSH_OUTPUT')); - } diff --git a/patches/README.txt b/patches/README.txt deleted file mode 100644 index 76742c883..000000000 --- a/patches/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -These patches are only required for hostmaster-6.x-1.9. - -All of them have been committed to hostmaster-6.x-1.x and will be included in the next release of aegir. From 79273366290596fab61f78ee72473876df6126f0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 15 Feb 2014 13:18:04 -0500 Subject: [PATCH 0635/3476] removing README.txt since its in the devshop project --- INSTALL.txt | 59 ++++++++--------------------------------------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/INSTALL.txt b/INSTALL.txt index 49267e263..9a4022e4a 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,57 +1,14 @@ - DevShop Hosting =============== -Installation ------------- - -DevShop is built on top of Aegir, and consists of two projects: -devshop_provision (backend) and devshop_hosting (front-end). - -DevShop Provision also requires the provision_git drush module. - - -1. Install Aegir - - DevShop assumes a base Aegir installation. - See http://community.aegirproject.org/handbook to learn about Aegir. - - The most reliable and generally supported way to install aegir is with - the Debian packages. - - See http://community.aegirproject.org/installing/debian for instructions on - installing in debian based systems like Ubuntu. Pay close attention to - -2. Install provision_git and devshop provision +devshop_hosting is the front-end code for DevShop. This module requires the +devshop distribution to work. - $ drush dl devshop_provision provision_git +Documentation is located in the main project: http://drupal.org/project/devshop - In a typical debian environment, it will try to download it to - /usr/share/drush/commands, but you can put it anywhere Drush command files can live. - -3. Install devshop_hosting and enable desired modules. - - Download devshop_hosting and ctools into your @hostmaster site with drush: - $ drush @hostmaster dl devshop_hosting ctools - - Enable devshop_projects, devshop_tasks, devshop_log, and optionally, - devshop_tasks and devshop_tests: - - $ drush @hostmaster en devshop_projects devshop_tasks devshop_log devshop_tests - -NOTE: The project creation wizard requires certain Hosting Tasks to run before -later steps can be completed. For this reason, it really helps to have a very -fast Hosting Queue, or to install the Hosting Queue Runner project (http://drupal.org/project/hosting_queue_runner) so that tasks fire as quickly -as possible. - -4. Connect to your private Git repositories. - -DevShop allows you to clone your projects via the web based interface. In order -to do this, you must first make sure your DevShop/aegir server can connect to -your repository. - -Setup SSH keys so that your aegir user can git clone your repository. The first -time you clone an SSH repository, you have to type "Yes" for security purposes. +Installation +------------ +Don't install this module by itself. It needs the entire devshop system to +function. -Once you can connect and clone, you no longer need to use the command line to -create new platforms on your aegir server. \ No newline at end of file +For installation instructions, see http://drupal.org/project/devshop From 475b4b2a2ab0a125431877cc8d6760139e7dac07 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 17:19:48 -0500 Subject: [PATCH 0636/3476] Fixing stray code. --- devshop_projects/inc/create-wizard.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index cefc28ab2..8423d586d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -236,9 +236,8 @@ function devshop_project_create_step_git(&$form, &$form_state) { // Display some help for certain errors if ($project->verify_error == '[DEVSHOP] Error retrieving remote information: Host key verification failed. fatal: The remote end hung up unexpectedly'){ - $project->verify_error + $form['note']['#value'] .= '
' . t('You might have to authorize the host your are trying to connect to first. Add "StrictHostKeyChecking no" to your ~/.ssh/config file to avoid this for all domains.') . '
'; } - $form['note']['#value'] .= '
' . t('You might have to authorize the host your are trying to connect to first. Add "StrictHostKeyChecking no" to your ~/.ssh/config file to avoid this for all domains.') . '
'; } $form['git_url'] = array( From 12176015b3e61b9873fff4c7f33e648f4e02e0be Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 17:29:27 -0500 Subject: [PATCH 0637/3476] Cleaning up validation of project name. --- devshop_projects/inc/create-wizard.inc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 8423d586d..3de664b3d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -309,13 +309,16 @@ function devshop_project_create_step_git_validate(&$from, &$form_state) { $project = &$form_state['project']; // No spaces or special characters allowed. - $url = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive - if (!_hosting_valid_fqdn($url)) { + $project_name = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive + if (!_hosting_valid_fqdn($project_name)) { + form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); + } + if (!ctype_alnum($project_name)) { form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); } // Check for duplicate project name here. - $node = hosting_context_load($form_state['values']['title']); + $node = hosting_context_load($project_name); if ($node->nid != $project->project_nid){ form_set_error('title', t('That name is in use. Please try again.')); } @@ -470,7 +473,8 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $task = node_load($project->verify_task_nid); $time_ago = time() - $task->updated; if ($time_ago > 60) { - $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; + // @TODO: This doesn't work quite right yet. + // $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; } } From c3ef2f4e6b1737d69e47d990c9f63451e1fd5a75 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 17:40:52 -0500 Subject: [PATCH 0638/3476] Adding prefix and suffix to show what link the site will live at. --- devshop_projects/inc/create-wizard.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 3de664b3d..87d0b0c71 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -529,11 +529,13 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#type' => 'textfield', '#title' => t('Environment Name'), '#default_value' => $env_title, - '#size' => 25, + '#size' => 6, '#maxlength' => 255, '#attributes' => array( - 'placeholder' => t('Name this environment...'), + 'placeholder' => t('name'), ), + '#field_prefix' => 'http://', + '#field_suffix' => ".$project->base_url", ); // Add environment settings form elements From 1dcab3cef2033df8e6f28bd9932d38059784887a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 17:46:58 -0500 Subject: [PATCH 0639/3476] Adding note about the too fast loading bug. --- devshop_projects/inc/create-wizard.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 87d0b0c71..a829fbcab 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -827,10 +827,11 @@ function devshop_project_create_step_sites(&$form, &$form_state) { return $form; } // If no available profiles: + // @TODO: This fails sometimes because the list of install profiles is not quite available. elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; - $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; + $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch. NOTE: Try reloading the page. This is a known bug.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; $form['error'] = array( '#type' => 'markup', From 10c95264d78d030184f564c754de4640b4691d9f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 17:51:53 -0500 Subject: [PATCH 0640/3476] Adding todo note about http server. --- devshop_projects/inc/create-wizard.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index a829fbcab..553087e33 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -171,6 +171,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { // @TODO: Can we speed things up here by only running install for the first, // then "Cloning" to create the rest? foreach ($node->project_objects['platform'] as $nid => $env) { + // @TODO: Does this set the http_server as well?? Doesn't look like it. $db_server = $project->environments[$env]['db_server']; devshop_projects_create_site($node, node_load($nid), $env, $db_server); } From a7029b1c50ba4a56c19f6d120191638fea8902e9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 17:59:18 -0500 Subject: [PATCH 0641/3476] Adding note about creating more environments later. --- devshop_projects/inc/theme.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index e09568d61..46f28c4b9 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -59,7 +59,7 @@ function theme_devshop_projects_create_settings_form($form) { $rows[] = $row; } $output = theme('table', $header, $rows, array('id' => 'project-settings-table')); - $output .= '

'. t('Create as many new environments as you would like. For example: "dev", "test", and "live"') .'

'; + $output .= '

'. t('Create as many new environments as you would like. For example: "dev", "test", and "live". You can create more later on if needed.') .'

'; return $output; } From c058237e9d0a10b45c7a3fc86f306739638c5f35 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 18:01:38 -0500 Subject: [PATCH 0642/3476] Adding @TODO about the create project settings not saving. --- devshop_projects/inc/create-wizard.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 553087e33..f24f450f1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -628,6 +628,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project_node = node_load($project->project_nid); $values = $form_state['values']; + // @TODO: Settings from the form are not saving. $settings = module_invoke_all('devshop_project_settings', $project_node); // Create these platforms, if they don't exist yet From ed69c537cc937da147abece467ef741019f03592 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 18:15:21 -0500 Subject: [PATCH 0643/3476] remove border on environment name to reduce confusion. --- devshop_projects/devshop-style.css | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 8945bf4c6..7bade81ad 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -30,14 +30,9 @@ span.branch.environment-branch { span.environment, table * span.environment { color:#777777; - font-weight:bold; - padding:0px 6px; + font-weight:bold; + padding:0px 6px; margin: 0px 5px 5px 0px; - border: 1px solid #dfdfdf; - -moz-border-radius:3px; - -webkit-border-radius:3px; - display:inline-block; - background-color:#fefefe; font-size: 1.5em; } From 07f11976450de9d936c775aa80a7da076629acf8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 18:21:54 -0500 Subject: [PATCH 0644/3476] ADding class to environments table. --- devshop_projects/inc/ui.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 3a019a2c6..a28b6c9d8 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -170,7 +170,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($site->site_status != HOSTING_SITE_ENABLED && $platform->platform_status != HOSTING_PLATFORM_ENABLED) { continue; } - + $row = array(); $row[] = "$site->environment"; @@ -255,7 +255,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $rows[] = $row; } $header = array(); - $table = theme('table', $header, $rows); + $table = theme('table', $header, $rows, array('class' => 'environments-table')); $node->content['sites'] = array( '#type' => 'fieldset', From c8c7cd770aed0bde396bff1ea2cfb3f47db4cdba Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 18:32:25 -0500 Subject: [PATCH 0645/3476] Adding a ctools dropdown to select git branch or tag! --- devshop_projects/inc/ui.inc | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index a28b6c9d8..4cae6b0ef 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -179,8 +179,24 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } else { $row[] = devshop_hosting_site_goto_link($site); } - $row[] = "$site->git_branch"; - +// $row[] = "$site->git_branch"; + + // Create branch/tag chooser + $actions = array(); + foreach ($node->git_branches as $branch){ + $actions[] = array( + 'title' => $branch, + 'href' => "node/" . $platform_nid . "/deploy", + ); + } + foreach ($node->git_tags as $tag){ + $actions[] = array( + 'title' => "tag/" . $tag, + 'href' => "node/" . $platform_nid . "/deploy", + ); + } + $row[] = theme('ctools_dropdown', $site->git_branch, $actions); + if (module_exists('devshop_log')) { $row[] =l(t('Commits'), "node/$site->nid/logs/commits"); From 155549aa328ebc854c4c6630f2afaf8769ccfa31 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 18:37:20 -0500 Subject: [PATCH 0646/3476] Making tags an option. --- devshop_projects/devshop_projects.module | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 5ec323f71..74437c053 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -145,9 +145,13 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ $branch_options = array(); } + foreach ($project_node->git_tags as $tag){ + $branch_options[$tag] = "tag/" . $tag; + } + $settings = array(); $settings['git_branch'] = array( - '#title' => t('Git Branch'), + '#title' => t('Git Branch/Tag'), '#node_type' => 'platform', '#type' => 'select', '#options' => $branch_options, From b3eaf9e40279848937e5b8762e626216d8bae48a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 18:47:37 -0500 Subject: [PATCH 0647/3476] Git fetch before checkout. --- devshop_projects/devshop_projects.drush.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 848d3d114..ee798a6ec 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -91,6 +91,8 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $git_branch = $platform->git_branch; $create_branch = FALSE; + $output = ''; + //Remove drupal_path to clone. if ($platform->drupal_path) { $root = str_replace($platform->drupal_path, '', $root); @@ -126,12 +128,15 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $root = str_replace($platform->drupal_path, '', $root); } + // Run git fetch to ensure we have all branches and tags + $output .= _devshop_projects_git_execute('git fetch', $root); + // Build the command string $command = "git checkout $git_branch"; } // Execute - $output = _devshop_projects_git_execute($command, $root); + $output .= _devshop_projects_git_execute($command, $root); if ($create_branch) { //Create branch. From 395456c8b653264f3ee40a84e4fab1ca513b0e90 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 7 Mar 2014 19:03:11 -0500 Subject: [PATCH 0648/3476] fixing output of git log. --- devshop_log/devshop_log.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index e25347a54..949963dfb 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -146,5 +146,5 @@ function devshop_log_page($nid) { } // return the output - return $output; + return check_markup($output); } From 205977a2cb056fab477078b8b85bf8c8c330e77a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 8 Mar 2014 12:33:57 -0500 Subject: [PATCH 0649/3476] Fixing bad URL --- devshop_pull/devshop_pull.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 70c8cfcf6..5a08e67cd 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -121,7 +121,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#type' => 'radios', '#description' => t('Choose the method of regularly calling "Pull Code". See !link to configure the queue. See !link2 to configure URL Callback.', array( '!link' => l(t('Hosting > Queues'), 'admin/hosting/queues'), - '!link2' => l(t('Hosting > DevShop Pull Settings'), 'admin/hosting/devshop_pull') + '!link2' => l(t('Hosting > DevShop Pull Settings'), 'admin/hosting/devshop/pull') )), '#default_value' => $node->pull_method, '#options' => array( From 37d3c3a4b061ae93e6db2c13175bcb6ceeacb4fe Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 18:32:31 +0000 Subject: [PATCH 0650/3476] Logging payload of commit notification --- devshop_pull/devshop_pull.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 7e5b29b13..3a22cfd90 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -75,6 +75,7 @@ function devshop_pull_callback($project, $hash) { // Output a message, no matter what. watchdog('devshop_pull', $message, array(), WATCHDOG_INFO); + watchdog('devshop_pull_debug', 'PAYLOAD: '. $GLOBALS['HTTP_RAW_POST_DATA'], array(), WATCHDOG_INFO); print $message; // Save a variable to help when using the settings page. From 7492b2024c2ac229f6fb279204dc684889ae22d6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 19:08:56 +0000 Subject: [PATCH 0651/3476] detecting pull requests! --- devshop_pull/devshop_pull.inc | 106 +++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 3a22cfd90..41dfbd55d 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -43,23 +43,20 @@ function devshop_pull_callback($project, $hash) { } // All checks pass! Server is allowed to trigger tasks! else { - $message = "Commit Received! Invoked by " . ip_address(); $status = DEVSHOP_PULL_STATUS_OK; - // @TODO: Check Payload Here... If we see a GitHub payload with branches, - // filter out the $environments_to_pull based on those branches. + // @TODO: Make this pluggable. - // Create the hosting task - // We need to pass environments so we can control what envs to pull based - // on the data coming back from github! - $args = array(); - $args['environments'] = implode(' ', $environments_to_pull); + // Check headers for GitHub Integration + $headers = getallheaders(); + if (isset($headers['X-GitHub-Event'])) { + $message = devshop_pull_github_webhook($project_node, $environments_to_pull); + } + else { + $message = devshop_pull_default_webhook($project_node, $environments_to_pull); + } - // Always clear cache... - // @TODO: Should we make this a setting? "What to do on auto-pull?" - $args['cache'] = 1; - hosting_add_task($project_node->nid, 'devshop-pull', $args); - } + } // Log It, only if there is a status if (isset($status)){ @@ -75,13 +72,95 @@ function devshop_pull_callback($project, $hash) { // Output a message, no matter what. watchdog('devshop_pull', $message, array(), WATCHDOG_INFO); + + // @TODO: Create a "debug" mode to prevent logging of all payloads watchdog('devshop_pull_debug', 'PAYLOAD: '. $GLOBALS['HTTP_RAW_POST_DATA'], array(), WATCHDOG_INFO); + watchdog('devshop_pull_debug', 'HEADERS: '. print_r(getallheaders(), 1), WATCHDOG_INFO); + + print $message; // Save a variable to help when using the settings page. variable_set('devshop_pull_last_ip', ip_address()); } +/** + * Default action to take on webhook init. + */ +function devshop_pull_default_webhook($project, $environments_to_pull){ + // Create the hosting task + // We need to pass environments so we can control what envs to pull based + // on the data coming back from github! + $args = array(); + $args['environments'] = implode(' ', $environments_to_pull); + + // Always clear cache... + // @TODO: Should we make this a setting? "What to do on auto-pull?" + $args['cache'] = 1; + hosting_add_task($project_node->nid, 'devshop-pull', $args); + + return "Commit notification received! Running 'Pull Code' on $project->title environments " . $args['environments']; +} + +/** + * GitHub action to take on webhook init + */ +function devshop_pull_github_webhook($project){ + $headers = getallheaders(); + + if ($headers['content-type'] == 'application/json'){ + $data = json_decode($GLOBALS['HTTP_RAW_POST_DATA']); + + $args = array(); + $args['environments'] = implode(' ', $environments_to_pull); + $args['cache'] = 1; + + + switch ($headers['X-GitHub-Event']){ + case 'ping': + $message = 'Pong!'; + break; + case 'push': + // Add a task to pull then project's environments + hosting_add_task($project->nid, 'devshop-pull', $args); + $message = 'Push Received.'; + break; + case 'pull_request': + $message = 'Pull Request Received.'; + + // @TODO: Add to project settings. + $create_environments_from_pr = TRUE; + $branch = $data->pull_request->head->ref; + + $message = "Detected Pull Request for $branch"; + if ($create_environments_from_pr == TRUE){ + // Lookup environments by branch + foreach ($project->settings as $env => $settings){ + if ($settings['git_branch'] == $branch){ + $pull_environment_found = $branch; + } + } + if (empty($pull_environment_found)){ + // @TODO: Create one! + print "CREATE $branch!"; + } + else { + print "Branch already has an environment."; + } + } + + + break; + } + + return $message; + + } + else { + watchdog('devshop_pull', 'GitHub Request Received, but not in JSON. Please make sure to configure the webhook to use Payload version: application/vnd.github.v3+json'); + } +} + /** * Check whether a given ip address matches a list of allowed ip addresses, some of which * may be CIDR. @@ -149,3 +228,4 @@ function devshop_pull_project($project_nid) { print "No environments configured to pull! Aborting."; } } + From c9a097440c22fbbf57a929b86695bacbf9cad3c3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 16:01:11 -0400 Subject: [PATCH 0652/3476] Creating a simple api function for creating a new environment. --- devshop_projects/inc/tasks.inc | 54 ++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index b3f08e1de..d56d18252 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -105,9 +105,9 @@ function hosting_task_devshop_create_form($node) { '#type' => 'hidden', '#value' => $site->git_branch, ); - $form['clone_nid'] = array( + $form['fork_source'] = array( '#type' => 'hidden', - '#value' => $site->nid, + '#value' => $site->environment, ); $form['new_branch'] = array( '#title' => t('New branch name'), @@ -174,6 +174,35 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $project = node_load($form_state['values']['nid']); $environment_name = $form_state['values']['parameters']['environment_name']; $branch = $form_state['values']['parameters']['branch']; + $fork_source = $form_state['values']['parameters']['fork_source']; + hosting_create_environment($project, $environment_name, $branch, $fork_source); + + // We are replacing hosting_confirm_form_submit here, so just do what it does, + // minus the hosting task creation! + $values = $form_state['values']; + $form_state['redirect'] = 'node/' . $values['nid']; + modalframe_close_dialog(); + + // Friendly message + drupal_set_message(t('Your environment is being created.')); +} + + +/** + * API-level function for creating a new environment. + * + * @TODO: Add more options like web server, etc. + * @param $project + * A full project node (for now. @TODO Allow project name (and/or nid) as parameters. + * @param $environment_name + * A new name for the environment + * @param $branch + * What git branch to track. + * @param $fork_source + * If desired, the environment to fork off of. (Copy the database and create a new branch from) + */ +function hosting_create_environment($project, $environment_name, $branch, $fork_source = NULL) { + $servers = hosting_get_servers('http'); $server = variable_get('devshop_projects_default_web_server', key($servers)); @@ -191,26 +220,19 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $platform->publish_path = $project->code_path . '/' . $environment_name . '/' . $project->drupal_path; } - //Check if is need to clone and create a new branch - if (isset($form_state['values']['parameters']['clone_nid'])) { - $platform->clone_nid = $form_state['values']['parameters']['clone_nid']; - $platform->git_branch = $form_state['values']['parameters']['new_branch']; - $platform->old_branch = $branch; + //Check if we are forking another site. + if (isset($project->environments[$fork_source])) { + $platform->clone_nid = $project->environments[$environment_to_fork]['site']; + $platform->git_branch = $branch; + $platform->old_branch = $project->settings[$environment_to_fork]['git_branch']; } watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); - // Create the platform node + // Create the platform node. The platform and site creation handle it from there. $platform_node = _devshop_projects_node_create('platform', $platform); - // We are replacing hosting_confirm_form_submit here, so just do what it does, - // minus the hosting task creation! - $values = $form_state['values']; - $form_state['redirect'] = 'node/' . $values['nid']; - modalframe_close_dialog(); - - // Friendly message - drupal_set_message(t('Your environment is being created.')); + return $platform_node; } From e71367291407dedc0c2230cfc0d5cfe67f88237c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 16:10:02 -0400 Subject: [PATCH 0653/3476] Allowing dashes and underscores in branch names --- devshop_projects/inc/tasks.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index d56d18252..17f5e5d4f 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -159,8 +159,8 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); } - if (isset($params['new_branch']) && !preg_match('!^[a-z0-9_]+$!', $params['environment_name'])) { - form_set_error('new_branch', t('The new branch name must contain only lowercase letters, numbers, and underscores.')); + if (isset($params['new_branch']) && !preg_match('!^[a-z0-9_-]+$!', $params['new_branch'])) { + form_set_error('new_branch', t('The new branch name must contain only lowercase letters, numbers, dashes and underscores.')); } } From 38349801035eb9dad4fd8966a8ddd0ec86b94440 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 18:00:55 -0400 Subject: [PATCH 0654/3476] Fixing saving the new or existing git branch. --- devshop_projects/inc/tasks.inc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 17f5e5d4f..57fcf0225 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -101,10 +101,6 @@ function hosting_task_devshop_create_form($node) { '#title' => t('Fork from'), '#value' => t('!env environment on branch !branch', array('!env' => "$site->environment", '!branch' => "$site->git_branch")), ); - $form['branch'] = array( - '#type' => 'hidden', - '#value' => $site->git_branch, - ); $form['fork_source'] = array( '#type' => 'hidden', '#value' => $site->environment, @@ -173,8 +169,9 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $project = node_load($form_state['values']['nid']); $environment_name = $form_state['values']['parameters']['environment_name']; - $branch = $form_state['values']['parameters']['branch']; + $branch = $form_state['values']['parameters']['new_branch']? $form_state['values']['parameters']['new_branch']: $form_state['values']['parameters']['branch']; $fork_source = $form_state['values']['parameters']['fork_source']; + hosting_create_environment($project, $environment_name, $branch, $fork_source); // We are replacing hosting_confirm_form_submit here, so just do what it does, From 3b12e3f662b95eb21a2bee24ea756bd4291622e7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 18:08:10 -0400 Subject: [PATCH 0655/3476] fix fork source variable --- devshop_projects/inc/tasks.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 57fcf0225..77988fad6 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -219,9 +219,9 @@ function hosting_create_environment($project, $environment_name, $branch, $fork_ //Check if we are forking another site. if (isset($project->environments[$fork_source])) { - $platform->clone_nid = $project->environments[$environment_to_fork]['site']; + $platform->clone_nid = $project->environments[$fork_source]['site']; $platform->git_branch = $branch; - $platform->old_branch = $project->settings[$environment_to_fork]['git_branch']; + $platform->old_branch = $project->settings[$fork_source]['git_branch']; } watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); From a61d55dd7aadc14d468c4146cea7a0ff51a23036 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 18:50:07 -0400 Subject: [PATCH 0656/3476] Fix unneeded foreach. --- devshop_projects/inc/tasks.inc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 77988fad6..7e958d283 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -86,12 +86,8 @@ function hosting_task_devshop_create_form($node) { $site_nid = check_plain($site_nid); //Check is a valid project site. - if (!empty($node->project_objects['site'])) { - foreach($node->project_objects['site'] as $nid => $alias) { - if ($nid == $site_nid) { - $site = node_load($nid); - } - } + if (!empty($node->project_objects['site'][$site_nid])) { + $site = node_load($site_nid); } } From b1cc39016e73cd2fa830d6832abe25bc666d5b45 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 18:54:05 -0400 Subject: [PATCH 0657/3476] hosting_create_environment --- devshop_pull/devshop_pull.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 41dfbd55d..ac4203ede 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -112,7 +112,7 @@ function devshop_pull_github_webhook($project){ $data = json_decode($GLOBALS['HTTP_RAW_POST_DATA']); $args = array(); - $args['environments'] = implode(' ', $environments_to_pull); +// $args['environments'] = implode(' ', $environments_to_pull); $args['cache'] = 1; @@ -141,8 +141,7 @@ function devshop_pull_github_webhook($project){ } } if (empty($pull_environment_found)){ - // @TODO: Create one! - print "CREATE $branch!"; + hosting_create_environment($project, str_replace('-', '_', $branch), $branch); } else { print "Branch already has an environment."; From eb0047fc3aba401d36b2c4e5c049c66a08b685a2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 22:57:35 -0400 Subject: [PATCH 0658/3476] fixing legth and width of environment name. --- devshop_projects/devshop-style.css | 3 +++ devshop_projects/inc/create-wizard.inc | 2 +- devshop_projects/inc/tasks.inc | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 7bade81ad..7b69fc163 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -116,3 +116,6 @@ p.error { #edit-add-environment { background: #999; } +#edit-parameters-environment-name-wrapper input.form-text { + width: inherit; +} \ No newline at end of file diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f24f450f1..0526bd649 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -531,7 +531,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#title' => t('Environment Name'), '#default_value' => $env_title, '#size' => 6, - '#maxlength' => 255, + '#maxlength' => 64, '#attributes' => array( 'placeholder' => t('name'), ), diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 7e958d283..0dcea7845 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -125,6 +125,10 @@ function hosting_task_devshop_create_form($node) { '#type' => 'textfield', '#description' => t('Enter a system name for your environment. For consistency, you should make this match the branch name.'), '#required' => TRUE, + '#field_prefix' => 'http://', + '#field_suffix' => ".$node->base_url", + '#size' => 6, + '#maxlength' => 64, ); $form['project_nid'] = array( '#type' => 'value', From d819da1d836bb4dad017ba57c023de222f895fe9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 13 Mar 2014 23:07:38 -0400 Subject: [PATCH 0659/3476] Add live site alias to the project settings form. --- devshop_projects/inc/forms.inc | 12 ++++++++++++ devshop_projects/inc/nodes.inc | 3 +++ 2 files changed, 15 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 1450a586b..25d8d3737 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -186,6 +186,18 @@ function devshop_projects_form(&$node) { $form[$field]['#type'] = 'value'; } } + + // Master Site alias + $form['live_alias'] = array( + '#type' => 'textfield', + '#title' => t('Live Site Alias'), + '#description' => t('The drush alias for the live production site of this project. Can be hosted here or setup as a remote alias.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->live_alias, + '#maxlength' => 255, + '#weight' => 4, + ); return $form; } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 07f15df9b..3cd0ada11 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -132,6 +132,7 @@ function devshop_projects_insert($node) { $data = array(); $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; + $data['live_alias'] = $node->live_alias; $info = new stdClass(); $info->nid = $node->nid; @@ -168,6 +169,7 @@ function devshop_projects_update($node) { // Branches and tags get saved upon verify. $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; + $data['live_alias'] = $node->live_alias; $info = new stdClass(); $info->nid = $node->nid; @@ -211,6 +213,7 @@ function devshop_projects_load($node) { $data = unserialize($additions['data']); $additions['git_branches'] = $data['git_branches']; $additions['git_tags'] = $data['git_tags']; + $additions['live_alias'] = $data['live_alias']; $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); From c7571c57b961e5bd9d5c25fb828a8e55aaa0943f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 10:06:35 -0400 Subject: [PATCH 0660/3476] Removing live_domain from project schema. Just use $data --- devshop_live/devshop_live.module | 1 + devshop_projects/devshop_projects.install | 14 +++++++++++++- devshop_projects/inc/nodes.inc | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module index 0ca941599..1ec847b4e 100644 --- a/devshop_live/devshop_live.module +++ b/devshop_live/devshop_live.module @@ -97,6 +97,7 @@ function devshop_live_form_alter(&$form, &$form_state, $form_id) { ); } + // Add live domain field to create project form. if ($form_id == 'devshop_project_create_step_settings') { $project = $form_state['project']; diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 1e55efe99..e9a9fbc78 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -207,7 +207,7 @@ function devshop_projects_update_8() { return $ret; } -/* +/** * Add clone_nid column to hosting_devshop_project_object. */ function devshop_projects_update_9() { @@ -216,3 +216,15 @@ function devshop_projects_update_9() { return $ret; } + + +/** + * Remove live_domain from hosting_devsho_project schema. + */ +function devshop_projects_update_10() { + $ret = array(); + if (db_column_exists('hosting_devshop_project', 'live_domain')){ + $ret[] = db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); + } + return $ret; +} \ No newline at end of file diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3cd0ada11..deae30d96 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -133,6 +133,7 @@ function devshop_projects_insert($node) { $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; $data['live_alias'] = $node->live_alias; + $data['live_domain'] = $node->live_domain; $info = new stdClass(); $info->nid = $node->nid; @@ -141,7 +142,6 @@ function devshop_projects_insert($node) { $info->drupal_path = hosting_path_normalize($node->drupal_path); $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; - $info->live_domain = $node->live_domain; $info->data = serialize($data); drupal_write_record('hosting_devshop_project', $info); @@ -170,6 +170,7 @@ function devshop_projects_update($node) { $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; $data['live_alias'] = $node->live_alias; + $data['live_domain'] = $node->live_domain; $info = new stdClass(); $info->nid = $node->nid; @@ -178,7 +179,6 @@ function devshop_projects_update($node) { $info->drupal_path = hosting_path_normalize($node->drupal_path); $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; - $info->live_domain = $node->live_domain; $info->data = serialize($data); drupal_write_record('hosting_devshop_project', $info, 'nid'); From 381395ed5e636aba30ece491d9794d5221b47ded Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 10:14:25 -0400 Subject: [PATCH 0661/3476] Link to all sites from the projects homepage. --- devshop_projects/inc/ui.inc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index a28b6c9d8..4cd2cb7d6 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -18,7 +18,7 @@ function devshop_projects_projects_page() { 'Version', '', 'Git URL', - 'Dev Site', + 'Environments', ); $r = db_query("SELECT hdp.* FROM {hosting_devshop_project} hdp LEFT JOIN {node} n ON hdp.nid = n.nid WHERE n.status = 1 ORDER BY n.title"); @@ -31,7 +31,6 @@ function devshop_projects_projects_page() { } $row = array(); - $link_options = array('attributes' => array('target' => '_blank')); // Link to Project page $row[] = '' . l($node->title, "node/$proj->nid") . ''; @@ -46,15 +45,20 @@ function devshop_projects_projects_page() { $num = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} hdpo LEFT JOIN {hosting_site} hs ON hdpo.object_nid = hs.nid WHERE hdpo.project_nid = %d AND hdpo.object_type='site' AND hs.status=1", $node->nid)); $row[] = format_plural($num, t('1 site'), t('!num sites', array('!num' => $num))); - - // Git URL $row[] = strtr("", array('!url' => $node->git_url)); - // Link to Dev Site - $dev_site_url = url("http://dev." . $node->base_url, array('absolute' => TRUE)); - $row[] = l($dev_site_url, $dev_site_url, $link_options); - + // Links to all sites + $actions = array(); + foreach ($node->environments as $env => $details){ + $dev_site_url = url("http://$env.$node->base_url", array('absolute' => TRUE)); + $actions[] = array( + 'title' => $dev_site_url, + 'href' => $dev_site_url, + 'attributes' => array('target' => '_blank'), + ); + } + $row[] = theme('ctools_dropdown', t('Visit...'), $actions); $rows[$proj->nid] = $row; } From 8ff7326f39186e94e3ad90ebadb14017ddee8f3e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:07:06 -0400 Subject: [PATCH 0662/3476] Fixing up live site domain and aliases. --- devshop_projects/devshop_projects.install | 3 +- devshop_projects/devshop_projects.module | 8 +++ devshop_projects/inc/forms.inc | 61 ++++++++++++++++------- devshop_projects/inc/nodes.inc | 48 +++++++++++++++++- 4 files changed, 99 insertions(+), 21 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index e9a9fbc78..6ad028dcd 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -219,9 +219,10 @@ function devshop_projects_update_9() { /** - * Remove live_domain from hosting_devsho_project schema. + * Remove live_domain from hosting_devsho_project schema and disable devshop_live if needed. */ function devshop_projects_update_10() { + modules_disable(array('devshop_live')); $ret = array(); if (db_column_exists('hosting_devshop_project', 'live_domain')){ $ret[] = db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 5ec323f71..dd6d459dd 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -146,6 +146,14 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ } $settings = array(); + $settings['site_nid'] = array( + '#node_type' => 'site', + '#type' => 'hidden', + ); + $settings['platform_nid'] = array( + '#node_type' => 'platform', + '#type' => 'hidden', + ); $settings['git_branch'] = array( '#title' => t('Git Branch'), '#node_type' => 'platform', diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 25d8d3737..7b258c126 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -82,6 +82,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } } + /** * Implementation of hook_form(). */ @@ -100,12 +101,12 @@ function devshop_projects_form(&$node) { ); $form['title'] = array( '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project.'), - '#size' => 40, - '#default_value' => $node->title, - '#maxlength' => 255, + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project.'), + '#size' => 40, + '#default_value' => $node->title, + '#maxlength' => 255, ); $form['code_path'] = array( '#type' => 'textfield', @@ -136,7 +137,7 @@ function devshop_projects_form(&$node) { '#maxlength' => 255, '#weight' => 4, ); - + // Unchanging Values $form['git_branches'] = array( '#type' => 'value', @@ -146,7 +147,7 @@ function devshop_projects_form(&$node) { '#type' => 'value', '#value' => $node->git_tags, ); - + // Extensible settings $form['settings'] = array( '#type' => 'fieldset', @@ -154,7 +155,7 @@ function devshop_projects_form(&$node) { '#theme' => 'devshop_projects_settings_form', '#tree' => TRUE, ); - + $settings = module_invoke_all('devshop_project_settings', $node); foreach ($node->project_objects['platform'] as $nid => $environment_name) { $platform = node_load($nid); @@ -187,25 +188,47 @@ function devshop_projects_form(&$node) { } } - // Master Site alias - $form['live_alias'] = array( + // @TODO: Give the user a select list of all environments, and allow , where they can input an alias manually. +// // Master Site alias +// $form['live_alias'] = array( +// '#type' => 'textfield', +// '#title' => t('Live Site Alias'), +// '#description' => t('The drush alias for the live production site of this project. Can be hosted here or setup as a remote alias.'), +// '#required' => TRUE, +// '#size' => 40, +// '#default_value' => $node->live_alias, +// '#maxlength' => 255, +// '#weight' => 4, +// ); + + // Live Domain + $form['live_domain'] = array( '#type' => 'textfield', - '#title' => t('Live Site Alias'), - '#description' => t('The drush alias for the live production site of this project. Can be hosted here or setup as a remote alias.'), - '#required' => TRUE, + '#title' => t('Live domain'), + '#description' => t('The live domain for this project. Do not include "www".'), '#size' => 40, - '#default_value' => $node->live_alias, - '#maxlength' => 255, - '#weight' => 4, + '#default_value' => $node->live_domain, + '#weight' => 5, ); + + // Use aliases + if (module_exists('hosting_alias')){ + $form['live_domain_aliases'] = array( + '#type' => 'checkbox', + '#title' => t('Use environment aliases'), + '#description' => t('Create aliases for all environments as subdomains of the live domain.'), + '#default_value' => $node->live_domain_aliases, + '#weight' => 6, + ); + } + return $form; } - /** * Submit function for save data of platforms. */ function devshop_projects_submit_settings($form, &$form_state) { - + // Go through and save our settings $project_node = node_load($form_state['values']['nid']); diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index deae30d96..89a6ec343 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -55,6 +55,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // On Load: load this object's project and environment type if ($op == 'load') { $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); + $data[$node->type . "_nid"] = $node->nid; return $data; } @@ -134,6 +135,7 @@ function devshop_projects_insert($node) { $data['git_tags'] = $node->git_tags; $data['live_alias'] = $node->live_alias; $data['live_domain'] = $node->live_domain; + $data['live_domain_aliases'] = $node->live_domain_aliases; $info = new stdClass(); $info->nid = $node->nid; @@ -150,6 +152,9 @@ function devshop_projects_insert($node) { if ((!$node->old_vid)) { hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); } + + // If using environment aliases... save them. + devshop_project_save_domain_aliases($node); } /** @@ -171,6 +176,7 @@ function devshop_projects_update($node) { $data['git_tags'] = $node->git_tags; $data['live_alias'] = $node->live_alias; $data['live_domain'] = $node->live_domain; + $data['live_domain_aliases'] = $node->live_domain_aliases; $info = new stdClass(); $info->nid = $node->nid; @@ -182,6 +188,42 @@ function devshop_projects_update($node) { $info->data = serialize($data); drupal_write_record('hosting_devshop_project', $info, 'nid'); + + // If using environment aliases... save them. + devshop_project_save_domain_aliases($node); + +} + + +/** + * Helper to add a domain alias to a site node. Makes sure not to add the same + * alias twice. + */ +function devshop_project_save_domain_aliases($project_node) { + if (module_exists('hosting_alias')){ + if (!empty($project_node->live_domain) && $project_node->live_domain_aliases) { + foreach ($project_node->settings as $env => $details){ + $domain = "$env.$project_node->live_domain"; + $site_node = node_load($details['site_nid']); + if (array_search($domain, $site_node->aliases) === FALSE) { + $site_node->aliases[] = $domain; + node_save($site_node); + } + } + } + // If live_domain_aliases is checked, remove them from the nodes. + elseif (!$project_node->live_domain_aliases){ + foreach ($project_node->settings as $env => $details){ + $domain = "$env.$project_node->live_domain"; + $site_node = node_load($details['site_nid']); + $i = array_search($domain, $site_node->aliases); + if ($i !== FALSE) { + unset($site_node->aliases[$i]); + node_save($site_node); + } + } + } + } } /** @@ -213,7 +255,11 @@ function devshop_projects_load($node) { $data = unserialize($additions['data']); $additions['git_branches'] = $data['git_branches']; $additions['git_tags'] = $data['git_tags']; - $additions['live_alias'] = $data['live_alias']; + $additions['live_domain'] = $data['live_domain']; + $additions['live_domain_aliases'] = $data['live_domain_aliases']; + + $additions['project'] = $data; + $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); From 371d4ffa21ba8bafddc6c2c05af9d6c2fb1e661a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:10:39 -0400 Subject: [PATCH 0663/3476] Display aliased domains on project node page. --- devshop_projects/inc/ui.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 4cd2cb7d6..9347ad9a0 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -357,12 +357,14 @@ function devshop_projects_nodeapi_platform_view(&$node, $a3, $a4) { * Our own version of _hosting_site_goto_link() */ function devshop_hosting_site_goto_link($node) { + $project = node_load($node->project_nid); + $url = $project->live_domain_aliases? "{$node->environment}.{$project->live_domain}": $node->title; $cache = cache_get("hosting:site:" . $node->nid . ":login_link"); if (!is_null($cache) && (time() < $cache->data['expire'])) { - $title = t("Log in: !url", array('!url' => $node->title)); + $title = t("Log in: !url", array('!url' => $url)); } else { - $title = t("!url", array('!url' => $node->title)); + $title = t("!url", array('!url' => $url)); } $options['attributes']['target'] = '_blank'; $options['attributes']['class'] = 'hosting-goto-site-link'; From 25e78448dc7c82a9665a9c38efb120381a5346fd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:14:13 -0400 Subject: [PATCH 0664/3476] Using live domain aliases for links. --- devshop_projects/inc/nodes.inc | 8 +++++--- devshop_projects/inc/ui.inc | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 89a6ec343..2f78b930a 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -244,9 +244,7 @@ function devshop_projects_delete($node) { * Node object */ function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT * ' . - 'FROM {hosting_devshop_project} ' . - 'WHERE nid = %d', $node->nid)); + $additions = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); if (is_array($hosting_name) && is_array($additions)) { $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; @@ -260,6 +258,10 @@ function devshop_projects_load($node) { $additions['project'] = $data; + // If live domain is set, override base url. + if (!empty($additions['live_domain'])){ + $additions['base_url'] = $additions['live_domain']; + } $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 9347ad9a0..23d1de752 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -358,7 +358,7 @@ function devshop_projects_nodeapi_platform_view(&$node, $a3, $a4) { */ function devshop_hosting_site_goto_link($node) { $project = node_load($node->project_nid); - $url = $project->live_domain_aliases? "{$node->environment}.{$project->live_domain}": $node->title; + $url = "{$node->environment}.{$project->base_url}"; $cache = cache_get("hosting:site:" . $node->nid . ":login_link"); if (!is_null($cache) && (time() < $cache->data['expire'])) { $title = t("Log in: !url", array('!url' => $url)); From 3b293a4debcc11e816b30129522dec3cb0c2bee6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:16:38 -0400 Subject: [PATCH 0665/3476] removing devshop live! it is integrated into devshop_projects now. --- devshop_live/README.txt | 5 -- devshop_live/devshop_live.info | 6 -- devshop_live/devshop_live.install | 36 --------- devshop_live/devshop_live.module | 124 ------------------------------ 4 files changed, 171 deletions(-) delete mode 100644 devshop_live/README.txt delete mode 100644 devshop_live/devshop_live.info delete mode 100644 devshop_live/devshop_live.install delete mode 100644 devshop_live/devshop_live.module diff --git a/devshop_live/README.txt b/devshop_live/README.txt deleted file mode 100644 index 9eac83331..000000000 --- a/devshop_live/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -DevShop Live -=========== - -Tracks a live domain for each project, and automatically creates aliases to that domain for -each environment in the project. diff --git a/devshop_live/devshop_live.info b/devshop_live/devshop_live.info deleted file mode 100644 index 2c89b516e..000000000 --- a/devshop_live/devshop_live.info +++ /dev/null @@ -1,6 +0,0 @@ -name = DevShop Live -description = Add a live domain to projects. -core = 6.x -package = DevShop -dependencies[] = devshop_projects -dependencies[] = hosting_alias diff --git a/devshop_live/devshop_live.install b/devshop_live/devshop_live.install deleted file mode 100644 index 65825104e..000000000 --- a/devshop_live/devshop_live.install +++ /dev/null @@ -1,36 +0,0 @@ - 'varchar', - 'length' => 128, - 'not null' => TRUE, - ); -} - -/** - * Implements hook_enable(). - */ -function devshop_live_enable() { - $ret = array(); - db_add_field($ret, 'hosting_devshop_project', 'live_domain', array('type' => 'varchar', 'length' => 128, 'not null' => TRUE)); - return $ret; -} - -/** - * Implementation of hook_uninstall(). - */ -function devshop_live_uninstall() { - $ret = array(); - db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); - return $ret; -} diff --git a/devshop_live/devshop_live.module b/devshop_live/devshop_live.module deleted file mode 100644 index 1ec847b4e..000000000 --- a/devshop_live/devshop_live.module +++ /dev/null @@ -1,124 +0,0 @@ -type == 'site') { - if (array_search($domain, $node->aliases) === FALSE) { - $node->aliases[] = $domain; - node_save($node); - return TRUE; - } - } - return FALSE; -} - -/** - * Implements hook_devshop_projects_page(). - * - * Adds a link to the live domain. - */ -function devshop_live_devshop_projects_page($rows, $header) { - $new_rows = array(); - $link_options = array('attributes' => array('target' => '_blank')); - - //add new header live - $header[] = t('Live site'); - - if (!empty($rows)) { - foreach ($rows as $nid => $row) { - $live_domain = db_result(db_query("SELECT live_domain FROM {hosting_devshop_project} WHERE nid=%d", $nid)); - - if ($live_domain) { - $live_domain = 'http://'. $live_domain; - $live_domain = l($live_domain, $live_domain, $link_options); - } - $new_rows[] = array_merge($row, array($live_domain)); - } - } - - return array('rows' => $new_rows, 'header' => $header); -} - -/** - * Implements hook_nodeapi(). - */ -function devshop_live_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { - if ($node->type == 'project') { - if (in_array($op, array('insert', 'update'))) { - //Load all information - $node_p = node_load($node->nid); - - if ($node->live_domain && !isset($node_p->project_objects['site'])) { - $live_nid = array_search('live', $node_p->project_objects['site']); - - if ($live_nid) { - $live_node = node_load($live_nid); - devshop_live_add_domain($live_node, $node->live_domain); - } - } - } - if ($op == 'view' && !empty($node->live_domain)) { - $url = 'http://' . $node->live_domain; - - $node->content['info']['live_domain'] = array( - '#type' => 'item', - '#title' => t('Live Site'), - '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), - '#weight' => -9, - ); - } - } -} - -/** - * Implements hook_form_alter(). - */ -function devshop_live_form_alter(&$form, &$form_state, $form_id) { - //Add field live_domain to edit form - if ($form_id == 'project_node_form') { - $node = $form['#node']; - - $form['live_domain'] = array( - '#type' => 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain for this project.'), - '#size' => 40, - '#default_value' => $node->live_domain, - '#maxlenght' => 255, - '#weight' => 3, - ); - } - - // Add live domain field to create project form. - if ($form_id == 'devshop_project_create_step_settings') { - $project = $form_state['project']; - - $form['live_domain'] = array( - '#type' => 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain for this project.'), - '#size' => 40, - '#default_value' => $project->live_domain, - '#maxlenght' => 255, - ); - - $form['#submit'][] = 'devshop_live_create_form_submit'; - } -} - -/** - * Extra submit handler for Project node form. - */ -function devshop_live_create_form_submit($form, &$form_state) { - $project = &$form_state['project']; - - $project->live_domain = $form_state['values']['live_domain']; -} From 78588ba458dc0fe67c41488e6830ddf80a39c511 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:17:01 -0400 Subject: [PATCH 0666/3476] Adding live domain to project display. --- devshop_projects/inc/ui.inc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 23d1de752..40cb17096 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -117,14 +117,17 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // '#value' => filter_xss($node->code_path), // '#weight' => -8 //); - // - //$url = 'http://dev.' . $node->base_url; - //$node->content['info']['base_url'] = array( - // '#type' => 'item', - // '#title' => t('Dev Site'), - // '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), - // '#weight' => -10 - //); + + if (!empty($node->live_domain)) { + $url = 'http://' . $node->live_domain; + + $node->content['info']['live_domain'] = array( + '#type' => 'item', + '#title' => t('Live Site'), + '#value' => l($url, $url, array('attributes' => array('target' => '_blank'))), + '#weight' => -9, + ); + } $node->content['info']['git_url'] = array( '#type' => 'item', From 95f46183d69daabb9b2bcef0bb86c7db98f56a69 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:29:18 -0400 Subject: [PATCH 0667/3476] Save domain alias on site insert or update. --- devshop_projects/inc/nodes.inc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2f78b930a..990876475 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -103,6 +103,14 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); } } + + // If we are updating or inserting sites, check for live domain aliases. + $live_domain_alias = "{$node->environment}.{$project->live_domain}"; + + // If project should use live_domain_aliases, and site does not have the alias already... + if ($node->type == 'site' && $project->live_domain_aliases && array_search($live_domain_alias, $node->aliases) === FALSE) { + $node->aliases[] = $live_domain_alias; + } } } } From 7bec33a83215c627b3d80c5b961f6c076e47af60 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:35:14 -0400 Subject: [PATCH 0668/3476] Overriding goto_site menu callback to work with live domains. --- devshop_hosting.module | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/devshop_hosting.module b/devshop_hosting.module index 528ab2ef5..4f27f9ba2 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -66,6 +66,79 @@ function devshop_hosting_menu_alter(&$items){ 'weight' => -1, 'type' => MENU_DEFAULT_LOCAL_TASK, ); + + $items['node/%hosting_site_node/goto_site']['page callback'] = 'devshop_hosting_site_goto'; +} + +/** + * Override for hosting_site_goto(). + * @param $node + */ +function devshop_hosting_site_goto($node) { + $cid = "hosting:site:" . $node->nid . ":login_link"; + $cache = cache_get($cid); + if (user_access('create login-reset task') && !is_null($cache) && (time() < $cache->data['expire'])) { + $theurl = $cache->data['link']; + cache_clear_all($cid, 'cache'); + } + else { + $theurl = devshop_hosting_site_url($node); + } + + drupal_goto($theurl); + exit(); +} + +/** + * Override for _hosting_site_url(). + * @param $node + * + * Allows us to check for project live domain alias. + */ +function devshop_hosting_site_url($node) { + $schema = 'http'; + $port = null; + + if (isset($node->project_nid)){ + $project = node_load($node->project_nid); + $url = "{$node->environment}.{$project->base_url}"; + } + else { + $url = strtolower(trim($node->title)); + } + + $platform = node_load($node->platform); + $server = node_load($platform->web_server); + + + if ($server->services['http']->has_port()) { + $port = $server->services['http']->port; + if ($port == 80) { + $port = null; + } + } + + /** + * This is part of the ssl feature, but is better to implement here. + */ + if (isset($node->ssl_enabled) && ($node->ssl_enabled == 2)) { + // this is a bit of a magic number, because we cant rely on the constant being available yet. + // 2 == Only SSL is enabled. + $schema = 'https'; + + if ($server->services['http']->has_port()) { + $port = $server->services['http']->ssl_port; + if ($port == 443) { + $port = null; + } + } + } + + if (is_numeric($port)) { + return "{$schema}://{$url}:{$port}"; + } + + return "{$schema}://{$url}"; } /** From 69b306a80a1dd8be3e7d639431441256a91920d6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:46:30 -0400 Subject: [PATCH 0669/3476] Adding check on pull request open. --- devshop_pull/devshop_pull.inc | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index ac4203ede..553fa60f7 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -128,27 +128,27 @@ function devshop_pull_github_webhook($project){ case 'pull_request': $message = 'Pull Request Received.'; - // @TODO: Add to project settings. - $create_environments_from_pr = TRUE; - $branch = $data->pull_request->head->ref; - - $message = "Detected Pull Request for $branch"; - if ($create_environments_from_pr == TRUE){ - // Lookup environments by branch - foreach ($project->settings as $env => $settings){ - if ($settings['git_branch'] == $branch){ - $pull_environment_found = $branch; + if ($data->action == 'opened'){ + // @TODO: Add to project settings. + $create_environments_from_pr = TRUE; + $branch = $data->pull_request->head->ref; + + $message = "Detected Pull Request for $branch"; + if ($create_environments_from_pr == TRUE){ + // Lookup environments by branch + foreach ($project->settings as $env => $settings){ + if ($settings['git_branch'] == $branch){ + $pull_environment_found = $branch; + } + } + if (empty($pull_environment_found)){ + hosting_create_environment($project, str_replace('-', '_', $branch), $branch); + } + else { + print "Branch already has an environment."; } - } - if (empty($pull_environment_found)){ - hosting_create_environment($project, str_replace('-', '_', $branch), $branch); - } - else { - print "Branch already has an environment."; } } - - break; } From b704604a2562c29a11766a1fc5bcbf8ce4ec6267 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:50:23 -0400 Subject: [PATCH 0670/3476] Removing slashes and dashes for env name. --- devshop_pull/devshop_pull.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 553fa60f7..5f4302fa6 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -142,7 +142,8 @@ function devshop_pull_github_webhook($project){ } } if (empty($pull_environment_found)){ - hosting_create_environment($project, str_replace('-', '_', $branch), $branch); + $environment_name = strtr($branch, array('-' => '_', '/' => '_')); + hosting_create_environment($project, $environment_name, $branch); } else { print "Branch already has an environment."; From 4a5681f5544f3b46007538e9f7f485160d9a45c9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:51:49 -0400 Subject: [PATCH 0671/3476] Fixing strtr --- devshop_pull/devshop_pull.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 5f4302fa6..f6891e151 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -142,7 +142,7 @@ function devshop_pull_github_webhook($project){ } } if (empty($pull_environment_found)){ - $environment_name = strtr($branch, array('-' => '_', '/' => '_')); + $environment_name = strtr(array('-' => '_', '/' => '_'), $branch); hosting_create_environment($project, $environment_name, $branch); } else { From c538af49742bb78beb1f78e405d6c347078e9ab5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:52:59 -0400 Subject: [PATCH 0672/3476] Fixing strtr again --- devshop_pull/devshop_pull.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index f6891e151..5f4302fa6 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -142,7 +142,7 @@ function devshop_pull_github_webhook($project){ } } if (empty($pull_environment_found)){ - $environment_name = strtr(array('-' => '_', '/' => '_'), $branch); + $environment_name = strtr($branch, array('-' => '_', '/' => '_')); hosting_create_environment($project, $environment_name, $branch); } else { From 8766c48d8389a31bf66bac2a40e4abc53c098c1c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 11:57:04 -0400 Subject: [PATCH 0673/3476] Using own project code name is ok. --- devshop_projects/inc/forms.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 7b258c126..dc1b4e94b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -299,7 +299,7 @@ function devshop_projects_validate($node, &$form) { // The project code name must not be in the hosting_context table if (!$node->retry){ - $result = db_fetch_object(db_query("SELECT name, nid FROM {hosting_context} WHERE name = '%s'", $node->title)); + $result = db_fetch_object(db_query("SELECT name, nid FROM {hosting_context} WHERE name = '%s' AND nid != %d", $node->title, $node->nid)); if ($node->nid != $result->nid){ form_set_error('title', t('Project code name is unavailable. Please choose another.')); } From 55df52d190fa4874d965096d0aa8995bdf63e0a8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 13:14:10 -0400 Subject: [PATCH 0674/3476] Fixing up style of fieldsets --- devshop_projects/devshop-style.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 7b69fc163..130798bf8 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -118,4 +118,15 @@ p.error { } #edit-parameters-environment-name-wrapper input.form-text { width: inherit; +} +#edit-live-alias-other-wrapper input.form-text { + width: inherit; + display: inline; +} +body.aegir fieldset { + margin-left: 40px !important; +} +body.aegir fieldset > legend, +body.aegir fieldset > table { + margin-left: -40px !important; } \ No newline at end of file From 8193f3922060f5e9635e7bc55ce793fbf0a0e663 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 13:18:55 -0400 Subject: [PATCH 0675/3476] fixing saving of live drush alias and live domain. --- devshop_projects/devshop-style.css | 2 +- devshop_projects/inc/forms.inc | 52 ++++++++++++++++++++---------- devshop_projects/inc/nodes.inc | 9 ++++++ 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 130798bf8..143e5f0b8 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -119,7 +119,7 @@ p.error { #edit-parameters-environment-name-wrapper input.form-text { width: inherit; } -#edit-live-alias-other-wrapper input.form-text { +#edit-live-environment-alias-wrapper input.form-text { width: inherit; display: inline; } diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index dc1b4e94b..38ea8e972 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -156,11 +156,11 @@ function devshop_projects_form(&$node) { '#tree' => TRUE, ); + $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); foreach ($node->project_objects['platform'] as $nid => $environment_name) { $platform = node_load($nid); $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); - if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { $form['settings'][$environment_name] = array( '#tree' => TRUE, @@ -174,7 +174,10 @@ function devshop_projects_form(&$node) { $form['settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; $form['settings'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } + $site = array_pop($sites); + $live_alias_options[$site->hosting_name] = $site->hosting_name; } + } $form['#submit'] = array('devshop_projects_submit_settings'); @@ -188,21 +191,37 @@ function devshop_projects_form(&$node) { } } - // @TODO: Give the user a select list of all environments, and allow , where they can input an alias manually. -// // Master Site alias -// $form['live_alias'] = array( -// '#type' => 'textfield', -// '#title' => t('Live Site Alias'), -// '#description' => t('The drush alias for the live production site of this project. Can be hosted here or setup as a remote alias.'), -// '#required' => TRUE, -// '#size' => 40, -// '#default_value' => $node->live_alias, -// '#maxlength' => 255, -// '#weight' => 4, -// ); + //All settings git pull in project page + $form['live'] = array( + '#type' => 'fieldset', + '#title' => t('Live Environment Settings'), + '#tree' => FALSE, + ); + + // Master Site alias + $live_alias_options['other'] = 'Other...'; + $form['live']['live_environment'] = array( + '#type' => 'select', + '#title' => t('Live Environment'), + '#options' => $live_alias_options, + '#required' => FALSE, + '#default_value' => $node->live_environment, + '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), + ); + $form['live']['live_environment_alias'] = array( + '#title' => t('Live Site Drush Alias'), + '#type' => 'textfield', + '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), + '#field_prefix' => '@', + '#required' => TRUE, + '#size' => 40, + '#default_value' => $node->live_environment_alias, + '#size' => 50, + '#maxlength' => 50, + ); // Live Domain - $form['live_domain'] = array( + $form['live']['live_domain'] = array( '#type' => 'textfield', '#title' => t('Live domain'), '#description' => t('The live domain for this project. Do not include "www".'), @@ -213,7 +232,7 @@ function devshop_projects_form(&$node) { // Use aliases if (module_exists('hosting_alias')){ - $form['live_domain_aliases'] = array( + $form['live']['live_domain_aliases'] = array( '#type' => 'checkbox', '#title' => t('Use environment aliases'), '#description' => t('Create aliases for all environments as subdomains of the live domain.'), @@ -229,7 +248,6 @@ function devshop_projects_form(&$node) { */ function devshop_projects_submit_settings($form, &$form_state) { - // Go through and save our settings $project_node = node_load($form_state['values']['nid']); $settings_info = module_invoke_all('devshop_project_settings', $project_node); @@ -306,7 +324,7 @@ function devshop_projects_validate($node, &$form) { } // The project code name must be unique - if (!$node->retry && ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid)))) { + if (!$node->retry && ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid)))) { form_set_error('title', t('Project code name is already is use by another project')); } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 990876475..0de91a044 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -145,6 +145,9 @@ function devshop_projects_insert($node) { $data['live_domain'] = $node->live_domain; $data['live_domain_aliases'] = $node->live_domain_aliases; + $data['live_environment'] = $node->live_environment; + $data['live_environment_alias'] = $node->live_environment_alias; + $info = new stdClass(); $info->nid = $node->nid; $info->git_url = $node->git_url; @@ -186,6 +189,9 @@ function devshop_projects_update($node) { $data['live_domain'] = $node->live_domain; $data['live_domain_aliases'] = $node->live_domain_aliases; + $data['live_environment'] = $node->live_environment; + $data['live_environment_alias'] = $node->live_environment_alias; + $info = new stdClass(); $info->nid = $node->nid; $info->git_url = $node->git_url; @@ -264,6 +270,9 @@ function devshop_projects_load($node) { $additions['live_domain'] = $data['live_domain']; $additions['live_domain_aliases'] = $data['live_domain_aliases']; + $additions['live_environment'] = $data['live_environment']; + $additions['live_environment_alias'] = $data['live_environment_alias']; + $additions['project'] = $data; // If live domain is set, override base url. From 442949973962a2915b65af16807cef7e7ccf3627 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 13:24:09 -0400 Subject: [PATCH 0676/3476] Loading live environment alias as live environment if environment is set as "other"; --- devshop_projects/inc/forms.inc | 10 +++++++++- devshop_projects/inc/nodes.inc | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 38ea8e972..4d1049616 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -205,7 +205,7 @@ function devshop_projects_form(&$node) { '#title' => t('Live Environment'), '#options' => $live_alias_options, '#required' => FALSE, - '#default_value' => $node->live_environment, + '#default_value' => in_array($node->live_environment, $live_alias_options)? $node->live_environment: 'other', '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), ); $form['live']['live_environment_alias'] = array( @@ -338,4 +338,12 @@ function devshop_projects_validate($node, &$form) { if (!$node->retry && $add && file_exists($cp)) { form_set_error('code_path', t('Code path directory already exists.')); } + + // If live environment is selected, do not save live environment alias + if ($node->live_environment == 'other'){ + $node->live_environment = $node->live_environment_alias; + } + else { + $node->live_environment_alias = ''; + } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 0de91a044..1a03ea8fa 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -270,7 +270,7 @@ function devshop_projects_load($node) { $additions['live_domain'] = $data['live_domain']; $additions['live_domain_aliases'] = $data['live_domain_aliases']; - $additions['live_environment'] = $data['live_environment']; + $additions['live_environment'] = $data['live_environment'] == 'other'? '@' . $data['live_environment_alias']: $data['live_environment']; $additions['live_environment_alias'] = $data['live_environment_alias']; $additions['project'] = $data; From a2e1e60d3cbe2d1e50527f04162a3d8577fba203 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 13:40:49 -0400 Subject: [PATCH 0677/3476] Respecting live environment in sync form. --- devshop_projects/devshop-style.css | 4 ++++ devshop_projects/inc/forms.inc | 3 +-- devshop_projects/inc/tasks.inc | 10 ++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop-style.css b/devshop_projects/devshop-style.css index 143e5f0b8..cd120e3b5 100644 --- a/devshop_projects/devshop-style.css +++ b/devshop_projects/devshop-style.css @@ -83,6 +83,10 @@ form .sync-envs div.form-item-labeled label { left: 0px; } +#hosting-task-confirm-form fieldset { + clear: both; +} + .path-hosting-projects-add-environments #right { display: none; } diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 4d1049616..c9431ec3d 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -175,7 +175,7 @@ function devshop_projects_form(&$node) { $form['settings'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } $site = array_pop($sites); - $live_alias_options[$site->hosting_name] = $site->hosting_name; + $live_alias_options[$environment_name] = $site->hosting_name; } } @@ -213,7 +213,6 @@ function devshop_projects_form(&$node) { '#type' => 'textfield', '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), '#field_prefix' => '@', - '#required' => TRUE, '#size' => 40, '#default_value' => $node->live_environment_alias, '#size' => 50, diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 0dcea7845..ffc5c6afd 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -343,6 +343,16 @@ function hosting_task_devshop_sync_form($node) { $form['destination']['#prefix'] = '
'; $form['destination']['#suffix'] = '
'; + if (!empty($node->live_environment)){ + // Remove live site from destination options. + unset($form['destination']['#options'][$node->live_environment]); + + $source_options = $form['source']['#options']; + unset($source_options[$node->live_environment]); + $form['source']['#options'] = array_merge(array($node->live_environment => $node->live_environment . " (LIVE ENVIRONMENT)"), $source_options); + $form['source']['#default_value'] = $node->live_environment; + } + $form['database'] = array( '#title' => t('Copy database from source to destination.'), '#type' => 'checkbox', From 7c826428e78dbef2f4a8d83e16255f3ec0d0bdea Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 13:46:17 -0400 Subject: [PATCH 0678/3476] Adding live alias as an option for sync. --- devshop_projects/inc/forms.inc | 2 +- devshop_projects/inc/tasks.inc | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c9431ec3d..2f67d1f70 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -205,7 +205,7 @@ function devshop_projects_form(&$node) { '#title' => t('Live Environment'), '#options' => $live_alias_options, '#required' => FALSE, - '#default_value' => in_array($node->live_environment, $live_alias_options)? $node->live_environment: 'other', + '#default_value' => isset($live_alias_options[$node->live_environment])? $node->live_environment: 'other', '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), ); $form['live']['live_environment_alias'] = array( diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index ffc5c6afd..e0c1cb159 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -343,7 +343,7 @@ function hosting_task_devshop_sync_form($node) { $form['destination']['#prefix'] = '
'; $form['destination']['#suffix'] = '
'; - if (!empty($node->live_environment)){ + if (!empty($form['destination']['#options'][$node->live_environment])){ // Remove live site from destination options. unset($form['destination']['#options'][$node->live_environment]); @@ -352,6 +352,9 @@ function hosting_task_devshop_sync_form($node) { $form['source']['#options'] = array_merge(array($node->live_environment => $node->live_environment . " (LIVE ENVIRONMENT)"), $source_options); $form['source']['#default_value'] = $node->live_environment; } + elseif (!empty($node->live_environment_alias)){ + $form['source']['#options']["@" . $node->live_environment_alias] = "@" . $node->live_environment_alias . " (LIVE ENVIRONMENT)"; + } $form['database'] = array( '#title' => t('Copy database from source to destination.'), From 8f4316f48dd31162f34f86584992af95f4a8b0bd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 14 Mar 2014 13:49:33 -0400 Subject: [PATCH 0679/3476] removing replacement of base url. --- devshop_projects/inc/nodes.inc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 1a03ea8fa..75c57edb9 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -275,11 +275,6 @@ function devshop_projects_load($node) { $additions['project'] = $data; - // If live domain is set, override base url. - if (!empty($additions['live_domain'])){ - $additions['base_url'] = $additions['live_domain']; - } - $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); $objects = array(); From 81951d685c194ff5079ff67ce62a770034e14209 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 15 Mar 2014 16:22:11 -0400 Subject: [PATCH 0680/3476] Use number of environments as the dropdown link. --- devshop_projects/inc/ui.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 40cb17096..11cfc60c1 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -50,6 +50,7 @@ function devshop_projects_projects_page() { // Links to all sites $actions = array(); + $count = 0; foreach ($node->environments as $env => $details){ $dev_site_url = url("http://$env.$node->base_url", array('absolute' => TRUE)); $actions[] = array( @@ -57,8 +58,10 @@ function devshop_projects_projects_page() { 'href' => $dev_site_url, 'attributes' => array('target' => '_blank'), ); + $count++; } - $row[] = theme('ctools_dropdown', t('Visit...'), $actions); + $label = format_plural($count, t('1 Environment'), t('@num Environments', array('@num' => $count))); + $row[] = theme('ctools_dropdown', $label, $actions); $rows[$proj->nid] = $row; } From 86f5999b3e23dc31c1188015ad91d208dd68fb2e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 15 Mar 2014 18:31:21 -0400 Subject: [PATCH 0681/3476] Fixing #2018265: premature complete detection. --- devshop_projects/inc/create-wizard.inc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 0526bd649..aa00e1821 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -749,6 +749,13 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // If platform verified successfully: if ($task->task_status == HOSTING_TASK_SUCCESS) { + // It's not really ready until we get a version. + if (empty($platform->release->version)){ + $completed = FALSE; + $row['version'] = '...'; + $row['profiles'] = '...'; + } + // Collect install profiles $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { From 442c7e22060b68f9c35e30d8454dcded138305ce Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 15 Mar 2014 18:34:18 -0400 Subject: [PATCH 0682/3476] Adding hosting alias as a dependency. --- devshop_projects/devshop_projects.info | 1 + devshop_projects/inc/forms.inc | 16 +++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/devshop_projects/devshop_projects.info b/devshop_projects/devshop_projects.info index 005571436..4dfaf903d 100644 --- a/devshop_projects/devshop_projects.info +++ b/devshop_projects/devshop_projects.info @@ -4,4 +4,5 @@ core = 6.x package = DevShop dependencies[] = hosting +dependencies[] = hosting_alias dependencies[] = ctools diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 2f67d1f70..b5beb9605 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -230,15 +230,13 @@ function devshop_projects_form(&$node) { ); // Use aliases - if (module_exists('hosting_alias')){ - $form['live']['live_domain_aliases'] = array( - '#type' => 'checkbox', - '#title' => t('Use environment aliases'), - '#description' => t('Create aliases for all environments as subdomains of the live domain.'), - '#default_value' => $node->live_domain_aliases, - '#weight' => 6, - ); - } + $form['live']['live_domain_aliases'] = array( + '#type' => 'checkbox', + '#title' => t('Use environment aliases'), + '#description' => t('Create aliases for all environments as subdomains of the live domain.'), + '#default_value' => $node->live_domain_aliases, + '#weight' => 6, + ); return $form; } From 73f5629f6626ef32227e0163f881ec51a0adae56 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 15 Mar 2014 18:39:40 -0400 Subject: [PATCH 0683/3476] fixing my bad function name --- devshop_projects/devshop_projects.install | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 6ad028dcd..5f0361126 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -219,13 +219,20 @@ function devshop_projects_update_9() { /** - * Remove live_domain from hosting_devsho_project schema and disable devshop_live if needed. + * Remove live_domain from hosting_devshop_project schema and disable devshop_live if needed. */ function devshop_projects_update_10() { - modules_disable(array('devshop_live')); $ret = array(); if (db_column_exists('hosting_devshop_project', 'live_domain')){ $ret[] = db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); } return $ret; +} + +/** + * Enable hosting_alias. + */ +function devshop_projects_update_11() { + module_enable(array('hosting_alias')); + return array(); } \ No newline at end of file From 0c15fd897c973f35a3e911a62680ee180b7e2e67 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Mar 2014 21:03:17 -0400 Subject: [PATCH 0684/3476] Moving forgotten arguments for the devshop-pull task. --- devshop_pull/devshop_pull.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 5f4302fa6..6eb4716e4 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -104,6 +104,7 @@ function devshop_pull_default_webhook($project, $environments_to_pull){ /** * GitHub action to take on webhook init + * @TODO: Once it is more pluggable, put this in it's own include, or maybe it's own module. */ function devshop_pull_github_webhook($project){ $headers = getallheaders(); @@ -112,16 +113,15 @@ function devshop_pull_github_webhook($project){ $data = json_decode($GLOBALS['HTTP_RAW_POST_DATA']); $args = array(); -// $args['environments'] = implode(' ', $environments_to_pull); $args['cache'] = 1; - switch ($headers['X-GitHub-Event']){ case 'ping': $message = 'Pong!'; break; case 'push': // Add a task to pull then project's environments + $args['environments'] = implode(' ', $environments_to_pull); hosting_add_task($project->nid, 'devshop-pull', $args); $message = 'Push Received.'; break; From 5bbd335385d49ea2807bf4cf21622281bff099ca Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 17 Mar 2014 21:05:53 -0400 Subject: [PATCH 0685/3476] Checking for environments to pull. --- devshop_pull/devshop_pull.inc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 6eb4716e4..0364150c1 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -121,6 +121,13 @@ function devshop_pull_github_webhook($project){ break; case 'push': // Add a task to pull then project's environments + // Check for environments set to pull + $environments_to_pull = array(); + foreach ($project->settings as $env => $settings) { + if ($settings['pull_enabled']) { + $environments_to_pull[] = $env; + } + } $args['environments'] = implode(' ', $environments_to_pull); hosting_add_task($project->nid, 'devshop-pull', $args); $message = 'Push Received.'; From 9ebece53c16d2a7badb456dcc778a87f451889bd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 10:00:37 -0400 Subject: [PATCH 0686/3476] Change settings to environment settings. --- devshop_projects/inc/forms.inc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 2f67d1f70..f20c4f47b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -149,7 +149,7 @@ function devshop_projects_form(&$node) { ); // Extensible settings - $form['settings'] = array( + $form['environment_settings'] = array( '#type' => 'fieldset', '#title' => t('Environment Settings'), '#theme' => 'devshop_projects_settings_form', @@ -162,7 +162,7 @@ function devshop_projects_form(&$node) { $platform = node_load($nid); $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { - $form['settings'][$environment_name] = array( + $form['environment_settings'][$environment_name] = array( '#tree' => TRUE, '#title' => $name, '#type' => 'fieldset', @@ -170,9 +170,9 @@ function devshop_projects_form(&$node) { ); foreach ($settings as $setting_id => $setting){ - $form['settings'][$environment_name][$setting_id] = $setting; - $form['settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; - $form['settings'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['environment_settings'][$environment_name][$setting_id] = $setting; + $form['environment_settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; + $form['environment_settings'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } $site = array_pop($sites); $live_alias_options[$environment_name] = $site->hosting_name; @@ -242,6 +242,7 @@ function devshop_projects_form(&$node) { return $form; } + /** * Submit function for save data of platforms. */ @@ -253,7 +254,7 @@ function devshop_projects_submit_settings($form, &$form_state) { $nodes = array(); // Go through each environment - foreach ($form_state['values']['settings'] as $environment_name => $settings){ + foreach ($form_state['values']['environment_settings'] as $environment_name => $settings){ // Then through each setting of each environment foreach ($settings as $setting_name => $setting_value){ From 5f078c1f72abee7049c049a0dceb109c3d67b359 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 10:01:14 -0400 Subject: [PATCH 0687/3476] Adding pull_request_environments setting for projects. --- devshop_pull/devshop_pull.inc | 3 +-- devshop_pull/devshop_pull.module | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 5f4302fa6..211256e57 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -130,11 +130,10 @@ function devshop_pull_github_webhook($project){ if ($data->action == 'opened'){ // @TODO: Add to project settings. - $create_environments_from_pr = TRUE; $branch = $data->pull_request->head->ref; $message = "Detected Pull Request for $branch"; - if ($create_environments_from_pr == TRUE){ + if ($project->pull_request_environments){ // Lookup environments by branch foreach ($project->settings as $env => $settings){ if ($settings['git_branch'] == $branch){ diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 70c8cfcf6..524595697 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -121,7 +121,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#type' => 'radios', '#description' => t('Choose the method of regularly calling "Pull Code". See !link to configure the queue. See !link2 to configure URL Callback.', array( '!link' => l(t('Hosting > Queues'), 'admin/hosting/queues'), - '!link2' => l(t('Hosting > DevShop Pull Settings'), 'admin/hosting/devshop_pull') + '!link2' => l(t('Hosting > DevShop Pull Settings'), 'admin/hosting/devshop/pull') )), '#default_value' => $node->pull_method, '#options' => array( @@ -154,6 +154,13 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#type' => 'value', '#value' => $node->last_pull_ip, ); + + $form['git_settings']['pull_request_environments'] = array( + '#type' => 'checkbox', + '#title' => t('Create Environments for Pull Requests'), + '#default_value' => $node->pull_request_environments, + '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), + ); } } From 59868545169688441651d5c08ed04d71f012d8a5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 11:01:41 -0400 Subject: [PATCH 0688/3476] refactoring project_settings to use serialized data for everything. --- devshop_projects/devshop_projects.drush.inc | 8 ++-- devshop_projects/inc/forms.inc | 53 +++++++++++++-------- devshop_projects/inc/nodes.inc | 38 +++++---------- devshop_projects/inc/ui.inc | 36 +++++++------- devshop_pull/devshop_pull.module | 34 ++++++------- 5 files changed, 87 insertions(+), 82 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 848d3d114..b41d20fd7 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -302,8 +302,8 @@ function devshop_projects_hosting_project_context_options(&$task) { $branches = getBranchesAndTags($task->ref->git_url); - $task->ref->git_branches = $branches['branches']; - $task->ref->git_tags = $branches['tags']; + $task->ref->project_settings['git_branches'] = $branches['branches']; + $task->ref->project_settings['git_tags'] = $branches['tags']; // Save the project node now that we have branches and tags. node_save($task->ref); @@ -333,8 +333,8 @@ function devshop_projects_drush_context_import($context, &$node) { $node->git_url = $context->git_url; $branches = getBranchesAndTags($context->git_url); - $node->git_branches = $branches['branches']; - $node->git_tags = $branches['tags']; + $node->project_settings['git_branches'] = $branches['branches']; + $node->project_settings['git_tags'] = $branches['tags']; } } diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index f20c4f47b..9aa339d9e 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -138,16 +138,6 @@ function devshop_projects_form(&$node) { '#weight' => 4, ); - // Unchanging Values - $form['git_branches'] = array( - '#type' => 'value', - '#value' => $node->git_branches, - ); - $form['git_tags'] = array( - '#type' => 'value', - '#value' => $node->git_tags, - ); - // Extensible settings $form['environment_settings'] = array( '#type' => 'fieldset', @@ -181,6 +171,21 @@ function devshop_projects_form(&$node) { } $form['#submit'] = array('devshop_projects_submit_settings'); + // Project Settings + $form['project_settings'] = array( + '#tree' => TRUE, + ); + + // Save git branches and tags + $form['project_settings']['git_branches'] = array( + '#type' => 'value', + '#value' => $node->git_branches, + ); + $form['project_settings']['git_tags'] = array( + '#type' => 'value', + '#value' => $node->git_tags, + ); + // Don't allow editing if ($node->nid) { @@ -192,50 +197,58 @@ function devshop_projects_form(&$node) { } //All settings git pull in project page - $form['live'] = array( + $form['project_settings']['live'] = array( '#type' => 'fieldset', '#title' => t('Live Environment Settings'), - '#tree' => FALSE, ); // Master Site alias $live_alias_options['other'] = 'Other...'; - $form['live']['live_environment'] = array( + if (empty($live_alias_options[$node->project_settings['live']['live_environment']])){ + // No live environment + $live_environment_default_value = ''; + } + elseif (!empty($node->project_settings['live']['live_environment_alias'])){ + // "other" live environment + $live_environment_default_value = 'other'; + } + + $form['project_settings']['live']['live_environment'] = array( '#type' => 'select', '#title' => t('Live Environment'), '#options' => $live_alias_options, '#required' => FALSE, - '#default_value' => isset($live_alias_options[$node->live_environment])? $node->live_environment: 'other', + '#default_value' => $live_environment_default_value, '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), ); - $form['live']['live_environment_alias'] = array( + $form['project_settings']['live']['live_environment_alias'] = array( '#title' => t('Live Site Drush Alias'), '#type' => 'textfield', '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), '#field_prefix' => '@', '#size' => 40, - '#default_value' => $node->live_environment_alias, + '#default_value' => $node->project_settings['live']['live_environment_alias'], '#size' => 50, '#maxlength' => 50, ); // Live Domain - $form['live']['live_domain'] = array( + $form['project_settings']['live']['live_domain'] = array( '#type' => 'textfield', '#title' => t('Live domain'), '#description' => t('The live domain for this project. Do not include "www".'), '#size' => 40, - '#default_value' => $node->live_domain, + '#default_value' => $node->project_settings['live']['live_domain'], '#weight' => 5, ); // Use aliases if (module_exists('hosting_alias')){ - $form['live']['live_domain_aliases'] = array( + $form['project_settings']['live']['live_domain_aliases'] = array( '#type' => 'checkbox', '#title' => t('Use environment aliases'), '#description' => t('Create aliases for all environments as subdomains of the live domain.'), - '#default_value' => $node->live_domain_aliases, + '#default_value' => $node->project_settings['live']['live_domain_aliases'], '#weight' => 6, ); } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 75c57edb9..7d5ccf372 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -138,12 +138,13 @@ function devshop_projects_insert($node) { hosting_add_task($node->nid, 'verify'); } - $data = array(); + $data = $node->project_settings; $data['git_branches'] = $node->git_branches; $data['git_tags'] = $node->git_tags; - $data['live_alias'] = $node->live_alias; - $data['live_domain'] = $node->live_domain; - $data['live_domain_aliases'] = $node->live_domain_aliases; + +// $data['live_alias'] = $node->live_alias; +// $data['live_domain'] = $node->live_domain; +// $data['live_domain_aliases'] = $node->live_domain_aliases; $data['live_environment'] = $node->live_environment; $data['live_environment_alias'] = $node->live_environment_alias; @@ -176,21 +177,9 @@ function devshop_projects_insert($node) { * */ function devshop_projects_update($node) { - if (!$node->no_verify) { hosting_add_task($node->nid, 'verify'); } - $data = array(); - - // Branches and tags get saved upon verify. - $data['git_branches'] = $node->git_branches; - $data['git_tags'] = $node->git_tags; - $data['live_alias'] = $node->live_alias; - $data['live_domain'] = $node->live_domain; - $data['live_domain_aliases'] = $node->live_domain_aliases; - - $data['live_environment'] = $node->live_environment; - $data['live_environment_alias'] = $node->live_environment_alias; $info = new stdClass(); $info->nid = $node->nid; @@ -199,7 +188,9 @@ function devshop_projects_update($node) { $info->drupal_path = hosting_path_normalize($node->drupal_path); $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; - $info->data = serialize($data); + + // Everything in $form['project_settings'] gets serialized and saved. + $info->data = serialize($node->project_settings); drupal_write_record('hosting_devshop_project', $info, 'nid'); @@ -264,16 +255,13 @@ function devshop_projects_load($node) { $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; $additions += $hosting_name; } - $data = unserialize($additions['data']); - $additions['git_branches'] = $data['git_branches']; - $additions['git_tags'] = $data['git_tags']; - $additions['live_domain'] = $data['live_domain']; - $additions['live_domain_aliases'] = $data['live_domain_aliases']; - $additions['live_environment'] = $data['live_environment'] == 'other'? '@' . $data['live_environment_alias']: $data['live_environment']; - $additions['live_environment_alias'] = $data['live_environment_alias']; + // Load up all project settings + $additions['project_settings'] = unserialize($additions['data']); - $additions['project'] = $data; + // Make most common attributes available. + $additions['git_branches'] = $additions['project_settings']['git_branches']; + $additions['git_tags'] = $additions['project_settings']['git_tags']; $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 40cb17096..678f355fe 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -136,24 +136,28 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -10 ); - if (!empty($node->git_branches)){ - $items = theme_item_list($node->git_branches, NULL, 'ul', array('class' => 'branches')); - $verify_task = hosting_get_most_recent_task($node->nid, 'verify'); - - if ($verify_task->task_status == HOSTING_TASK_SUCCESS){ - $refresh_link = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); - } else { - $refresh_link = t('Refresh in progress...'); - } - - $node->content['info']['git_branches'] = array( - '#type' => 'item', - '#title' => t('Remote Branches'), - '#value' => '
' . $items . $refresh_link . '
', - '#weight' => -8, - ); + // Branches display + if (!empty($node->project_settings['git_branches'])){ + $items = theme_item_list($node->project_settings['git_branches'], NULL, 'ul', array('class' => 'branches')); + } + else { + $items = theme_item_list(array('No Branches Found!'), NULL, 'ul', array('class' => 'branches')); + } + $verify_task = hosting_get_most_recent_task($node->nid, 'verify'); + + if ($verify_task->task_status == HOSTING_TASK_SUCCESS){ + $refresh_link = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); + } else { + $refresh_link = t('Refresh in progress...'); } + $node->content['info']['git_branches'] = array( + '#type' => 'item', + '#title' => t('Remote Branches'), + '#value' => '
' . $items . $refresh_link . '
', + '#weight' => -8, + ); + if (!empty($node->install_profile)){ $node->content['info']['install_profile'] = array( '#type' => 'item', diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 524595697..3d1f14991 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -111,19 +111,19 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { } //All settings git pull in project page - $form['git_settings'] = array( + $form['project_settings']['pull'] = array( '#type' => 'fieldset', '#title' => t('Git Settings'), ); - $form['git_settings']['pull_method'] = array( + $form['project_settings']['pull']['pull_method'] = array( '#title' => 'Automatic Git Pull Method', '#type' => 'radios', '#description' => t('Choose the method of regularly calling "Pull Code". See !link to configure the queue. See !link2 to configure URL Callback.', array( '!link' => l(t('Hosting > Queues'), 'admin/hosting/queues'), '!link2' => l(t('Hosting > DevShop Pull Settings'), 'admin/hosting/devshop/pull') )), - '#default_value' => $node->pull_method, + '#default_value' => $node->project_settings['pull']['pull_method'], '#options' => array( DEVSHOP_PULL_DISABLED => t('Pull disabled.'), DEVSHOP_PULL_QUEUE => t('Pull on queue (every minute).'), @@ -131,7 +131,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { ), ); module_load_include('inc', 'devshop_pull'); - $form['git_settings']['pull_url'] = array( + $form['project_settings']['pull']['pull_url'] = array( '#type' => 'textfield', '#title' => t('Pull Trigger URL'), '#value' => _devshop_pull_callback_url($node), @@ -142,23 +142,23 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { ); // @TODO: is there a better way to save certain values? We lose data without these. - $form['git_settings']['last_pull'] = array( + $form['project_settings']['pull']['last_pull'] = array( '#type' => 'value', - '#value' => $node->last_pull, + '#value' => $node->project_settings['pull']['last_pull'], ); - $form['git_settings']['last_pull_status'] = array( + $form['project_settings']['pull']['last_pull_status'] = array( '#type' => 'value', - '#value' => $node->last_pull_status, + '#value' => $node->project_settings['pull']['last_pull_status'], ); - $form['git_settings']['last_pull_ip'] = array( + $form['project_settings']['pull']['last_pull_ip'] = array( '#type' => 'value', - '#value' => $node->last_pull_ip, + '#value' => $node->project_settings['pull']['last_pull_ip'], ); - $form['git_settings']['pull_request_environments'] = array( + $form['project_settings']['pull']['pull_request_environments'] = array( '#type' => 'checkbox', '#title' => t('Create Environments for Pull Requests'), - '#default_value' => $node->pull_request_environments, + '#default_value' => $node->project_settings['pull']['pull_request_environments'], '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), ); } @@ -175,26 +175,26 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'project'){ // View Project - if ($op == 'view' && $node->pull_method == DEVSHOP_PULL_CALLBACK){ + if ($op == 'view' && $node->project_settings['pull']['pull_method']== DEVSHOP_PULL_CALLBACK){ module_load_include('inc', 'devshop_pull'); $url = _devshop_pull_callback_url($node); $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); - $status = (int) $node->last_pull_status; + $status = (int) $node->project_settings['pull']['last_pull_status']; // If access denied, provide link to settings page if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ $output = '' . t('Access Denied') . '
'; - $output .= '' . hosting_format_interval($node->last_pull) . '
'; + $output .= '' . hosting_format_interval($node->project_settings['pull']['last_pull']) . '
'; $output .= t('Commit notification recieved from %ip, but the IP is not allowed to trigger tasks. See !link.', array( '!link' => l(t('DevShop Pull Settings'), 'admin/hosting/devshop_pull'), - '%ip' => $node->last_pull_ip, + '%ip' => $node->project_settings['pull']['last_pull_ip'], )); } // If OK, show how much time has passed. elseif ($status == DEVSHOP_PULL_STATUS_OK) { - $output = hosting_format_interval($node->last_pull); + $output = hosting_format_interval($node->project_settings['pull']['last_pull']); } // Otherwise, we assume no commit notification recieved. else { From 1f01a4ffa67cda7d2bbdaa1c7150d4e792264e55 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 11:20:23 -0400 Subject: [PATCH 0689/3476] Commenting out code referencing separate schema. --- devshop_pull/devshop_pull.module | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 3d1f14991..4bd4867c6 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -211,29 +211,29 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { // Load Project elseif ($op == 'load'){ - $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); - if (!empty($data)) { - unset($data['project_nid']); - return $data; - } +// $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); +// if (!empty($data)) { +// unset($data['project_nid']); +// return $data; +// } } // Insert Project elseif ($op == 'insert'){ - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, %d, "%s")', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); +// db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, %d, "%s")', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); } // Update Project elseif ($op == 'update'){ // We can't update because devshop_pull might have been enabled after // project exists - db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, "%s", %d)', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); +// db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); +// db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, "%s", %d)', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); } // Delete Project elseif ($op == 'delete'){ - db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); +// db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); } } @@ -243,27 +243,27 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { // Load Platform if ($node->type == 'platform' && $op == 'load'){ - $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); - if (!empty($data)){ - unset($data['project_nid']); - return $data; - } +// $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); +// if (!empty($data)){ +// unset($data['project_nid']); +// return $data; +// } } // Insert Platform elseif ($op == 'insert'){ - db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); +// db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); } // Update Platform elseif ($op == 'update'){ - db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); - db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); +// db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); +// db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); } // Delete Project elseif ($op == 'delete'){ - db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); +// db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); } } } From 4944c54f2b9de15db44135a6bf7899919c1ff91d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 11:24:13 -0400 Subject: [PATCH 0690/3476] Removing environment specific pull on commit settings. Adding "pull enabled" switch instead. --- devshop_pull/devshop_pull.module | 79 +++++++++++++++----------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 4bd4867c6..7d2602739 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -74,26 +74,26 @@ function devshop_pull_hosting_queues() { return $items; } -/** - * Implements hook_devshop_project_settings() - */ -function devshop_pull_devshop_project_settings(){ - return array( - 'pull_enabled' => array( - '#title' => t('Pull on Commit'), - '#node_type' => 'platform', - '#type' => 'checkbox', - '#description' => t('When DevShop receives commit notification, Pull Code.'), - ), - // Pull reset won't work until we are storing the settings in the project drush context. - //'pull_reset' => array( - // '#title' => t('Reset files'), - // '#node_type' => 'platform', - // '#type' => 'checkbox', - // '#description' => t('When runing a Pull Code task triggered by a git commit, run git reset --hard just before.'), - //), - ); -} +///** +// * Implements hook_devshop_project_settings() +// */ +//function devshop_pull_devshop_project_settings(){ +// return array( +// 'pull_enabled' => array( +// '#title' => t('Pull on Commit'), +// '#node_type' => 'platform', +// '#type' => 'checkbox', +// '#description' => t('When DevShop receives commit notification, Pull Code.'), +// ), +// // Pull reset won't work until we are storing the settings in the project drush context. +// //'pull_reset' => array( +// // '#title' => t('Reset files'), +// // '#node_type' => 'platform', +// // '#type' => 'checkbox', +// // '#description' => t('When runing a Pull Code task triggered by a git commit, run git reset --hard just before.'), +// //), +// ); +//} /** * Implements hook_form_alter(). @@ -116,31 +116,24 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#title' => t('Git Settings'), ); - $form['project_settings']['pull']['pull_method'] = array( - '#title' => 'Automatic Git Pull Method', - '#type' => 'radios', - '#description' => t('Choose the method of regularly calling "Pull Code". See !link to configure the queue. See !link2 to configure URL Callback.', array( - '!link' => l(t('Hosting > Queues'), 'admin/hosting/queues'), - '!link2' => l(t('Hosting > DevShop Pull Settings'), 'admin/hosting/devshop/pull') - )), - '#default_value' => $node->project_settings['pull']['pull_method'], - '#options' => array( - DEVSHOP_PULL_DISABLED => t('Pull disabled.'), - DEVSHOP_PULL_QUEUE => t('Pull on queue (every minute).'), - DEVSHOP_PULL_CALLBACK => t('Pull on URL Callback (ie. GitHub Webhook)'), - ), - ); - module_load_include('inc', 'devshop_pull'); - $form['project_settings']['pull']['pull_url'] = array( - '#type' => 'textfield', - '#title' => t('Pull Trigger URL'), - '#value' => _devshop_pull_callback_url($node), - '#description' => t('Configure your repo to hit this URL when it receives a commit.'), - '#attributes' => array( - 'onclick' => 'this.select()', - ), + $form['project_settings']['pull']['pull_enabled'] = array( + '#title' => 'Pull on Commit', + '#type' => 'checkbox', + '#description' => t('Run a "Pull Code" task when a commit notification is received.'), + '#default_value' => $node->project_settings['pull']['pull_enabled'], ); +// module_load_include('inc', 'devshop_pull'); +// $form['project_settings']['pull']['pull_url'] = array( +// '#type' => 'textfield', +// '#title' => t('Pull Trigger URL'), +// '#value' => _devshop_pull_callback_url($node), +// '#description' => t('Configure your repo to hit this URL when it receives a commit.'), +// '#attributes' => array( +// 'onclick' => 'this.select()', +// ), +// ); + // @TODO: is there a better way to save certain values? We lose data without these. $form['project_settings']['pull']['last_pull'] = array( '#type' => 'value', From 5466915fbd0ba680fc4c49483843e9ebf36fa0d1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 11:24:55 -0400 Subject: [PATCH 0691/3476] comment enhancement. --- devshop_pull/devshop_pull.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 7d2602739..e5e5b865f 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -119,7 +119,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { $form['project_settings']['pull']['pull_enabled'] = array( '#title' => 'Pull on Commit', '#type' => 'checkbox', - '#description' => t('Run a "Pull Code" task when a commit notification is received.'), + '#description' => t('Run a "Pull Code" task when a commit notification is received. All environments set to a branch will have their code updated.'), '#default_value' => $node->project_settings['pull']['pull_enabled'], ); From 4d12ab2b9877b22705156858c6738f75afc1f001 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 11:35:12 -0400 Subject: [PATCH 0692/3476] Make branches and tags selector a nested select. --- devshop_projects/devshop_projects.module | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index aa03743fb..5fce6e00f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -140,15 +140,14 @@ function devshop_projects_menu() { */ function devshop_projects_devshop_project_settings($project_node = NULL){ if (!empty($project_node->git_branches)){ - $branch_options = array_combine($project_node->git_branches, $project_node->git_branches); + $branch_options = array( + 'Branches' => $project_node->git_branches, + 'Tags' => $project_node->git_tags, + ); } else { $branch_options = array(); } - foreach ($project_node->git_tags as $tag){ - $branch_options[$tag] = "tag/" . $tag; - } - $settings = array(); $settings['site_nid'] = array( '#node_type' => 'site', From de24000ff06a368c805de0490bdd1461c67b5834 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 15:28:26 -0400 Subject: [PATCH 0693/3476] Refactoring project object completely. --- devshop_projects/devshop_projects.api.php | 4 +- devshop_projects/devshop_projects.drush.inc | 10 +-- devshop_projects/devshop_projects.module | 17 +++-- devshop_projects/inc/forms.inc | 33 ++++----- devshop_projects/inc/nodes.inc | 79 ++++++++------------- devshop_projects/inc/ui.inc | 19 +++-- devshop_pull/devshop_pull.module | 33 ++++----- 7 files changed, 84 insertions(+), 111 deletions(-) diff --git a/devshop_projects/devshop_projects.api.php b/devshop_projects/devshop_projects.api.php index cf1e46f6f..89db5eef2 100644 --- a/devshop_projects/devshop_projects.api.php +++ b/devshop_projects/devshop_projects.api.php @@ -22,8 +22,8 @@ * * */ -function hook_devshop_project_settings($project_node = NULL){ - $branch_options = array_combine((array) $project_node->git_branches, (array) $project_node->git_branches); +function hook_devshop_project_settings($node = NULL){ + $branch_options = array_combine((array) $node->project->git->branches, (array) $node->project->git->branches); return array( 'git_branch' => array( '#title' => t('Git Branch'), diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index bee374d58..62508be4e 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -305,10 +305,10 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['drupal_path'] = trim($task->ref->drupal_path, " "); $task->context_options['git_url'] = $task->ref->git_url; - $branches = getBranchesAndTags($task->ref->git_url); + $branches = getBranchesAndTags($task->ref->project->git_url); - $task->ref->project_settings['git_branches'] = $branches['branches']; - $task->ref->project_settings['git_tags'] = $branches['tags']; + $task->ref->project->git['branches'] = $branches['branches']; + $task->ref->project->git['tags'] = $branches['tags']; // Save the project node now that we have branches and tags. node_save($task->ref); @@ -338,8 +338,8 @@ function devshop_projects_drush_context_import($context, &$node) { $node->git_url = $context->git_url; $branches = getBranchesAndTags($context->git_url); - $node->project_settings['git_branches'] = $branches['branches']; - $node->project_settings['git_tags'] = $branches['tags']; + $node->project->git['branches'] = $branches['branches']; + $node->project->git['tags'] = $branches['tags']; } } diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 5fce6e00f..aa985b06c 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -139,13 +139,16 @@ function devshop_projects_menu() { * Implements hook_devshop_project_settings */ function devshop_projects_devshop_project_settings($project_node = NULL){ - if (!empty($project_node->git_branches)){ - $branch_options = array( - 'Branches' => $project_node->git_branches, - 'Tags' => $project_node->git_tags, - ); - } else { - $branch_options = array(); + + // Build branch options + $branch_options = array( + 'Branches' => $project_node->project->git['branches'], + 'Tags' => $project_node->project->git['tags'], + ); + + // If no branches... the project probably needs verification. + if (empty($project_node->project->git->branches)) { + $branch_options = array('' => 'No Branches'); } $settings = array(); diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 9aa339d9e..d8470e60a 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -138,8 +138,14 @@ function devshop_projects_form(&$node) { '#weight' => 4, ); - // Extensible settings - $form['environment_settings'] = array( + // Project Settings + // Every value under $form['project'] gets serialized and saved into a project's "data" column. + $form['project'] = array( + '#tree' => TRUE, + ); + + // Environment settings + $form['project']['environments'] = array( '#type' => 'fieldset', '#title' => t('Environment Settings'), '#theme' => 'devshop_projects_settings_form', @@ -148,21 +154,21 @@ function devshop_projects_form(&$node) { $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); - foreach ($node->project_objects['platform'] as $nid => $environment_name) { - $platform = node_load($nid); - $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); + + foreach ($node->project->environments as $environment_name => $environment) { + $platform = node_load($environment['platform']); + $sites = hosting_get_sites_by_status($environment['platform'], HOSTING_SITE_ENABLED); if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { - $form['environment_settings'][$environment_name] = array( + $form['project']['environments'][$environment_name] = array( '#tree' => TRUE, - '#title' => $name, - '#type' => 'fieldset', + '#title' => $environment_name, '#theme' => 'devshop_projects_settings_table', ); foreach ($settings as $setting_id => $setting){ - $form['environment_settings'][$environment_name][$setting_id] = $setting; - $form['environment_settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; - $form['environment_settings'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project']['environments'][$environment_name][$setting_id] = $setting; + $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $node->project->environments[$environment_name]['settings'][$setting_id]; + $form['project']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } $site = array_pop($sites); $live_alias_options[$environment_name] = $site->hosting_name; @@ -171,11 +177,6 @@ function devshop_projects_form(&$node) { } $form['#submit'] = array('devshop_projects_submit_settings'); - // Project Settings - $form['project_settings'] = array( - '#tree' => TRUE, - ); - // Save git branches and tags $form['project_settings']['git_branches'] = array( '#type' => 'value', diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 7d5ccf372..70d447957 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -36,17 +36,21 @@ function devshop_projects_node_info() { */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - if ($node->type == 'project' && isset($node->git_branches)) { + if ($node->type == 'project') { // Settings are added here, since $git_branches hadn't been added to the // node yet in devshop_projects_load(). $settings = module_invoke_all('devshop_project_settings', $node); - foreach ($settings as $setting_name => $setting) { - $setting_node_type = $setting['#node_type']; - foreach ((array) $node->project_objects[$setting_node_type] as $nid => $environment_name) { - $object = node_load($nid); - $node->settings[$environment_name][$setting_name] = $object->{$setting_name}; + // For each environment, pull values out of the site or platform into a new environments object. + foreach ($node->project->environments as $environment_name => &$environment){ + $nodes = array(); + $nodes['site'] = node_load($environment['site']); + $nodes['platform'] = node_load($environment['platform']); + + foreach ($settings as $setting_name => $setting) { + $environment[$setting_name] = $nodes[$setting['#node_type']]->{$setting_name}; } + $environment = $environment; } } @@ -138,17 +142,6 @@ function devshop_projects_insert($node) { hosting_add_task($node->nid, 'verify'); } - $data = $node->project_settings; - $data['git_branches'] = $node->git_branches; - $data['git_tags'] = $node->git_tags; - -// $data['live_alias'] = $node->live_alias; -// $data['live_domain'] = $node->live_domain; -// $data['live_domain_aliases'] = $node->live_domain_aliases; - - $data['live_environment'] = $node->live_environment; - $data['live_environment_alias'] = $node->live_environment_alias; - $info = new stdClass(); $info->nid = $node->nid; $info->git_url = $node->git_url; @@ -156,7 +149,7 @@ function devshop_projects_insert($node) { $info->drupal_path = hosting_path_normalize($node->drupal_path); $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; - $info->data = serialize($data); + $info->data = serialize($node->project); drupal_write_record('hosting_devshop_project', $info); @@ -189,8 +182,10 @@ function devshop_projects_update($node) { $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; - // Everything in $form['project_settings'] gets serialized and saved. - $info->data = serialize($node->project_settings); + // Everything from $form['project'] gets serialized and saved. + // Save our environments data here as well. + $info->data = serialize($node->project); + drupal_write_record('hosting_devshop_project', $info, 'nid'); @@ -249,41 +244,23 @@ function devshop_projects_delete($node) { * Node object */ function devshop_projects_load($node) { - $additions = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); - $hosting_name = db_fetch_array(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); - if (is_array($hosting_name) && is_array($additions)) { - $hosting_name['hosting_name'] = 'project_' . $hosting_name['hosting_name']; - $additions += $hosting_name; - } - // Load up all project settings - $additions['project_settings'] = unserialize($additions['data']); + // Load everything straight from the database table. + $project_data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); + $additions = $project_data; - // Make most common attributes available. - $additions['git_branches'] = $additions['project_settings']['git_branches']; - $additions['git_tags'] = $additions['project_settings']['git_tags']; + // Load the "hosting context". The unique name in the aegir system. + $additions['hosting_name'] = 'project_' . db_result(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); - $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d", $node->nid); - - $objects = array(); - while($project_object = db_fetch_object($query)) { - // Only load if site or platform is enabled. - $object_node = node_load($project_object->object_nid); - - if (($object_node->type == 'site' && $object_node->site_status == HOSTING_SITE_ENABLED) || - ($object_node->type == 'platform' && $object_node->platform_status == HOSTING_PLATFORM_ENABLED) - ){ - $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; - - } - } - $additions['project_objects'] = $objects; + // Load up all project settings, merging db table and serialized data. + $data = $project_data['data']; + unset($project_data['data']); + $project = (object) array_merge($project_data, unserialize($data)); // Set status - $additions['project_status'] = devshop_project_status((object) (array_merge($additions, (array) $node))); + $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); // Environments Array - // @TODO: Remove the above code, after refactoring out of the rest of the system. $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d ORDER BY environment", $node->nid); $environments = array(); @@ -296,10 +273,10 @@ function devshop_projects_load($node) { $environments[$obj->environment][$obj->object_type] = $obj->object_nid; } } - $additions['environments'] = $environments; - - // Settings are added in devshop_projects_nodeapi + $project->environments = $environments; + // Save project object be available at $node->project. + $additions['project'] = $project; return $additions; } diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 0477211dd..1696ddb87 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -140,18 +140,17 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); // Branches display - if (!empty($node->project_settings['git_branches'])){ - $items = theme_item_list($node->project_settings['git_branches'], NULL, 'ul', array('class' => 'branches')); - } - else { - $items = theme_item_list(array('No Branches Found!'), NULL, 'ul', array('class' => 'branches')); - } +// if (!empty($node->project->git['branches'])){ +// $items = theme_item_list($node->project->git['branches', NULL, 'ul', array('class' => 'branches')); +// } +// else { +// $items = theme_item_list(array('No Branches Found!'), NULL, 'ul', array('class' => 'branches')); +// } $verify_task = hosting_get_most_recent_task($node->nid, 'verify'); - - if ($verify_task->task_status == HOSTING_TASK_SUCCESS){ - $refresh_link = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); - } else { + if (!empty($verify_task) && ($verify_task->task_status == HOSTING_TASK_PROCESSING || $verify_task->task_status == HOSTING_TASK_QUEUED)) { $refresh_link = t('Refresh in progress...'); + } else { + $refresh_link = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); } $node->content['info']['git_branches'] = array( diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index e5e5b865f..cf27db49a 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -74,26 +74,19 @@ function devshop_pull_hosting_queues() { return $items; } -///** -// * Implements hook_devshop_project_settings() -// */ -//function devshop_pull_devshop_project_settings(){ -// return array( -// 'pull_enabled' => array( -// '#title' => t('Pull on Commit'), -// '#node_type' => 'platform', -// '#type' => 'checkbox', -// '#description' => t('When DevShop receives commit notification, Pull Code.'), -// ), -// // Pull reset won't work until we are storing the settings in the project drush context. -// //'pull_reset' => array( -// // '#title' => t('Reset files'), -// // '#node_type' => 'platform', -// // '#type' => 'checkbox', -// // '#description' => t('When runing a Pull Code task triggered by a git commit, run git reset --hard just before.'), -// //), -// ); -//} +/** + * Implements hook_devshop_project_settings() + */ +function devshop_pull_devshop_project_settings(){ + return array( + 'disable_pull' => array( + '#title' => t('Disable Code Pull'), + '#node_type' => 'platform', + '#type' => 'checkbox', + '#description' => t('Prevent git pull and reset, allowing "Live Development".'), + ), + ); +} /** * Implements hook_form_alter(). From dc38e14dce1291679fd3d72844595f80d679496d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 15:41:53 -0400 Subject: [PATCH 0694/3476] Fixing create wizard to work with new project object structure. --- devshop_projects/devshop_projects.module | 7 +++---- devshop_projects/inc/create-wizard.inc | 11 +++++------ devshop_projects/inc/nodes.inc | 6 +++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index aa985b06c..620ff1608 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -143,12 +143,11 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ // Build branch options $branch_options = array( 'Branches' => $project_node->project->git['branches'], - 'Tags' => $project_node->project->git['tags'], ); - // If no branches... the project probably needs verification. - if (empty($project_node->project->git->branches)) { - $branch_options = array('' => 'No Branches'); + // If there are tags... + if (!empty($project_node->project->git['tags'])) { + $branch_options['Tags'] = $project_node->project->git['tags']; } $settings = array(); diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index aa00e1821..e197d139b 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -43,7 +43,6 @@ function devshop_projects_create_wizard($step = NULL){ $project->project_nid = NULL; $project->title = ''; $project->environments = array('NEW' => array()); - $project->live_domain = ''; // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); @@ -170,10 +169,10 @@ function devshop_projects_create_wizard_finish(&$form_state) { // Create the site nodes // @TODO: Can we speed things up here by only running install for the first, // then "Cloning" to create the rest? - foreach ($node->project_objects['platform'] as $nid => $env) { + foreach ($node->project->environments as $environment_name => $environment) { // @TODO: Does this set the http_server as well?? Doesn't look like it. - $db_server = $project->environments[$env]['db_server']; - devshop_projects_create_site($node, node_load($nid), $env, $db_server); + $db_server = $environment['db_server']; + devshop_projects_create_site($node, node_load($environment['platform']), $environment_name, $db_server); } ctools_object_cache_clear('project', $form_state['cache name']); @@ -733,7 +732,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $all_tasks_queued = TRUE; $all_tasks_succeeded = TRUE; - foreach ($project_node->environments as $name => $environment){ + foreach ($project_node->project->environments as $name => $environment){ // Get platform and latest verify task. $platform_nid = $environment['platform']; @@ -840,7 +839,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; - $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch. NOTE: Try reloading the page. This is a known bug.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; + $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; $form['error'] = array( '#type' => 'markup', diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 70d447957..6909d9f4a 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -202,7 +202,7 @@ function devshop_projects_update($node) { function devshop_project_save_domain_aliases($project_node) { if (module_exists('hosting_alias')){ if (!empty($project_node->live_domain) && $project_node->live_domain_aliases) { - foreach ($project_node->settings as $env => $details){ + foreach ($project_node->project->environments as $env => $details){ $domain = "$env.$project_node->live_domain"; $site_node = node_load($details['site_nid']); if (array_search($domain, $site_node->aliases) === FALSE) { @@ -213,7 +213,7 @@ function devshop_project_save_domain_aliases($project_node) { } // If live_domain_aliases is checked, remove them from the nodes. elseif (!$project_node->live_domain_aliases){ - foreach ($project_node->settings as $env => $details){ + foreach ($project_node->project->environments as $env => $details){ $domain = "$env.$project_node->live_domain"; $site_node = node_load($details['site_nid']); $i = array_search($domain, $site_node->aliases); @@ -255,7 +255,7 @@ function devshop_projects_load($node) { // Load up all project settings, merging db table and serialized data. $data = $project_data['data']; unset($project_data['data']); - $project = (object) array_merge($project_data, unserialize($data)); + $project = (object) array_merge((array) $project_data, (array) unserialize($data)); // Set status $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); From 2a61edb5b502b246fea338451d90faba1329baef Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 15:44:08 -0400 Subject: [PATCH 0695/3476] in create form, project doesn't have environments yet. --- devshop_projects/inc/nodes.inc | 18 ++++++++++-------- devshop_projects/inc/ui.inc | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 6909d9f4a..42cb2a5e4 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -42,15 +42,17 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $settings = module_invoke_all('devshop_project_settings', $node); // For each environment, pull values out of the site or platform into a new environments object. - foreach ($node->project->environments as $environment_name => &$environment){ - $nodes = array(); - $nodes['site'] = node_load($environment['site']); - $nodes['platform'] = node_load($environment['platform']); - - foreach ($settings as $setting_name => $setting) { - $environment[$setting_name] = $nodes[$setting['#node_type']]->{$setting_name}; + if (!empty($node->project->environments)){ + foreach ($node->project->environments as $environment_name => &$environment){ + $nodes = array(); + $nodes['site'] = node_load($environment['site']); + $nodes['platform'] = node_load($environment['platform']); + + foreach ($settings as $setting_name => $setting) { + $environment[$setting_name] = $nodes[$setting['#node_type']]->{$setting_name}; + } + $environment = $environment; } - $environment = $environment; } } diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 1696ddb87..21de76c1a 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -171,7 +171,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Environments $rows = array(); - foreach($node->environments as $env => $environment) { + foreach ($node->project->environments as $env => $environment) { $site_nid = $environment['site']; $platform_nid = $environment['platform']; @@ -185,7 +185,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $row = array(); - $row[] = "$site->environment"; + $row[] = "$env"; if ($site->site_status == HOSTING_SITE_DISABLED){ $row[] = devshop_hosting_site_goto_link($site) . '' . t('Disabled') . ''; From 9cac0f3bd3680e90c5c46df11f9001abe0c7475b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 15:59:47 -0400 Subject: [PATCH 0696/3476] More refactoring of project environments. --- devshop_projects/devshop_projects.module | 17 ++++++++---- devshop_projects/inc/create-wizard.inc | 2 +- devshop_projects/inc/nodes.inc | 10 ++++--- devshop_projects/inc/tasks.inc | 10 +++---- devshop_projects/inc/ui.inc | 35 +++++++++++++----------- 5 files changed, 43 insertions(+), 31 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 620ff1608..8755d343b 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -141,13 +141,20 @@ function devshop_projects_menu() { function devshop_projects_devshop_project_settings($project_node = NULL){ // Build branch options - $branch_options = array( - 'Branches' => $project_node->project->git['branches'], - ); + if (is_array($project_node->project->git['branches']) && !empty($project_node->project->git['branches'])) { + $branch_options = array( + 'Branches' => array_combine($project_node->project->git['branches'], $project_node->project->git['branches']), + ); + } // If there are tags... - if (!empty($project_node->project->git['tags'])) { - $branch_options['Tags'] = $project_node->project->git['tags']; + if (is_array($project_node->project->git['tags']) && !empty($project_node->project->git['tags'])) { + $branch_options['Tags'] = array_combine($project_node->project->git['tags'], $project_node->project->git['tags']); + } + + // If there are none... + if (empty($branch_options['Tags']) && empty($branch_options['Branches'])){ + $branch_options = array(t('No branches or tags! Re-validate the project.')); } $settings = array(); diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index e197d139b..d406a685f 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -916,7 +916,7 @@ function devshop_projects_add_status($type = 'platform'){ // When checking platforms... if ($type == 'platform') { - foreach ($project_node->environments as $name => $environment){ + foreach ($project_node->project->environments as $name => $environment){ $nids[] = $environment['platform']; } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 42cb2a5e4..cadfa21df 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -218,10 +218,12 @@ function devshop_project_save_domain_aliases($project_node) { foreach ($project_node->project->environments as $env => $details){ $domain = "$env.$project_node->live_domain"; $site_node = node_load($details['site_nid']); - $i = array_search($domain, $site_node->aliases); - if ($i !== FALSE) { - unset($site_node->aliases[$i]); - node_save($site_node); + if (!empty($site_node->aliases)){ + $i = array_search($domain, $site_node->aliases); + if ($i !== FALSE) { + unset($site_node->aliases[$i]); + node_save($site_node); + } } } } diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index e0c1cb159..904c4b3ae 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -198,14 +198,14 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { * @param $fork_source * If desired, the environment to fork off of. (Copy the database and create a new branch from) */ -function hosting_create_environment($project, $environment_name, $branch, $fork_source = NULL) { +function hosting_create_environment($project_node, $environment_name, $branch, $fork_source = NULL) { $servers = hosting_get_servers('http'); $server = variable_get('devshop_projects_default_web_server', key($servers)); // hosting_platform fields $platform = new stdClass; - $platform->title = $project->title . '_' . $environment_name; + $platform->title = $project_node->title . '_' . $environment_name; $platform->publish_path = $project->code_path . '/' . $environment_name; $platform->web_server = $server; $platform->git_branch = $branch; @@ -218,10 +218,10 @@ function hosting_create_environment($project, $environment_name, $branch, $fork_ } //Check if we are forking another site. - if (isset($project->environments[$fork_source])) { - $platform->clone_nid = $project->environments[$fork_source]['site']; + if (isset($project_node->project->environments[$fork_source])) { + $platform->clone_nid = $project_node->project->environments[$fork_source]['site']; $platform->git_branch = $branch; - $platform->old_branch = $project->settings[$fork_source]['git_branch']; + $platform->old_branch = $project_node->project->environments[$fork_source]['git_branch']; } watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 21de76c1a..710673530 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -51,7 +51,7 @@ function devshop_projects_projects_page() { // Links to all sites $actions = array(); $count = 0; - foreach ($node->environments as $env => $details){ + foreach ($node->project->environments as $env => $details){ $dev_site_url = url("http://$env.$node->base_url", array('absolute' => TRUE)); $actions[] = array( 'title' => $dev_site_url, @@ -192,23 +192,26 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } else { $row[] = devshop_hosting_site_goto_link($site); } -// $row[] = "$site->git_branch"; + + // Display current branch + $row[] = "$site->git_branch"; // Create branch/tag chooser - $actions = array(); - foreach ($node->git_branches as $branch){ - $actions[] = array( - 'title' => $branch, - 'href' => "node/" . $platform_nid . "/deploy", - ); - } - foreach ($node->git_tags as $tag){ - $actions[] = array( - 'title' => "tag/" . $tag, - 'href' => "node/" . $platform_nid . "/deploy", - ); - } - $row[] = theme('ctools_dropdown', $site->git_branch, $actions); + // @TODO: Uncomment once we have a link to go to. +// $actions = array(); +// foreach ($node->project->git['branches'] as $branch){ +// $actions[] = array( +// 'title' => $branch, +// 'href' => "node/" . $platform_nid . "/deploy", +// ); +// } +// foreach ($node->project->git['tags'] as $tag){ +// $actions[] = array( +// 'title' => "tag/" . $tag, +// 'href' => "node/" . $platform_nid . "/deploy", +// ); +// } +// $row[] = theme('ctools_dropdown', $site->git_branch, $actions); if (module_exists('devshop_log')) { From 756ca31a4bf1eba2d511e938492031dca144dceb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 16:19:34 -0400 Subject: [PATCH 0697/3476] Fixing up project settings --- devshop_projects/inc/forms.inc | 61 +++++++++++++++++--------------- devshop_projects/inc/nodes.inc | 1 + devshop_pull/devshop_pull.module | 27 ++++++-------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index d8470e60a..878308c12 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -178,13 +178,13 @@ function devshop_projects_form(&$node) { $form['#submit'] = array('devshop_projects_submit_settings'); // Save git branches and tags - $form['project_settings']['git_branches'] = array( + $form['project']['git']['branches'] = array( '#type' => 'value', - '#value' => $node->git_branches, + '#value' => $node->project->git['branches'], ); - $form['project_settings']['git_tags'] = array( + $form['project']['git']['tags'] = array( '#type' => 'value', - '#value' => $node->git_tags, + '#value' => $node->project->git['tags'], ); @@ -198,23 +198,23 @@ function devshop_projects_form(&$node) { } //All settings git pull in project page - $form['project_settings']['live'] = array( + $form['project']['live'] = array( '#type' => 'fieldset', '#title' => t('Live Environment Settings'), ); // Master Site alias $live_alias_options['other'] = 'Other...'; - if (empty($live_alias_options[$node->project_settings['live']['live_environment']])){ + if (empty($live_alias_options[$node->project->live['live_environment']])){ // No live environment $live_environment_default_value = ''; } - elseif (!empty($node->project_settings['live']['live_environment_alias'])){ + elseif (!empty($node->project->live['live_environment_alias'])){ // "other" live environment $live_environment_default_value = 'other'; } - $form['project_settings']['live']['live_environment'] = array( + $form['project']['live']['live_environment'] = array( '#type' => 'select', '#title' => t('Live Environment'), '#options' => $live_alias_options, @@ -222,34 +222,34 @@ function devshop_projects_form(&$node) { '#default_value' => $live_environment_default_value, '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), ); - $form['project_settings']['live']['live_environment_alias'] = array( + $form['project']['live']['live_environment_alias'] = array( '#title' => t('Live Site Drush Alias'), '#type' => 'textfield', '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), '#field_prefix' => '@', '#size' => 40, - '#default_value' => $node->project_settings['live']['live_environment_alias'], + '#default_value' => $node->project->live['live_environment_alias'], '#size' => 50, '#maxlength' => 50, ); // Live Domain - $form['project_settings']['live']['live_domain'] = array( + $form['project']['live']['live_domain'] = array( '#type' => 'textfield', '#title' => t('Live domain'), '#description' => t('The live domain for this project. Do not include "www".'), '#size' => 40, - '#default_value' => $node->project_settings['live']['live_domain'], + '#default_value' => $node->project->live['live_domain'], '#weight' => 5, ); // Use aliases if (module_exists('hosting_alias')){ - $form['project_settings']['live']['live_domain_aliases'] = array( + $form['project']['live']['live_domain_aliases'] = array( '#type' => 'checkbox', '#title' => t('Use environment aliases'), '#description' => t('Create aliases for all environments as subdomains of the live domain.'), - '#default_value' => $node->project_settings['live']['live_domain_aliases'], + '#default_value' => $node->project->live['live_domain_aliases'], '#weight' => 6, ); } @@ -268,35 +268,35 @@ function devshop_projects_submit_settings($form, &$form_state) { $nodes = array(); // Go through each environment - foreach ($form_state['values']['environment_settings'] as $environment_name => $settings){ + foreach ($form_state['values']['project']['environments'] as $environment_name => $settings){ // Then through each setting of each environment foreach ($settings as $setting_name => $setting_value){ //Find the node type for this setting. $node_type = $settings_info[$setting_name]['#node_type']; - $nids = array_flip($project_node->project_objects[$node_type]); - + // Load the site or platform node for this environment, then set the value and save. - $nid = $nids[$environment_name]; - if (empty($nodes[$nid])){ + if (!empty($project_node->project->environments[$environment_name][$node_type])){ + $nid = $project_node->project->environments[$environment_name][$node_type]; $nodes[$nid] = node_load($nid); $nodes[$nid]->no_verify = TRUE; - } - //If changed database then execute migrate task. - if ($setting_name == 'db_server' && $nodes[$nid]->{$setting_name} != $setting_value) { - $args['target_platform'] = $nodes[$nid]->platform; - $args['new_uri'] = $nodes[$nid]->title; - $args['new_db_server'] = $setting_value; + //If changed database then execute migrate task. + if ($setting_name == 'db_server' && $nodes[$nid]->{$setting_name} != $setting_value) { + $args['target_platform'] = $nodes[$nid]->platform; + $args['new_uri'] = $nodes[$nid]->title; + $args['new_db_server'] = $setting_value; - hosting_add_task($nid, 'migrate', $args); + hosting_add_task($nid, 'migrate', $args); + } + $nodes[$nid]->{$setting_name} = $setting_value; } - $nodes[$nid]->{$setting_name} = $setting_value; } } // Go save all nodes + dsm($nodes, 'nodes to save'); foreach ($nodes as $nid => $node){ node_save($node); } @@ -328,11 +328,14 @@ function devshop_projects_validate($node, &$form) { if(strpos($node->title, ' ') != FALSE) { form_set_error('title', t('Project code name must not contain any white spaces.')); } - // The project code name must not be in the hosting_context table if (!$node->retry){ - $result = db_fetch_object(db_query("SELECT name, nid FROM {hosting_context} WHERE name = '%s' AND nid != %d", $node->title, $node->nid)); + $result = db_fetch_object(db_query("SELECT name, nid FROM {hosting_context} WHERE name = '%s'", $node->title)); if ($node->nid != $result->nid){ + + dsm($node, 'node'); + dsm($result, 'result'); + form_set_error('title', t('Project code name is unavailable. Please choose another.')); } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index cadfa21df..5ad5bc48f 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -186,6 +186,7 @@ function devshop_projects_update($node) { // Everything from $form['project'] gets serialized and saved. // Save our environments data here as well. + dsm($node->project); $info->data = serialize($node->project); diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index cf27db49a..6df910ec9 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -98,22 +98,17 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { // Get node $node = $form['#node']; - // Get default value - if (is_null($node->pull_method)) { - $node->pull_method = DEVSHOP_PULL_DISABLED; - } - //All settings git pull in project page - $form['project_settings']['pull'] = array( + $form['project']['pull'] = array( '#type' => 'fieldset', '#title' => t('Git Settings'), ); - $form['project_settings']['pull']['pull_enabled'] = array( + $form['project']['pull']['pull_enabled'] = array( '#title' => 'Pull on Commit', '#type' => 'checkbox', '#description' => t('Run a "Pull Code" task when a commit notification is received. All environments set to a branch will have their code updated.'), - '#default_value' => $node->project_settings['pull']['pull_enabled'], + '#default_value' => $node->project->pull['pull_enabled'], ); // module_load_include('inc', 'devshop_pull'); @@ -128,23 +123,23 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { // ); // @TODO: is there a better way to save certain values? We lose data without these. - $form['project_settings']['pull']['last_pull'] = array( + $form['project']['pull']['last_pull'] = array( '#type' => 'value', - '#value' => $node->project_settings['pull']['last_pull'], + '#value' => $node->project->pull['last_pull'], ); - $form['project_settings']['pull']['last_pull_status'] = array( + $form['project']['pull']['last_pull_status'] = array( '#type' => 'value', - '#value' => $node->project_settings['pull']['last_pull_status'], + '#value' => $node->project->pull['last_pull_status'], ); - $form['project_settings']['pull']['last_pull_ip'] = array( + $form['project']['pull']['last_pull_ip'] = array( '#type' => 'value', - '#value' => $node->project_settings['pull']['last_pull_ip'], + '#value' => $node->project->pull['last_pull_ip'], ); - $form['project_settings']['pull']['pull_request_environments'] = array( + $form['project']['pull']['pull_request_environments'] = array( '#type' => 'checkbox', '#title' => t('Create Environments for Pull Requests'), - '#default_value' => $node->project_settings['pull']['pull_request_environments'], + '#default_value' => $node->project->pull['pull_request_environments'], '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), ); } From f3c69a39b3c1d9c21faaecd1490dda3c4be083f4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 16:26:51 -0400 Subject: [PATCH 0698/3476] removing dsm --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 5ad5bc48f..cdb41b00b 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -186,7 +186,7 @@ function devshop_projects_update($node) { // Everything from $form['project'] gets serialized and saved. // Save our environments data here as well. - dsm($node->project); +// dsm($node->project); $info->data = serialize($node->project); From 5537d0c37152348ca23b46f1b897cfd71cc97d8e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 16:56:17 -0400 Subject: [PATCH 0699/3476] Cleaning up form code. --- devshop_projects/inc/forms.inc | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 878308c12..8bbd730df 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -154,11 +154,7 @@ function devshop_projects_form(&$node) { $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); - foreach ($node->project->environments as $environment_name => $environment) { - $platform = node_load($environment['platform']); - $sites = hosting_get_sites_by_status($environment['platform'], HOSTING_SITE_ENABLED); - if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { $form['project']['environments'][$environment_name] = array( '#tree' => TRUE, '#title' => $environment_name, @@ -167,13 +163,11 @@ function devshop_projects_form(&$node) { foreach ($settings as $setting_id => $setting){ $form['project']['environments'][$environment_name][$setting_id] = $setting; - $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $node->project->environments[$environment_name]['settings'][$setting_id]; + $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $node->project->environments[$environment_name][$setting_id]; $form['project']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } - $site = array_pop($sites); + $site = node_load($environment['site']); $live_alias_options[$environment_name] = $site->hosting_name; - } - } $form['#submit'] = array('devshop_projects_submit_settings'); @@ -262,17 +256,17 @@ function devshop_projects_form(&$node) { */ function devshop_projects_submit_settings($form, &$form_state) { - // Go through and save our settings + // Go through and save our settings to site and platform nodes. $project_node = node_load($form_state['values']['nid']); $settings_info = module_invoke_all('devshop_project_settings', $project_node); $nodes = array(); - + // Go through each environment foreach ($form_state['values']['project']['environments'] as $environment_name => $settings){ - + // Then through each setting of each environment foreach ($settings as $setting_name => $setting_value){ - + //Find the node type for this setting. $node_type = $settings_info[$setting_name]['#node_type']; @@ -294,7 +288,7 @@ function devshop_projects_submit_settings($form, &$form_state) { } } } - + // Go save all nodes dsm($nodes, 'nodes to save'); foreach ($nodes as $nid => $node){ From f2601044e3b61138c17506e313bd64e13a9b7714 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 18 Mar 2014 17:38:27 -0400 Subject: [PATCH 0700/3476] adding environments schema --- devshop_projects/devshop_projects.install | 60 ++++++++++++++++++++++- devshop_projects/inc/nodes.inc | 10 ++-- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 6ad028dcd..301e0db13 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -46,13 +46,14 @@ function devshop_projects_schema() { 'type' => 'text', 'not null' => FALSE, 'size' => 'big', - 'description' => 'A serialized array of name value pairs for this project.', + 'description' => 'A serialized array of data for this project.', ), ), 'primary key' => array('nid'), ); - $schema['hosting_devshop_project_object'] = array( + // @TODO: Delete this schema once update scripts work. + $schema['hosting_devshop_project_object'] = array( 'fields' => array( 'project_nid' => array( 'type' => 'int', @@ -102,6 +103,52 @@ function devshop_projects_schema() { 'primary key' => array('object_nid'), ); + // Table for tracking environments + $schema['hosting_devshop_project_environment'] = array( + 'fields' => array( + 'project_nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => "The project's Node ID.", + ), + 'environment' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => 64, + 'default' => '', + 'description' => 'Environment name.', + ), + 'git_branch' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'The current branch of this site or platform.', + ), + 'site_nid' => array( + 'type' => 'int', + 'not null' => FALSE, + 'default' => 0, + 'unsigned' => TRUE, + ), + 'platform_nid' => array( + 'type' => 'int', + 'not null' => FALSE, + 'default' => 0, + 'unsigned' => TRUE, + ), + 'data' => array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + 'description' => 'A serialized array of data for this project.', + ), + ), + 'indexes' => array( + 'project_environment' => array('project_nid', 'environment'), + ), + ); return $schema; } @@ -228,4 +275,13 @@ function devshop_projects_update_10() { $ret[] = db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); } return $ret; +} + + +/** + * Create new hosting_devshop_environment table. + */ +function devshop_projects_update_11() { + // Create tables. + drupal_install_schema('devshop_projects'); } \ No newline at end of file diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index cdb41b00b..b4d027d31 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -186,9 +186,11 @@ function devshop_projects_update($node) { // Everything from $form['project'] gets serialized and saved. // Save our environments data here as well. -// dsm($node->project); - $info->data = serialize($node->project); + dsm($node->project, 'node project object update.'); + + $info->data = serialize($node->project); + dsm($info->data, 'serialized'); drupal_write_record('hosting_devshop_project', $info, 'nid'); @@ -271,14 +273,14 @@ function devshop_projects_load($node) { $environments = array(); while ($obj = db_fetch_object($query)) { $object_node = node_load($obj->object_nid); - + if (($object_node->type == 'site' && $object_node->site_status != HOSTING_SITE_DELETED) || ($object_node->type == 'platform' && $object_node->platform_status != HOSTING_PLATFORM_DELETED) ){ $environments[$obj->environment][$obj->object_type] = $obj->object_nid; } } - $project->environments = $environments; + $project->environment_nodes = $environments; // Save project object be available at $node->project. $additions['project'] = $project; From 4f4f44530cc99db32261d3fdf4cfbe516527d218 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 21:26:48 +0000 Subject: [PATCH 0701/3476] fixing devshop pull to work with new project settings array. --- devshop_pull/devshop_pull.inc | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index f650a8d7b..16c094159 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -29,13 +29,9 @@ function devshop_pull_callback($project, $hash) { $status = DEVSHOP_PULL_STATUS_INVALID_CODE; } // Make sure the project has pull callback enabled - elseif ($project_node->pull_method != DEVSHOP_PULL_CALLBACK){ + elseif (!$project_node->project_settings['pull']['pull_enabled']){ $message = "Project $project is NOT configured to use Pull Code URL callback!"; } - // Make sure the project has platforms with pull enabled. - elseif (empty($environments_to_pull)){ - $message = "Project $project has no environments configured to Pull Code on commit!"; - } // Make sure the client's IP address is on the list else if (!devshop_pull_ip_match(ip_address(), $allowed_ips)) { $message = ip_address() . " is not authorized to invoke a Pull Code request."; @@ -55,7 +51,6 @@ function devshop_pull_callback($project, $hash) { else { $message = devshop_pull_default_webhook($project_node, $environments_to_pull); } - } // Log It, only if there is a status @@ -139,32 +134,27 @@ function devshop_pull_github_webhook($project){ // @TODO: Add to project settings. $branch = $data->pull_request->head->ref; - $message = "Detected Pull Request for $branch"; - if ($project->pull_request_environments){ - // Lookup environments by branch - foreach ($project->settings as $env => $settings){ - if ($settings['git_branch'] == $branch){ - $pull_environment_found = $branch; - } - } - if (empty($pull_environment_found)){ - $environment_name = strtr($branch, array('-' => '_', '/' => '_')); - hosting_create_environment($project, $environment_name, $branch); + $message = "Detected Pull Request for $branch \n"; + if ($project->project_settings['pull']['pull_request_environments']){ + $environment_name = "pr" . $data->pull_request->number; + + if (isset($project->environments[$environment_name])){ + $message = "Environment $environment_name already exists."; } else { - print "Branch already has an environment."; + hosting_create_environment($project, $environment_name, $branch); + $message .= "Environment $environment_name created for $project->title \n"; } } } break; } - return $message; - } else { - watchdog('devshop_pull', 'GitHub Request Received, but not in JSON. Please make sure to configure the webhook to use Payload version: application/vnd.github.v3+json'); + $message = 'GitHub Request Received, but not in JSON. Please make sure to configure the webhook to use Payload version: application/vnd.github.v3+json'; } + return $message; } /** From 6b8f7da216da0dda94dd686b3f6033da4e4a83c3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 21:35:30 +0000 Subject: [PATCH 0702/3476] update display for git pull notification --- devshop_pull/devshop_pull.module | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index e5e5b865f..dfd0f38bc 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -168,12 +168,13 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'project'){ // View Project - if ($op == 'view' && $node->project_settings['pull']['pull_method']== DEVSHOP_PULL_CALLBACK){ + if ($op == 'view' && $node->project_settings['pull']['pull_enabled']){ module_load_include('inc', 'devshop_pull'); $url = _devshop_pull_callback_url($node); $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); $status = (int) $node->project_settings['pull']['last_pull_status']; + drupal_set_message(print_r($node->project_settings, 1)); // If access denied, provide link to settings page if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ From d8da5a3f91384d42138283127b108d7a7e1e7f6d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 17:40:15 -0400 Subject: [PATCH 0703/3476] Saving last pull info. --- devshop_pull/devshop_pull.inc | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 16c094159..954b258e4 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -39,10 +39,7 @@ function devshop_pull_callback($project, $hash) { } // All checks pass! Server is allowed to trigger tasks! else { - $status = DEVSHOP_PULL_STATUS_OK; - // @TODO: Make this pluggable. - // Check headers for GitHub Integration $headers = getallheaders(); if (isset($headers['X-GitHub-Event'])) { @@ -51,28 +48,18 @@ function devshop_pull_callback($project, $hash) { else { $message = devshop_pull_default_webhook($project_node, $environments_to_pull); } - } - - // Log It, only if there is a status - if (isset($status)){ - $record = new stdClass; - $record->project_nid = $project_node->nid; - $record->pull_method = $project_node->pull_method; - $record->last_pull = time(); - $record->last_pull_status = $status; - $record->last_pull_ip = ip_address(); - - drupal_write_record('hosting_devshop_pull_projects', $record, array('project_nid')); } + // Save the project node with last pull info. + $project_node->project_settings['pull']['last_pull'] = time(); + $project_node->project_settings['pull']['last_pull_status'] = DEVSHOP_PULL_STATUS_OK; + $project_node->project_settings['pull']['last_pull_ip'] = ip_address(); + node_save($project_node); + // Output a message, no matter what. watchdog('devshop_pull', $message, array(), WATCHDOG_INFO); - // @TODO: Create a "debug" mode to prevent logging of all payloads - watchdog('devshop_pull_debug', 'PAYLOAD: '. $GLOBALS['HTTP_RAW_POST_DATA'], array(), WATCHDOG_INFO); - watchdog('devshop_pull_debug', 'HEADERS: '. print_r(getallheaders(), 1), WATCHDOG_INFO); - - + // @TODO Print an appropriate header. print $message; // Save a variable to help when using the settings page. From 969aa1d67d3471870816f769e68eb9f3ee861ef3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 17:40:59 -0400 Subject: [PATCH 0704/3476] fixing bad environment for default pull from non-github source. --- devshop_pull/devshop_pull.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 954b258e4..2c744e36e 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -79,7 +79,7 @@ function devshop_pull_default_webhook($project, $environments_to_pull){ // Always clear cache... // @TODO: Should we make this a setting? "What to do on auto-pull?" $args['cache'] = 1; - hosting_add_task($project_node->nid, 'devshop-pull', $args); + hosting_add_task($project->nid, 'devshop-pull', $args); return "Commit notification received! Running 'Pull Code' on $project->title environments " . $args['environments']; } From 8d17f8bcfe50b6922cd8d7eeb3e973f7be18c268 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 17:41:40 -0400 Subject: [PATCH 0705/3476] adding TODO to handle form content as well as json. --- devshop_pull/devshop_pull.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 2c744e36e..08cd5b0ad 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -91,6 +91,7 @@ function devshop_pull_default_webhook($project, $environments_to_pull){ function devshop_pull_github_webhook($project){ $headers = getallheaders(); + // @TODO: Handle form content as well. if ($headers['content-type'] == 'application/json'){ $data = json_decode($GLOBALS['HTTP_RAW_POST_DATA']); From ead830e242dfc02890078bb4d0ec9885bebc697f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 17:43:37 -0400 Subject: [PATCH 0706/3476] removing print r messages. --- devshop_pull/devshop_pull.module | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index dfd0f38bc..08dea7af1 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -171,10 +171,9 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if ($op == 'view' && $node->project_settings['pull']['pull_enabled']){ module_load_include('inc', 'devshop_pull'); $url = _devshop_pull_callback_url($node); - $pull_method .= '
' . l(_filter_url_trim($url, 30), $url); + $output = ''; $status = (int) $node->project_settings['pull']['last_pull_status']; - drupal_set_message(print_r($node->project_settings, 1)); // If access denied, provide link to settings page if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ From 37957287c4d14f0c7dc4c56c58341d16dde6429a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 17:50:44 -0400 Subject: [PATCH 0707/3476] Adding "pull disabled" setting. it's more common to turn it off :) --- devshop_pull/devshop_pull.inc | 2 +- devshop_pull/devshop_pull.module | 33 +++++++++++++------------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 08cd5b0ad..694e37fef 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -107,7 +107,7 @@ function devshop_pull_github_webhook($project){ // Check for environments set to pull $environments_to_pull = array(); foreach ($project->settings as $env => $settings) { - if ($settings['pull_enabled']) { + if (!$settings['pull_disabled']) { $environments_to_pull[] = $env; } } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 08dea7af1..b4a694c0a 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -74,26 +74,19 @@ function devshop_pull_hosting_queues() { return $items; } -///** -// * Implements hook_devshop_project_settings() -// */ -//function devshop_pull_devshop_project_settings(){ -// return array( -// 'pull_enabled' => array( -// '#title' => t('Pull on Commit'), -// '#node_type' => 'platform', -// '#type' => 'checkbox', -// '#description' => t('When DevShop receives commit notification, Pull Code.'), -// ), -// // Pull reset won't work until we are storing the settings in the project drush context. -// //'pull_reset' => array( -// // '#title' => t('Reset files'), -// // '#node_type' => 'platform', -// // '#type' => 'checkbox', -// // '#description' => t('When runing a Pull Code task triggered by a git commit, run git reset --hard just before.'), -// //), -// ); -//} +/** + * Implements hook_devshop_project_settings() + */ +function devshop_pull_devshop_project_settings(){ + return array( + 'pull_disabled' => array( + '#title' => t('Disable Pull'), + '#node_type' => 'platform', + '#type' => 'checkbox', + '#description' => t('Disable pulling code on this environment, allowing "live development".'), + ), + ); +} /** * Implements hook_form_alter(). From 86815d6ff1b4fca578b99e6bd9a9c74ed96092fa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 17:52:48 -0400 Subject: [PATCH 0708/3476] Gah! array combine! --- devshop_projects/devshop_projects.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 5fce6e00f..b0e73db24 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -141,8 +141,8 @@ function devshop_projects_menu() { function devshop_projects_devshop_project_settings($project_node = NULL){ if (!empty($project_node->git_branches)){ $branch_options = array( - 'Branches' => $project_node->git_branches, - 'Tags' => $project_node->git_tags, + 'Branches' => array_combine($project_node->git_branches, $project_node->git_branches), + 'Tags' => array_combine($project_node->git_tags, $project_node->git_tags), ); } else { $branch_options = array(); From 354299f1c50bda3ae266fc22b40c959e9a458e4c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 20 Mar 2014 18:26:51 -0400 Subject: [PATCH 0709/3476] adding TODO about synchronize action --- devshop_pull/devshop_pull.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 694e37fef..470e8b15a 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -134,6 +134,7 @@ function devshop_pull_github_webhook($project){ $message .= "Environment $environment_name created for $project->title \n"; } } + // @TODO: synchronize action } break; } From e2f5ff4a672a6df28061c941595f45572a8dda9e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 08:59:19 -0400 Subject: [PATCH 0710/3476] removing devshop live from hook_hosting_features --- hosting.feature.devshop.inc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hosting.feature.devshop.inc b/hosting.feature.devshop.inc index 55006cd07..ac83a3f29 100644 --- a/hosting.feature.devshop.inc +++ b/hosting.feature.devshop.inc @@ -40,12 +40,5 @@ function devshop_hosting_hosting_feature() { 'module' => 'devshop_log', 'group' => 'devshop', ); - $features['devshop_live'] = array( - 'title' => t('DevShop Live'), - 'description' => t('Provides tools for going live.'), - 'status' => HOSTING_FEATURE_DISABLED, - 'module' => 'devshop_live', - 'group' => 'devshop', - ); return $features; } From 380067580d09289d0cf44a8e92c9821e12403912 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:02:47 -0400 Subject: [PATCH 0711/3476] Save environments info in project_settings. --- devshop_projects/inc/forms.inc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 9aa339d9e..a152a8eb9 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -138,8 +138,13 @@ function devshop_projects_form(&$node) { '#weight' => 4, ); + // Project Settings + $form['project_settings'] = array( + '#tree' => TRUE, + ); + // Extensible settings - $form['environment_settings'] = array( + $form['project_settings']['environments'] = array( '#type' => 'fieldset', '#title' => t('Environment Settings'), '#theme' => 'devshop_projects_settings_form', @@ -152,7 +157,7 @@ function devshop_projects_form(&$node) { $platform = node_load($nid); $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { - $form['environment_settings'][$environment_name] = array( + $form['project_settings']['environments'][$environment_name] = array( '#tree' => TRUE, '#title' => $name, '#type' => 'fieldset', @@ -160,9 +165,9 @@ function devshop_projects_form(&$node) { ); foreach ($settings as $setting_id => $setting){ - $form['environment_settings'][$environment_name][$setting_id] = $setting; - $form['environment_settings'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; - $form['environment_settings'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project_settings']['environments'][$environment_name][$setting_id] = $setting; + $form['project_settings']['environments'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; + $form['project_settings']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } $site = array_pop($sites); $live_alias_options[$environment_name] = $site->hosting_name; @@ -171,10 +176,6 @@ function devshop_projects_form(&$node) { } $form['#submit'] = array('devshop_projects_submit_settings'); - // Project Settings - $form['project_settings'] = array( - '#tree' => TRUE, - ); // Save git branches and tags $form['project_settings']['git_branches'] = array( From e57714ad75e3ca56459db0e8196132006f83a3a2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:06:03 -0400 Subject: [PATCH 0712/3476] Determine environments to pull from project_settings and whether it is tracking a tag. --- devshop_pull/devshop_pull.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 470e8b15a..0644287e1 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -106,8 +106,9 @@ function devshop_pull_github_webhook($project){ // Add a task to pull then project's environments // Check for environments set to pull $environments_to_pull = array(); - foreach ($project->settings as $env => $settings) { - if (!$settings['pull_disabled']) { + foreach ($project->project_settings['environments'] as $env => $settings) { + // Only pull if pull disabled or is tracking a tag. + if (!$settings['pull_disabled'] && !in_array($settings['git_branch'], $project->project_settings['git_tags'])) { $environments_to_pull[] = $env; } } From 47b6197bab5d20ebb9181d68707c7596909183a3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:08:01 -0400 Subject: [PATCH 0713/3476] adding @TODO: Remove any environments where pull is disabled or is tracking a tag. --- devshop_projects/inc/tasks.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index e0c1cb159..8e8fac3f6 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -281,6 +281,7 @@ function hosting_task_devshop_pull_form($node) { $form = array(); devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environments to pull code to.'), 'environments', 'Environments', 'checkboxes'); + // @TODO: Remove any environments where pull is disabled or is tracking a tag. /* *$form['environments'] = array( '#title' => t('list of environments'), From 19ac3afc7e9859fbed6dfa25603b2786f0e2151c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:12:01 -0400 Subject: [PATCH 0714/3476] Removing environments where pull is disabled or is tracking tag. --- devshop_projects/inc/tasks.inc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 8e8fac3f6..5614a45dc 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -279,17 +279,14 @@ function hosting_task_devshop_commit_form($node) { function hosting_task_devshop_pull_form($node) { $form = array(); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environments to pull code to.'), 'environments', 'Environments', 'checkboxes'); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environments to pull code to. NOTE: Environments that are tracking a tag or have pull disabled are not listed.'), 'environments', 'Environments', 'checkboxes'); - // @TODO: Remove any environments where pull is disabled or is tracking a tag. - /* - *$form['environments'] = array( - '#title' => t('list of environments'), - '#type' => 'textfield', - '#default_value' => 'dev test', - ); - - */ + // Remove any environments where pull is disabled or is tracking a tag. + foreach ($node->project_settings['environments'] as $env => $settings){ + if (isset($form['environments']['#options'][$env]) && ($settings['pull_disabled'] || in_array($settings['git_branch'], $node->project_settings['git_tags']))){ + unset($form['environments']['#options'][$env]); + } + } $form['update'] = array( '#title' => t('Run update.php after code pull?'), From cf2659980c09f1c05a8766ce37bd055f4d04c715 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:15:51 -0400 Subject: [PATCH 0715/3476] redirect if there are no environments configured to pull. --- devshop_projects/inc/tasks.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 5614a45dc..53296a5d2 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -288,6 +288,12 @@ function hosting_task_devshop_pull_form($node) { } } + // If empty, send them to project page. + if (empty($form['environments']['#options'][$env])){ + drupal_set_message(t('You do not have any environments with Pull Code enabled, or they are all tracking tags. !link to be able to pull code.', array('!link' => l(t('Edit the project settings'), "node/$node->nid/edit")))); + drupal_goto("node/$node->nid"); + } + $form['update'] = array( '#title' => t('Run update.php after code pull?'), '#type' => 'checkbox', From e9b2f5d528403a6086b5a77c09bb79cc29adcaa3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:16:37 -0400 Subject: [PATCH 0716/3476] no goto, just retunr. --- devshop_projects/inc/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 53296a5d2..3e73e79c1 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -291,7 +291,7 @@ function hosting_task_devshop_pull_form($node) { // If empty, send them to project page. if (empty($form['environments']['#options'][$env])){ drupal_set_message(t('You do not have any environments with Pull Code enabled, or they are all tracking tags. !link to be able to pull code.', array('!link' => l(t('Edit the project settings'), "node/$node->nid/edit")))); - drupal_goto("node/$node->nid"); + return; } $form['update'] = array( From ba75aab3afe1dad01b609b622183bedb145af742 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:17:23 -0400 Subject: [PATCH 0717/3476] gotta return something apparently. --- devshop_projects/inc/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 3e73e79c1..77b41f13e 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -291,7 +291,7 @@ function hosting_task_devshop_pull_form($node) { // If empty, send them to project page. if (empty($form['environments']['#options'][$env])){ drupal_set_message(t('You do not have any environments with Pull Code enabled, or they are all tracking tags. !link to be able to pull code.', array('!link' => l(t('Edit the project settings'), "node/$node->nid/edit")))); - return; + return $form; } $form['update'] = array( From b2a9f8e7c9b3bca6e379830030214a79d42d382a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:17:38 -0400 Subject: [PATCH 0718/3476] // @TODO: Close modal window instead? --- devshop_projects/inc/tasks.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 77b41f13e..29ae65be8 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -291,6 +291,7 @@ function hosting_task_devshop_pull_form($node) { // If empty, send them to project page. if (empty($form['environments']['#options'][$env])){ drupal_set_message(t('You do not have any environments with Pull Code enabled, or they are all tracking tags. !link to be able to pull code.', array('!link' => l(t('Edit the project settings'), "node/$node->nid/edit")))); + // @TODO: Close modal window instead? return $form; } From 057ae2d793de5d31a8482815706e1847d7abff7b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:22:45 -0400 Subject: [PATCH 0719/3476] quick re-refactor of environments form. --- devshop_projects/inc/forms.inc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index a152a8eb9..37e130062 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -153,9 +153,14 @@ function devshop_projects_form(&$node) { $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); - foreach ($node->project_objects['platform'] as $nid => $environment_name) { - $platform = node_load($nid); - $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); + + +// foreach ($node->project_objects['platform'] as $nid => $environment_name) { + foreach ($node->project_settings['environments'] as $environment_name => $environment_settings) { +// $platform = node_load($nid); +// $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); + + $platform = node_load($settings['platform_nid']); if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { $form['project_settings']['environments'][$environment_name] = array( '#tree' => TRUE, @@ -163,16 +168,14 @@ function devshop_projects_form(&$node) { '#type' => 'fieldset', '#theme' => 'devshop_projects_settings_table', ); - foreach ($settings as $setting_id => $setting){ $form['project_settings']['environments'][$environment_name][$setting_id] = $setting; - $form['project_settings']['environments'][$environment_name][$setting_id]['#default_value'] = $node->settings[$environment_name][$setting_id]; + $form['project_settings']['environments'][$environment_name][$setting_id]['#default_value'] = $environment_settings[$setting_id]; $form['project_settings']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } - $site = array_pop($sites); + $site = node_load($settings['site_nid']); $live_alias_options[$environment_name] = $site->hosting_name; } - } $form['#submit'] = array('devshop_projects_submit_settings'); From 52fe80cc2f4ed03e7ac9ae126a521c517734ba7a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 13:25:54 +0000 Subject: [PATCH 0720/3476] fixing project settings form refactor. --- devshop_projects/inc/forms.inc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 37e130062..80c040bf7 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -143,7 +143,7 @@ function devshop_projects_form(&$node) { '#tree' => TRUE, ); - // Extensible settings + // Extensible Environment settings $form['project_settings']['environments'] = array( '#type' => 'fieldset', '#title' => t('Environment Settings'), @@ -154,14 +154,11 @@ function devshop_projects_form(&$node) { $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); - -// foreach ($node->project_objects['platform'] as $nid => $environment_name) { foreach ($node->project_settings['environments'] as $environment_name => $environment_settings) { -// $platform = node_load($nid); -// $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); - $platform = node_load($settings['platform_nid']); - if ($platform->status == HOSTING_PLATFORM_ENABLED && !empty($sites)) { + $platform = node_load($environment_settings['platform_nid']); + $site = node_load($environment_settings['site_nid']); + if ($platform->status == HOSTING_PLATFORM_ENABLED && $site->status == HOSTING_PLATFORM_ENABLED) { $form['project_settings']['environments'][$environment_name] = array( '#tree' => TRUE, '#title' => $name, @@ -173,7 +170,6 @@ function devshop_projects_form(&$node) { $form['project_settings']['environments'][$environment_name][$setting_id]['#default_value'] = $environment_settings[$setting_id]; $form['project_settings']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } - $site = node_load($settings['site_nid']); $live_alias_options[$environment_name] = $site->hosting_name; } } From b20c585177b2c96f219c2b034f1dd1f9a3b918c2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:28:56 -0400 Subject: [PATCH 0721/3476] If something went wrong connecting to the git repo, don't wipe out our branches. --- devshop_projects/devshop_projects.drush.inc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index bee374d58..0edc9f5be 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -307,11 +307,14 @@ function devshop_projects_hosting_project_context_options(&$task) { $branches = getBranchesAndTags($task->ref->git_url); - $task->ref->project_settings['git_branches'] = $branches['branches']; - $task->ref->project_settings['git_tags'] = $branches['tags']; + // If something went wrong connecting to the git repo, don't wipe out our branches. + if (!empty($branches['branches'])){ + $task->ref->project_settings['git_branches'] = $branches['branches']; + $task->ref->project_settings['git_tags'] = $branches['tags']; - // Save the project node now that we have branches and tags. - node_save($task->ref); + // Save the project node now that we have branches and tags. + node_save($task->ref); + } $task->context_options['git_branches'] = $branches['branches']; $task->context_options['git_tags'] = $branches['tags']; From 1b4a841a88e8b8476c5f466449141326b1f87d14 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 09:30:14 -0400 Subject: [PATCH 0722/3476] Don't hide the refresh link if verify task failed. --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 0477211dd..694f66e0f 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -148,7 +148,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $verify_task = hosting_get_most_recent_task($node->nid, 'verify'); - if ($verify_task->task_status == HOSTING_TASK_SUCCESS){ + if ($verify_task->task_status == HOSTING_TASK_SUCCESS || $verify_task->task_status == HOSTING_TASK_ERROR){ $refresh_link = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); } else { $refresh_link = t('Refresh in progress...'); From ba1a9890f97394f18f324613f2ce324b001f951f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 13:31:39 +0000 Subject: [PATCH 0723/3476] removing branch selector for now --- devshop_projects/inc/ui.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 0477211dd..ee0a6acad 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -193,8 +193,9 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } else { $row[] = devshop_hosting_site_goto_link($site); } -// $row[] = "$site->git_branch"; - + $row[] = "$site->git_branch"; + /* + @TODO: One step at a time ... // Create branch/tag chooser $actions = array(); foreach ($node->git_branches as $branch){ @@ -210,7 +211,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } $row[] = theme('ctools_dropdown', $site->git_branch, $actions); - + */ if (module_exists('devshop_log')) { $row[] =l(t('Commits'), "node/$site->nid/logs/commits"); From eccf53c173a57e875159029dad0aed2c15b315fb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 13:56:47 +0000 Subject: [PATCH 0724/3476] improving the projects page: environments show up as soon as they are created --- devshop_projects/inc/nodes.inc | 8 ++++---- devshop_projects/inc/ui.inc | 10 +++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 7d5ccf372..e4f01498e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -270,12 +270,12 @@ function devshop_projects_load($node) { // Only load if site or platform is enabled. $object_node = node_load($project_object->object_nid); - if (($object_node->type == 'site' && $object_node->site_status == HOSTING_SITE_ENABLED) || - ($object_node->type == 'platform' && $object_node->platform_status == HOSTING_PLATFORM_ENABLED) - ){ + // if (($object_node->type == 'site' && $object_node->site_status == HOSTING_SITE_ENABLED) || + // ($object_node->type == 'platform' && $object_node->platform_status == HOSTING_PLATFORM_ENABLED) + // ){ $objects[$project_object->object_type][$project_object->object_nid] = $project_object->environment; - } + // } } $additions['project_objects'] = $objects; diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index cc0c0e186..6b3340061 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -181,19 +181,23 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $platform = node_load($platform_nid); // Skip this if it is not enabled. - if ($site->site_status != HOSTING_SITE_ENABLED && $platform->platform_status != HOSTING_PLATFORM_ENABLED) { + if ($site->site_status == HOSTING_SITE_DELETED && $platform->platform_status == HOSTING_PLATFORM_DELETED) { continue; } $row = array(); - $row[] = "$site->environment"; - + $row[] = "$env"; + + if ($site->site_status == HOSTING_SITE_DISABLED){ $row[] = devshop_hosting_site_goto_link($site) . '' . t('Disabled') . ''; + } elseif ($site->site_status == HOSTING_SITE_QUEUED){ + $row[] = '' . t('Installing...') . ''; } else { $row[] = devshop_hosting_site_goto_link($site); } $row[] = "$site->git_branch"; + /* @TODO: One step at a time ... // Create branch/tag chooser From a04bcddf12e3b3878c1d7bba069cac53238be8d6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 14:03:04 +0000 Subject: [PATCH 0725/3476] using loaded environments instead of raw json for project settings page. --- devshop_projects/inc/forms.inc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 80c040bf7..e67f84c58 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -153,11 +153,12 @@ function devshop_projects_form(&$node) { $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); + // @TODO: Whenever an environment is created, make sure to resave the project so settings are captured in json. + foreach ($node->environments as $environment_name => $env) { - foreach ($node->project_settings['environments'] as $environment_name => $environment_settings) { - - $platform = node_load($environment_settings['platform_nid']); - $site = node_load($environment_settings['site_nid']); + $environment_settings = $node->project_settings['environments'][$environment_name]; + $platform = node_load($env['platform']); + $site = node_load($env['site']); if ($platform->status == HOSTING_PLATFORM_ENABLED && $site->status == HOSTING_PLATFORM_ENABLED) { $form['project_settings']['environments'][$environment_name] = array( '#tree' => TRUE, From c7dc529734e085f6095fbd7ea36d04c0e2d7d0b3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 14:12:41 +0000 Subject: [PATCH 0726/3476] handle pull request deletion --- devshop_pull/devshop_pull.inc | 13 ++++++++++--- devshop_pull/devshop_pull.module | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 0644287e1..2294e17d7 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -118,17 +118,17 @@ function devshop_pull_github_webhook($project){ break; case 'pull_request': $message = 'Pull Request Received.'; + $branch = $data->pull_request->head->ref; + $environment_name = "pr" . $data->pull_request->number; if ($data->action == 'opened'){ // @TODO: Add to project settings. - $branch = $data->pull_request->head->ref; $message = "Detected Pull Request for $branch \n"; if ($project->project_settings['pull']['pull_request_environments']){ - $environment_name = "pr" . $data->pull_request->number; if (isset($project->environments[$environment_name])){ - $message = "Environment $environment_name already exists."; + $message .= "Environment $environment_name already exists."; } else { hosting_create_environment($project, $environment_name, $branch); @@ -137,6 +137,13 @@ function devshop_pull_github_webhook($project){ } // @TODO: synchronize action } + elseif ($data->action == 'closed'){ + $message .= "Pull Request Closed \n"; + if ($project->project_settings['pull']['pull_request_environments_delete']){ + hosting_add_task($project->environments[$environment_name]['site'], 'delete'); + $message .= "Site $environment_name scheduled for deletion."; + } + } break; } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index b4a694c0a..8aca7743d 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -147,6 +147,13 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#default_value' => $node->project_settings['pull']['pull_request_environments'], '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), ); + $form['project_settings']['pull']['pull_request_environments_delete'] = array( + '#type' => 'checkbox', + '#title' => t('Delete Pull Request Environments'), + '#default_value' => $node->project_settings['pull']['pull_request_environments_delete'], + '#description' => t('When Pull Requests are closed, delete the environment.'), + ); + } } From 7be135709662b94b971f41e86f4d734635007c19 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 10:13:06 -0400 Subject: [PATCH 0727/3476] removing verification of all the things. --- devshop_projects/devshop_projects.drush.inc | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 0edc9f5be..1f6512062 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -272,21 +272,21 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } // When a project is verified, queue a verification for all platforms - if ($task->ref->type == 'project' && isset($task->ref->project_objects['platform'])){ - $project = node_load($task->ref->nid); - $platform_nids = array_keys($project->project_objects['platform']); - foreach ($project->project_objects['platform'] as $nid => $name) { - //Check if a platform was deleted for exclude from verify. - $status = db_result(db_query("SELECT status FROM {hosting_platform} WHERE nid = %d", $nid)); - if ($status != HOSTING_PLATFORM_DELETED) { - drush_log('[DEVSHOP] Running verify on project: ' . $name, 'ok'); - hosting_add_task($nid, 'verify'); - } - else { - drush_log('[DEVSHOP] Can\'t run verify on project: ' . $name, 'ok'); - } - } - } +// if ($task->ref->type == 'project' && isset($task->ref->project_objects['platform'])){ +// $project = node_load($task->ref->nid); +// $platform_nids = array_keys($project->project_objects['platform']); +// foreach ($project->project_objects['platform'] as $nid => $name) { +// //Check if a platform was deleted for exclude from verify. +// $status = db_result(db_query("SELECT status FROM {hosting_platform} WHERE nid = %d", $nid)); +// if ($status != HOSTING_PLATFORM_DELETED) { +// drush_log('[DEVSHOP] Running verify on project: ' . $name, 'ok'); +// hosting_add_task($nid, 'verify'); +// } +// else { +// drush_log('[DEVSHOP] Can\'t run verify on project: ' . $name, 'ok'); +// } +// } +// } } /** From b6904b8b9dd7f2c2769a1f1c391356489f7f4d25 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 21 Mar 2014 10:13:31 -0400 Subject: [PATCH 0728/3476] Adding / @TODO: Figure out a better way to verify all the things. --- devshop_projects/devshop_projects.drush.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 1f6512062..e1b71433d 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -271,7 +271,8 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } } - // When a project is verified, queue a verification for all platforms + // When a project is verified, queue a verification for all platforms + // @TODO: Figure out a better way to verify all the things. // if ($task->ref->type == 'project' && isset($task->ref->project_objects['platform'])){ // $project = node_load($task->ref->nid); // $platform_nids = array_keys($project->project_objects['platform']); From 44b1050eb35cd151a26df86e374a11efcda4fa0d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 22 Mar 2014 23:08:15 -0400 Subject: [PATCH 0729/3476] handle repos with no tags. --- devshop_projects/devshop_projects.module | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index b0e73db24..4a75f2c00 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -142,10 +142,12 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ if (!empty($project_node->git_branches)){ $branch_options = array( 'Branches' => array_combine($project_node->git_branches, $project_node->git_branches), - 'Tags' => array_combine($project_node->git_tags, $project_node->git_tags), ); + if (!empty($project_node->git_tags)){ + $branch_options['Tags'] = array_combine($project_node->git_tags, $project_node->git_tags); + } } else { - $branch_options = array(); + $branch_options = array('No branches found. Something went wrong.'); } $settings = array(); From 739a3483b519e6d156a97e359e81f7b397542817 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 22 Mar 2014 23:46:51 -0400 Subject: [PATCH 0730/3476] clean and remove --- devshop_projects/inc/nodes.inc | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index b4d027d31..ce798d794 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -239,6 +239,9 @@ function devshop_project_save_domain_aliases($project_node) { function devshop_projects_delete($node) { db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); + db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); + + // @TODO: Remove once table is deleted. db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); hosting_context_delete($node->nid); @@ -247,40 +250,36 @@ function devshop_projects_delete($node) { /** * Implementation of hook_load(). * + * Loads data for project and environments. + * * @param node * Node object */ function devshop_projects_load($node) { - // Load everything straight from the database table. - $project_data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); - $additions = $project_data; - // Load the "hosting context". The unique name in the aegir system. $additions['hosting_name'] = 'project_' . db_result(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); + // Load project data + $project_data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); + // Load up all project settings, merging db table and serialized data. $data = $project_data['data']; unset($project_data['data']); $project = (object) array_merge((array) $project_data, (array) unserialize($data)); + // @TODO: Create a good status system. // Set status - $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); - - // Environments Array - $query = db_query("SELECT * FROM {hosting_devshop_project_object} WHERE project_nid = %d ORDER BY environment", $node->nid); + // $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); + // Load Environments Data + $query = db_query("SELECT * FROM {hosting_devshop_project_environment} WHERE project_nid = %d ORDER BY environment", $node->nid); $environments = array(); - while ($obj = db_fetch_object($query)) { - $object_node = node_load($obj->object_nid); - - if (($object_node->type == 'site' && $object_node->site_status != HOSTING_SITE_DELETED) || - ($object_node->type == 'platform' && $object_node->platform_status != HOSTING_PLATFORM_DELETED) - ){ - $environments[$obj->environment][$obj->object_type] = $obj->object_nid; - } + while ($environment_data = db_fetch_array($query)) { + $environment_data['data'] = unserialize($environment_data['data']); + $environments[$environment_data['environment']] = $environment_data; } - $project->environment_nodes = $environments; + $project->environments = $environments; // Save project object be available at $node->project. $additions['project'] = $project; From 8be1b5f616a2048bf3ad59baa0dd2cb8dc600e54 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 22 Mar 2014 23:49:05 -0400 Subject: [PATCH 0731/3476] removing dsms --- devshop_projects/inc/nodes.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index ce798d794..4c8d6000a 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -186,11 +186,8 @@ function devshop_projects_update($node) { // Everything from $form['project'] gets serialized and saved. // Save our environments data here as well. - dsm($node->project, 'node project object update.'); - $info->data = serialize($node->project); - dsm($info->data, 'serialized'); drupal_write_record('hosting_devshop_project', $info, 'nid'); From d10bb70553a8c0737363b3e065ef0532c69aec5b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Mar 2014 00:18:16 -0400 Subject: [PATCH 0732/3476] Saving environment records --- devshop_projects/devshop_projects.install | 4 +-- devshop_projects/inc/nodes.inc | 34 +++++++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 301e0db13..544623113 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -120,11 +120,11 @@ function devshop_projects_schema() { 'default' => '', 'description' => 'Environment name.', ), - 'git_branch' => array( + 'git_ref' => array( 'type' => 'varchar', 'length' => 128, 'not null' => FALSE, - 'description' => 'The current branch of this site or platform.', + 'description' => 'The current branch or tag of this site or platform.', ), 'site_nid' => array( 'type' => 'int', diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 4c8d6000a..cdac0bfcd 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -107,7 +107,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); while ($site = db_fetch_object($result)) { db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); - } + } } // If we are updating or inserting sites, check for live domain aliases. @@ -156,12 +156,25 @@ function devshop_projects_insert($node) { drupal_write_record('hosting_devshop_project', $info); // Save hosting context - if ((!$node->old_vid)) { + if (!$node->old_vid) { hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); } // If using environment aliases... save them. devshop_project_save_domain_aliases($node); + + // Save Environment records. + foreach ($node->project->environments as $env => $environment){ + $info = new stdClass(); + $info->project_nid = $node->nid; + $info->environment = $env; + $info->git_ref = $environment['git_ref']; + $info->site_nid = $environment['site_nid']; + $info->platform_nid = $environment['platform_nid']; + $info->data = serialize($environment); + + drupal_write_record('hosting_devshop_project_environment', $info); + } } /** @@ -183,10 +196,6 @@ function devshop_projects_update($node) { $info->drupal_path = hosting_path_normalize($node->drupal_path); $info->base_url = $node->base_url; $info->install_profile = $node->install_profile; - - // Everything from $form['project'] gets serialized and saved. - // Save our environments data here as well. - $info->data = serialize($node->project); drupal_write_record('hosting_devshop_project', $info, 'nid'); @@ -194,6 +203,19 @@ function devshop_projects_update($node) { // If using environment aliases... save them. devshop_project_save_domain_aliases($node); + // Save Environment records. + foreach ($node->project->environments as $env => $environment){ + $info = new stdClass(); + $info->project_nid = $node->nid; + $info->environment = $env; + $info->git_ref = $environment['git_ref']; + $info->site_nid = $environment['site_nid']; + $info->platform_nid = $environment['platform_nid']; + $info->data = serialize($environment); + + drupal_write_record('hosting_devshop_project_environment', $info, 'project_nid'); + } + } From fda880b43512cd9d480f6fb2518ccc0dbfba4330 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Mar 2014 00:42:17 -0400 Subject: [PATCH 0733/3476] moving nodeapi --- devshop_projects/inc/nodes.inc | 194 ++++++++++++++++----------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index cdac0bfcd..91c8890dd 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -25,103 +25,6 @@ function devshop_projects_node_info() { return $types; } -/** - * Implementation of hook_nodeapi() - * - * Handle project information for Platforms and Sites: - * $node->project : The context name of the project. - * $node->environment: The type of environment this platform or site is: - * can be dev, test, or live. - * $node->git_branch: The current git branch the project lives on. - */ -function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - - if ($node->type == 'project') { - // Settings are added here, since $git_branches hadn't been added to the - // node yet in devshop_projects_load(). - $settings = module_invoke_all('devshop_project_settings', $node); - - // For each environment, pull values out of the site or platform into a new environments object. - if (!empty($node->project->environments)){ - foreach ($node->project->environments as $environment_name => &$environment){ - $nodes = array(); - $nodes['site'] = node_load($environment['site']); - $nodes['platform'] = node_load($environment['platform']); - - foreach ($settings as $setting_name => $setting) { - $environment[$setting_name] = $nodes[$setting['#node_type']]->{$setting_name}; - } - $environment = $environment; - } - } - } - - // Project-enabled platforms get - if ($node->type == 'platform' || $node->type == 'site') { - // On Load: load this object's project and environment type - if ($op == 'load') { - $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); - $data[$node->type . "_nid"] = $node->nid; - return $data; - } - - // On insert or update, insert records saving this objects project and environment - if ($op == 'update' || $op == 'insert') { - //Special case for migrate site. Is need copy the information to the new site. - if ($op == 'insert' && $node->type == 'site' && $node->import && !$node->verified) { - $platform = node_load($node->platform); - $node->project = $platform->project; - $node->project_nid = $platform->project_nid; - $node->environment = $platform->environment; - $node->git_branch = $platform->git_branch; - $node->drupal_path = $platform->drupal_path; - $node->clone_nid = $platform->clone_nid; - } - if (!empty($node->project) && !empty($node->environment)) { - // Get the project node by name. - $project = hosting_context_load($node->project); - - // Save to table - $data = new stdClass(); - $data->project_nid = $project->nid; - $data->object_nid = $node->nid; - $data->object_type = $node->type; - $data->environment = $node->environment; - $data->git_branch = $node->git_branch; - $data->drupal_path = $node->drupal_path; - - //Site used for cloned the new site or platorm - if (isset($node->clone_nid)) { - $data->clone_nid = $node->clone_nid; - } - - if ($op == 'insert') { - drupal_write_record('hosting_devshop_project_object', $data); - } - else { - drupal_write_record('hosting_devshop_project_object', $data, 'object_nid'); - } - - //If we are updating or inserting a platform, update all sites with the correct git_branch and environment - if ($node->type == 'platform') { - $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); - while ($site = db_fetch_object($result)) { - db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); - } - } - - // If we are updating or inserting sites, check for live domain aliases. - $live_domain_alias = "{$node->environment}.{$project->live_domain}"; - - // If project should use live_domain_aliases, and site does not have the alias already... - if ($node->type == 'site' && $project->live_domain_aliases && array_search($live_domain_alias, $node->aliases) === FALSE) { - $node->aliases[] = $live_domain_alias; - } - } - } - } -} - /** * Helper to load a project node by code path. */ @@ -305,3 +208,100 @@ function devshop_projects_load($node) { return $additions; } + +/** + * Implementation of hook_nodeapi() + * + * Handle project information for Platforms and Sites: + * $node->project : The context name of the project. + * $node->environment: The type of environment this platform or site is: + * can be dev, test, or live. + * $node->git_branch: The current git branch the project lives on. + */ +function devshop_projects_nodeapi(&$node, $op, $a3 = null) { + + if ($node->type == 'project') { + // Settings are added here, since $git_branches hadn't been added to the + // node yet in devshop_projects_load(). + $settings = module_invoke_all('devshop_project_settings', $node); + + // For each environment, pull values out of the site or platform into a new environments object. + if (!empty($node->project->environments)){ + foreach ($node->project->environments as $environment_name => &$environment){ + $nodes = array(); + $nodes['site'] = node_load($environment['site']); + $nodes['platform'] = node_load($environment['platform']); + + foreach ($settings as $setting_name => $setting) { + $environment[$setting_name] = $nodes[$setting['#node_type']]->{$setting_name}; + } + $environment = $environment; + } + } + } + + // Project-enabled platforms get + if ($node->type == 'platform' || $node->type == 'site') { + // On Load: load this object's project and environment type + if ($op == 'load') { + $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); + $data[$node->type . "_nid"] = $node->nid; + return $data; + } + + // On insert or update, insert records saving this objects project and environment + if ($op == 'update' || $op == 'insert') { + //Special case for migrate site. Is need copy the information to the new site. + if ($op == 'insert' && $node->type == 'site' && $node->import && !$node->verified) { + $platform = node_load($node->platform); + $node->project = $platform->project; + $node->project_nid = $platform->project_nid; + $node->environment = $platform->environment; + $node->git_branch = $platform->git_branch; + $node->drupal_path = $platform->drupal_path; + $node->clone_nid = $platform->clone_nid; + } + if (!empty($node->project) && !empty($node->environment)) { + // Get the project node by name. + $project = hosting_context_load($node->project); + + // Save to table + $data = new stdClass(); + $data->project_nid = $project->nid; + $data->object_nid = $node->nid; + $data->object_type = $node->type; + $data->environment = $node->environment; + $data->git_branch = $node->git_branch; + $data->drupal_path = $node->drupal_path; + + //Site used for cloned the new site or platorm + if (isset($node->clone_nid)) { + $data->clone_nid = $node->clone_nid; + } + + if ($op == 'insert') { + drupal_write_record('hosting_devshop_project_object', $data); + } + else { + drupal_write_record('hosting_devshop_project_object', $data, 'object_nid'); + } + + //If we are updating or inserting a platform, update all sites with the correct git_branch and environment + if ($node->type == 'platform') { + $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); + while ($site = db_fetch_object($result)) { + db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); + } + } + + // If we are updating or inserting sites, check for live domain aliases. + $live_domain_alias = "{$node->environment}.{$project->live_domain}"; + + // If project should use live_domain_aliases, and site does not have the alias already... + if ($node->type == 'site' && $project->live_domain_aliases && array_search($live_domain_alias, $node->aliases) === FALSE) { + $node->aliases[] = $live_domain_alias; + } + } + } + } +} From d656e73397e7d4a16c93d936f27be4600ecd752c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Mar 2014 00:44:53 -0400 Subject: [PATCH 0734/3476] Removing loading of extra settings in project nodes. I don't think it's needed any more. --- devshop_projects/inc/nodes.inc | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 91c8890dd..5b2a4efd7 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -220,26 +220,6 @@ function devshop_projects_load($node) { */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - if ($node->type == 'project') { - // Settings are added here, since $git_branches hadn't been added to the - // node yet in devshop_projects_load(). - $settings = module_invoke_all('devshop_project_settings', $node); - - // For each environment, pull values out of the site or platform into a new environments object. - if (!empty($node->project->environments)){ - foreach ($node->project->environments as $environment_name => &$environment){ - $nodes = array(); - $nodes['site'] = node_load($environment['site']); - $nodes['platform'] = node_load($environment['platform']); - - foreach ($settings as $setting_name => $setting) { - $environment[$setting_name] = $nodes[$setting['#node_type']]->{$setting_name}; - } - $environment = $environment; - } - } - } - // Project-enabled platforms get if ($node->type == 'platform' || $node->type == 'site') { // On Load: load this object's project and environment type From 43c2926916b1c00613c9be4a39acdca82a87e5e5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Mar 2014 01:31:53 -0400 Subject: [PATCH 0735/3476] refactoring platform and site update code. --- devshop_projects/inc/nodes.inc | 135 ++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 63 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 5b2a4efd7..c8b184983 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -220,68 +220,77 @@ function devshop_projects_load($node) { */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { - // Project-enabled platforms get - if ($node->type == 'platform' || $node->type == 'site') { - // On Load: load this object's project and environment type - if ($op == 'load') { - $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); - $data[$node->type . "_nid"] = $node->nid; - return $data; - } - - // On insert or update, insert records saving this objects project and environment - if ($op == 'update' || $op == 'insert') { - //Special case for migrate site. Is need copy the information to the new site. - if ($op == 'insert' && $node->type == 'site' && $node->import && !$node->verified) { - $platform = node_load($node->platform); - $node->project = $platform->project; - $node->project_nid = $platform->project_nid; - $node->environment = $platform->environment; - $node->git_branch = $platform->git_branch; - $node->drupal_path = $platform->drupal_path; - $node->clone_nid = $platform->clone_nid; - } - if (!empty($node->project) && !empty($node->environment)) { - // Get the project node by name. - $project = hosting_context_load($node->project); - - // Save to table - $data = new stdClass(); - $data->project_nid = $project->nid; - $data->object_nid = $node->nid; - $data->object_type = $node->type; - $data->environment = $node->environment; - $data->git_branch = $node->git_branch; - $data->drupal_path = $node->drupal_path; - - //Site used for cloned the new site or platorm - if (isset($node->clone_nid)) { - $data->clone_nid = $node->clone_nid; - } - - if ($op == 'insert') { - drupal_write_record('hosting_devshop_project_object', $data); - } - else { - drupal_write_record('hosting_devshop_project_object', $data, 'object_nid'); - } - - //If we are updating or inserting a platform, update all sites with the correct git_branch and environment - if ($node->type == 'platform') { - $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); - while ($site = db_fetch_object($result)) { - db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); - } - } - - // If we are updating or inserting sites, check for live domain aliases. - $live_domain_alias = "{$node->environment}.{$project->live_domain}"; - - // If project should use live_domain_aliases, and site does not have the alias already... - if ($node->type == 'site' && $project->live_domain_aliases && array_search($live_domain_alias, $node->aliases) === FALSE) { - $node->aliases[] = $live_domain_alias; - } - } - } + // Load environment info into platforms and sites. + if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { + $additions = array(); + $additions['environment'] = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project_environment} where {$node->type}_nid', $node->nid)); + return $additions; } } + +// @TODO: Remove. I believe this is all cruft from back things were a bit more decoupled. +// +// if ($op == 'load') { +// +// $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); +// +// $data[$node->type . "_nid"] = $node->nid; +// return $data; +// } +// +// // On insert or update, insert records saving this objects project and environment +// if ($op == 'update' || $op == 'insert') { +// //Special case for migrate site. Is need copy the information to the new site. +// if ($op == 'insert' && $node->type == 'site' && $node->import && !$node->verified) { +// $platform = node_load($node->platform); +// $node->project = $platform->project; +// $node->project_nid = $platform->project_nid; +// $node->environment = $platform->environment; +// $node->git_branch = $platform->git_branch; +// $node->drupal_path = $platform->drupal_path; +// $node->clone_nid = $platform->clone_nid; +// } +// if (!empty($node->project) && !empty($node->environment)) { +// // Get the project node by name. +// $project = hosting_context_load($node->project); +// +// // Save to table +// $data = new stdClass(); +// $data->project_nid = $project->nid; +// $data->object_nid = $node->nid; +// $data->object_type = $node->type; +// $data->environment = $node->environment; +// $data->git_branch = $node->git_branch; +// $data->drupal_path = $node->drupal_path; +// +// //Site used for cloned the new site or platorm +// if (isset($node->clone_nid)) { +// $data->clone_nid = $node->clone_nid; +// } +// +// if ($op == 'insert') { +// drupal_write_record('hosting_devshop_project_object', $data); +// } +// else { +// drupal_write_record('hosting_devshop_project_object', $data, 'object_nid'); +// } +// +// //If we are updating or inserting a platform, update all sites with the correct git_branch and environment +// if ($node->type == 'platform') { +// $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); +// while ($site = db_fetch_object($result)) { +// db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); +// } +// } +// +// // If we are updating or inserting sites, check for live domain aliases. +// $live_domain_alias = "{$node->environment}.{$project->live_domain}"; +// +// // If project should use live_domain_aliases, and site does not have the alias already... +// if ($node->type == 'site' && $project->live_domain_aliases && array_search($live_domain_alias, $node->aliases) === FALSE) { +// $node->aliases[] = $live_domain_alias; +// } +// } +// } +// } +//} From 253439ef01ef36be27e3953e373c2b635a57bfd5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Mar 2014 01:37:31 -0400 Subject: [PATCH 0736/3476] removing todo --- devshop_projects/inc/nodes.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index c8b184983..4e38f61d8 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -4,7 +4,6 @@ * * DevShop Project Node related hooks and support functions. * - * @TODO: Add default http and db servers to project nodes. */ /** From cefaae8c5d5e0f85cba059c19e22e09675505079 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Mar 2014 01:37:43 -0400 Subject: [PATCH 0737/3476] removing unused function. --- devshop_projects/inc/nodes.inc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 4e38f61d8..65f9151f7 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -24,14 +24,6 @@ function devshop_projects_node_info() { return $types; } -/** - * Helper to load a project node by code path. - */ -function devshop_project_load_by_path($project_path){ - $nid = db_result(db_query('SELECT nid FROM {hosting_devshop_project} WHERE code_path = "%s"', array($project_path))); - return node_load($nid); -} - /** * Implementation of hook_insert(). * From 341845d7cc4b9ca4594677f048d2c7e3b92bdb4a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 4 Apr 2014 22:39:06 -0400 Subject: [PATCH 0738/3476] Fixing bad module_disable() call, only install new table in update. --- devshop_projects/devshop_projects.install | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 544623113..c67fd5135 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -269,7 +269,7 @@ function devshop_projects_update_9() { * Remove live_domain from hosting_devsho_project schema and disable devshop_live if needed. */ function devshop_projects_update_10() { - modules_disable(array('devshop_live')); + module_disable(array('devshop_live')); $ret = array(); if (db_column_exists('hosting_devshop_project', 'live_domain')){ $ret[] = db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); @@ -277,11 +277,19 @@ function devshop_projects_update_10() { return $ret; } - /** * Create new hosting_devshop_environment table. */ function devshop_projects_update_11() { - // Create tables. - drupal_install_schema('devshop_projects'); + // Create only the new table + // Clone of drupal_install_schema('devshop_projects'); + $module = 'devshop_projects'; + $name = 'hosting_devshop_project_environment'; + + $schema = drupal_get_schema_unprocessed($module); + _drupal_initialize_schema($module, $schema); + + $ret = array(); + db_create_table($ret, $name, $schema['hosting_devshop_project_environment']); + return $ret; } \ No newline at end of file From afecd6b74a346c94d53adb20959dec5e67f6586e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 4 Apr 2014 22:42:33 -0400 Subject: [PATCH 0739/3476] Fixing bad query string. --- devshop_projects/inc/nodes.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 65f9151f7..3d80b4b4e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -196,6 +196,7 @@ function devshop_projects_load($node) { // Save project object be available at $node->project. $additions['project'] = $project; + return $additions; } @@ -214,7 +215,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $additions['environment'] = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project_environment} where {$node->type}_nid', $node->nid)); + $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} where {$node->type}_nid", $node->nid)); return $additions; } } From 33e3a53a7c28890660e02a5ab88d205b5bf3e545 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 4 Apr 2014 22:52:38 -0400 Subject: [PATCH 0740/3476] Utilizing project object. --- devshop_projects/devshop_projects.drush.inc | 13 +++++------- devshop_projects/devshop_projects.module | 3 +-- devshop_projects/inc/create-wizard.inc | 2 +- devshop_projects/inc/forms.inc | 4 ---- devshop_projects/inc/nodes.inc | 22 +++++++++++---------- 5 files changed, 19 insertions(+), 25 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index cbde5de86..3428f5461 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -310,19 +310,16 @@ function devshop_projects_hosting_project_context_options(&$task) { // If something went wrong connecting to the git repo, don't wipe out our branches. if (!empty($branches['branches'])){ - $task->ref->project_settings['git_branches'] = $branches['branches']; - $task->ref->project_settings['git_tags'] = $branches['tags']; + $task->ref->project->git['branches'] = $branches['branches']; + $task->ref->project->git['tags'] = $branches['tags']; // Save the project node now that we have branches and tags. node_save($task->ref); } - $task->context_options['git_branches'] = $branches['branches']; - $task->context_options['git_tags'] = $branches['tags']; - - // Load environment settings - if (isset($task->ref->settings)) { - $task->context_options['settings'] = $task->ref->settings; + // Save project object to context. + if (isset($task->ref->project)) { + $task->context_options['project'] = $task->ref->project; } } diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 23ec1bba4..9bf1d5f31 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -139,11 +139,10 @@ function devshop_projects_menu() { * Implements hook_devshop_project_settings */ function devshop_projects_devshop_project_settings($project_node = NULL){ - // Build branch options if (is_array($project_node->project->git['branches']) && !empty($project_node->project->git['branches'])) { $branch_options = array( - 'Branches' => array_combine($project_node->git_branches, $project_node->git_branches), + 'Branches' => array_combine($project_node->project->git['branches'], $project_node->project->git['branches']), ); } diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index d406a685f..049b23581 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -676,7 +676,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($project_node->environments, $values['environments']); + $removed_environments = array_diff_key($project_node->project->environments, $values['environments']); foreach ($removed_environments as $environment_name => $settings) { // @TODO: Determine what to do here based on task status... diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 89fbc801d..31e0b9b21 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -290,7 +290,6 @@ function devshop_projects_submit_settings($form, &$form_state) { } // Go save all nodes - dsm($nodes, 'nodes to save'); foreach ($nodes as $nid => $node){ node_save($node); } @@ -327,9 +326,6 @@ function devshop_projects_validate($node, &$form) { $result = db_fetch_object(db_query("SELECT name, nid FROM {hosting_context} WHERE name = '%s'", $node->title)); if ($node->nid != $result->nid){ - dsm($node, 'node'); - dsm($result, 'result'); - form_set_error('title', t('Project code name is unavailable. Please choose another.')); } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3d80b4b4e..01a236f0e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -58,16 +58,18 @@ function devshop_projects_insert($node) { devshop_project_save_domain_aliases($node); // Save Environment records. - foreach ($node->project->environments as $env => $environment){ - $info = new stdClass(); - $info->project_nid = $node->nid; - $info->environment = $env; - $info->git_ref = $environment['git_ref']; - $info->site_nid = $environment['site_nid']; - $info->platform_nid = $environment['platform_nid']; - $info->data = serialize($environment); - - drupal_write_record('hosting_devshop_project_environment', $info); + if (!empty($node->project->environments)) { + foreach ($node->project->environments as $env => $environment){ + $info = new stdClass(); + $info->project_nid = $node->nid; + $info->environment = $env; + $info->git_ref = $environment['git_ref']; + $info->site_nid = $environment['site_nid']; + $info->platform_nid = $environment['platform_nid']; + $info->data = serialize($environment); + + drupal_write_record('hosting_devshop_project_environment', $info); + } } } From 9de3e4d25d8b86815a85040b3246c90c58026e87 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 00:26:53 -0400 Subject: [PATCH 0741/3476] Major progress on create wizard refactor for new environments table. --- devshop_projects/devshop_projects.module | 4 +- devshop_projects/inc/create-wizard.inc | 192 +++++++++++------------ devshop_projects/inc/forms.inc | 2 +- devshop_projects/inc/nodes.inc | 6 +- 4 files changed, 99 insertions(+), 105 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9bf1d5f31..0bb86f984 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -157,11 +157,11 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ } $settings = array(); - $settings['site_nid'] = array( + $settings['site'] = array( '#node_type' => 'site', '#type' => 'hidden', ); - $settings['platform_nid'] = array( + $settings['platform'] = array( '#node_type' => 'platform', '#type' => 'hidden', ); diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 049b23581..8da627b42 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -227,6 +227,7 @@ function devshop_projects_create_wizard_cancel(&$form_state) { */ function devshop_project_create_step_git(&$form, &$form_state) { $project = &$form_state['project']; + $project_node = node_load($project->project_nid); if ($project->verify_error){ $form['note'] = array( @@ -245,7 +246,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { '#required' => 1, '#title' => t('Git URL'), '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), - '#default_value' => $project->git_url, + '#default_value' => $project_node->project->git_url, ); $form['title'] = array( @@ -254,7 +255,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { '#required' => TRUE, '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository. NOTE: You cannot change this, but you can delete and start over.Choose wisely.'), '#size' => 40, - '#default_value' => $project->title, + '#default_value' => $project_node->project->title, '#maxlength' => 255, ); @@ -305,7 +306,7 @@ HTML; * * This is also where we get all the tags and branches. */ -function devshop_project_create_step_git_validate(&$from, &$form_state) { +function devshop_project_create_step_git_validate(&$form, &$form_state) { $project = &$form_state['project']; // No spaces or special characters allowed. @@ -317,6 +318,8 @@ function devshop_project_create_step_git_validate(&$from, &$form_state) { form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); } + form_set_value($form['title'], $project_name, $form_state); + // Check for duplicate project name here. $node = hosting_context_load($project_name); if ($node->nid != $project->project_nid){ @@ -332,49 +335,48 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project = &$form_state['project']; - // If the project already exists, and git url has changed... + // If the project already exists, this means the git url has changed... if ($project->project_nid) { // Change the git url and save the node. Verification SHOULD run again. $node = node_load($project->project_nid); - $node->git_url = $form_state['values']['git_url']; - $node->branches = array(); + $node->project->git_url = $form_state['values']['git_url']; node_save($node); - - // Also clear the repo related info from $project } - - // Now that we've compared old and new, set $project properties. - $project->git_url = $form_state['values']['git_url']; - $project->title = $project_name = $form_state['values']['title']; - - if (empty($project->project_nid)){ + // Create new project node + elseif (empty($project->project_nid)){ // Create the project node now. We will update it with the chosen path. // This is so we can check for branches and save the hosting_context as soon // as possible. $node = new stdClass; - $node->title = $project->title; - $node->git_url = $project->git_url; + $node->title = $form_state['values']['title']; + + $node->type = 'project'; $node->status = 0; $node->uid = $user->uid; $node->name = $user->name; + + $node->project = new stdClass(); + $node->project->git_url = $form_state['values']['git_url']; + $node->project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $form_state['values']['title']; + + // Setup project url. + $base_url = $_SERVER['SERVER_NAME']; + $server = variable_get('devshop_project_master_url', $base_url); + $node->project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => $form_state['values']['title'], '@hostname' => $server)); + $node->project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); + + // Save project node. if ($node = node_submit($node)) { node_save($node); } + + // Save NID to ctools object cache. if ($node->nid){ $project->project_nid = $node->nid; } } - // Now that we have the project name, set the defaults for code path and project URL - $project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $project_name; - - // Setup project url. - $base_url = $_SERVER['SERVER_NAME']; - $server = variable_get('devshop_project_master_url', $base_url); - $project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => strtolower($project_name), '@hostname' => $server)); - $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); - //Verify if is need to skip. $skip = variable_get('devshop_projects_skip_settings', TRUE); if ($skip) { @@ -444,11 +446,13 @@ function devshop_project_create_step_settings_validate(&$from, &$form_state) { function devshop_project_create_step_settings_submit(&$from, &$form_state) { $project = &$form_state['project']; - // For this step, we just save code path and base url for later saving and - // verification. - $project->code_path = $form_state['values']['code_path']; - $project->drupal_path = $form_state['values']['drupal_path']; - $project->base_url = $form_state['values']['base_url']; + // Save code path and base url. + $project_node = node_load($project->project_nid); + $project_node->project->code_path = $form_state['values']['code_path']; + $project_node->project->drupal_path = $form_state['values']['drupal_path']; + $project_node->project->base_url = $form_state['values']['base_url']; + + node_save($project_node); } @@ -488,8 +492,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); // Add code to reload the page when complete. devshop_form_reloader($form, 'project'); - - return; } @@ -498,8 +500,14 @@ function devshop_project_create_step_environments(&$form, &$form_state) { drupal_add_js($path); $settings = module_invoke_all('devshop_project_settings', $project_node); - - $form['environments'] = array( + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + $form['project'] = array( + '#tree' => TRUE, + ); + $form['project']['environments'] = array( '#theme' => 'devshop_projects_create_settings_form', '#tree' => TRUE, '#prefix' => '
', @@ -507,11 +515,10 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); // Ensure a blank row exists (happens when using 'Back' button) - if (!is_array($project->environments['NEW'])){ - $project->environments['NEW'] = array(); + if (!is_array($project_node->project->environments['NEW'])){ + $project_node->project->environments['NEW'] = array(); } - - foreach ($project->environments as $env => $env_settings) { + foreach ($project_node->project->environments as $env => $env_settings) { // No platforms exist yet if ($env == 'NEW') { $env_title = ''; @@ -519,13 +526,13 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $env_title = $env; } - $form['environments'][$env] = array( + $form['project']['environments'][$env] = array( '#tree' => TRUE, '#type' => 'fieldset', '#theme' => 'devshop_projects_settings_table', ); - $form['environments'][$env]['title'] = array( + $form['project']['environments'][$env]['title'] = array( '#type' => 'textfield', '#title' => t('Environment Name'), '#default_value' => $env_title, @@ -535,15 +542,15 @@ function devshop_project_create_step_environments(&$form, &$form_state) { 'placeholder' => t('name'), ), '#field_prefix' => 'http://', - '#field_suffix' => ".$project->base_url", + '#field_suffix' => "." . $project_node->project->base_url, ); // Add environment settings form elements foreach ($settings as $setting_id => $setting){ - $form['environments'][$env][$setting_id] = $setting; - $form['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; - $form['environments'][$env][$setting_id]['#attributes']['title'] = $setting['#description']; - $form['environments'][$env][$setting_id]['#description'] = ''; + $form['project']['environments'][$env][$setting_id] = $setting; + $form['project']['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; + $form['project']['environments'][$env][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project']['environments'][$env][$setting_id]['#description'] = ''; } //Now add button. @@ -564,56 +571,43 @@ function devshop_project_create_step_environments(&$form, &$form_state) { */ function devshop_project_create_step_environments_validate(&$form, &$form_state) { $project = &$form_state['project']; - $values = &$form_state['values']; - // Project not verified yet. We don't know the branches. - if (isset($values['not_ready'])) { - form_set_error('', t('We haven\'t connected to your repo yet. Please wait for the queue.')); - return; - } + $values = &$form_state['values']; // Changes NEW environment data to $title - if ($values['environments']['NEW']['title']){ - $new_env = $values['environments']['NEW']['title']; - $new_env_settings = $values['environments']['NEW']; - $values['environments'][$new_env] = $new_env_settings; + if ($values['project']['environments']['NEW']['title']){ + $new_env = $values['project']['environments']['NEW']['title']; + $new_env_settings = $values['project']['environments']['NEW']; + $values['project']['environments'][$new_env] = $new_env_settings; // Create the next NEW environment. Unset makes sure its always last. - unset($values['environments']['NEW']); + unset($values['project']['environments']['NEW']); // If "add environment" button clicked, add another row. if ($form_state['clicked_button']['#name'] == 'add_environment'){ - $values['environments']['NEW'] = array(); + $values['project']['environments']['NEW'] = array(); } } else { - unset($values['environments']['NEW']); + unset($values['project']['environments']['NEW']); } - // Reset project environments - $project->environments = array(); - // Check environment titles - foreach ($values['environments'] as $env => $env_settings) { + foreach ($values['project']['environments'] as $env => $env_settings) { // Check for illegal chars if ($env != 'NEW' && !empty($env_settings['title'])){ if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { $form_item = 'environments][' . $env . '][title'; form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); - } else { - // If all is well, save to project cache - $project->environments[$env] = $env_settings; } } } - - // Reject if empty - if (count($project->environments) < 1){ + if (count($values['project']['environments']) < 1){ if ($form_state['clicked_button']['#name'] == 'add_environment'){ - form_set_error('environments][NEW][title', t('Name this environment before you add another.')); + form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); } else { - form_set_error('environments][NEW][title', t('You must add at least one environment.')); + form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); } } } @@ -623,22 +617,24 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) */ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Get project and reset properties.. - $project = &$form_state['project']; - $project_node = node_load($project->project_nid); - $values = $form_state['values']; - - // @TODO: Settings from the form are not saving. + $project_node = node_load($form_state['values']['nid']); $settings = module_invoke_all('devshop_project_settings', $project_node); // Create these platforms, if they don't exist yet - foreach ($project->environments as $name => $environment) { - $platform_nid = $project_node->environments[$name]['platform']; + foreach ($form_state['values']['project']['environments'] as $name => $environment) { + + if ($name == 'NEW') continue; + + // If platform exists, it's because user went back in the wizard. + $platform_nid = $project_node->project->environments[$name]['platform']; // If the platform already exists, save it again with new settings. if ($platform_nid) { $platform_node = node_load($platform_nid); foreach ($settings as $setting_name => $element){ - $platform_node->{$setting_name} = $environment[$setting_name]; + if ($element['#node_type'] == 'platform'){ + $platform_node->{$setting_name} = $environment[$setting_name]; + } } node_save($platform_node); } @@ -649,34 +645,38 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform = new stdClass; // Platform name - $platform->title = $project->title . '_' . $environment['title']; + $platform->title = $project_node->title . '_' . $environment['title']; // Platform publish_path - if ($project->drupal_path) { - $platform->publish_path = $project->code_path . '/' . $environment['title'] . '/' . $project->drupal_path; + if (!empty($project_node->project->drupal_path)) { + $platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; } else { - $platform->publish_path = $project->code_path . '/' . $environment['title']; + $platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; } // Other attributes $platform->web_server = $environment['web_server']; $platform->git_branch = $environment['git_branch']; $platform->pull_enable = $environment['pull_enabled']; - $platform->project = $project->title; + $platform->project = $project_node->title; $platform->environment = $environment['title']; - $platform->drupal_path = $project->drupal_path; +// $platform->drupal_path = $project_node->project->drupal_path; foreach ($settings as $setting_name => $element){ - $platform->{$setting_name} = $environment[$setting_name]; + if ($element['#node_type'] == 'platform'){ + $platform->{$setting_name} = $environment[$setting_name]; + } } $platform_node = _devshop_projects_node_create('platform', $platform); - $project->environments[$name]['platform_nid'] = $platform_node->nid; } + + $environment['platform'] = $platform_node->nid; + $project_node->project->environments[$name] = $environment; } // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($project_node->project->environments, $values['environments']); + $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); foreach ($removed_environments as $environment_name => $settings) { // @TODO: Determine what to do here based on task status... @@ -689,6 +689,10 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { hosting_add_task($platform_nid, 'delete'); } } + +// dsm($platform_node, 'platform'); +// dsm($project_node, 'saving project'); + node_save($project_node); } /********** @@ -703,20 +707,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); - // OUTPUT - // @TODO: Clean this up!! - - // @TODO: This is old code that is never seen. User ends up on project page. - // Perhaps we should keep people on this page until the sites are all installed? No, because we want to free up the wizard for another project create... instead lets fix the project node page to display proper status. - if ($project_node->project_status == 'sites_installing') { - $form['note'] = array( - '#type' => 'item', - '#title' => t('Sites Installing'), - '#value' => t('Your sites are being installed!'), - ); - return $form; - } - $profiles = array(); $available_profiles = array(); $completed = TRUE; diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 31e0b9b21..e74b0cb3b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -257,7 +257,7 @@ function devshop_projects_form(&$node) { function devshop_projects_submit_settings($form, &$form_state) { // Go through and save our settings to site and platform nodes. - $project_node = node_load($form_state['values']['nid']); +// $project_node = node_load($form_state['values']['nid']); $settings_info = module_invoke_all('devshop_project_settings', $project_node); $nodes = array(); diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 01a236f0e..32a3811b3 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -99,6 +99,9 @@ function devshop_projects_update($node) { // If using environment aliases... save them. devshop_project_save_domain_aliases($node); + // Delete existing environment records + db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); + // Save Environment records. foreach ($node->project->environments as $env => $environment){ $info = new stdClass(); @@ -109,7 +112,7 @@ function devshop_projects_update($node) { $info->platform_nid = $environment['platform_nid']; $info->data = serialize($environment); - drupal_write_record('hosting_devshop_project_environment', $info, 'project_nid'); + drupal_write_record('hosting_devshop_project_environment', $info); } } @@ -193,6 +196,7 @@ function devshop_projects_load($node) { while ($environment_data = db_fetch_array($query)) { $environment_data['data'] = unserialize($environment_data['data']); $environments[$environment_data['environment']] = $environment_data; + $environments[$environment_data['environment']]['in_db'] = TRUE; } $project->environments = $environments; From b98c7703189d2204b25f2254a6b2af29cf56f666 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 00:35:11 -0400 Subject: [PATCH 0742/3476] More progress on create wizard refactor for new environments table. --- devshop_projects/inc/create-wizard.inc | 14 +++----------- devshop_projects/inc/nodes.inc | 4 ++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 8da627b42..7486a7f66 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -623,8 +623,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Create these platforms, if they don't exist yet foreach ($form_state['values']['project']['environments'] as $name => $environment) { - if ($name == 'NEW') continue; - // If platform exists, it's because user went back in the wizard. $platform_nid = $project_node->project->environments[$name]['platform']; @@ -639,12 +637,9 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { node_save($platform_node); } // If platform hasn't been created yet, do so now! - elseif (empty($platform_nid)) { + else { - // Save the damn platform nodes $platform = new stdClass; - - // Platform name $platform->title = $project_node->title . '_' . $environment['title']; // Platform publish_path @@ -657,11 +652,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Other attributes $platform->web_server = $environment['web_server']; - $platform->git_branch = $environment['git_branch']; - $platform->pull_enable = $environment['pull_enabled']; - $platform->project = $project_node->title; - $platform->environment = $environment['title']; -// $platform->drupal_path = $project_node->project->drupal_path; foreach ($settings as $setting_name => $element){ if ($element['#node_type'] == 'platform'){ @@ -707,6 +697,8 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); + dsm($project_node, 'project'); + $profiles = array(); $available_profiles = array(); $completed = TRUE; diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 32a3811b3..c611e8427 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -108,8 +108,8 @@ function devshop_projects_update($node) { $info->project_nid = $node->nid; $info->environment = $env; $info->git_ref = $environment['git_ref']; - $info->site_nid = $environment['site_nid']; - $info->platform_nid = $environment['platform_nid']; + $info->site_nid = $environment['site']; + $info->platform_nid = $environment['platform']; $info->data = serialize($environment); drupal_write_record('hosting_devshop_project_environment', $info); From 84df8b4571b73e10de314096b0e00968fd3c0016 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 11:42:10 -0400 Subject: [PATCH 0743/3476] fixing load query for platforms and sites. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index c611e8427..e40e83370 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -221,7 +221,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} where {$node->type}_nid", $node->nid)); + $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} where {$node->type}_nid = %d", $node->nid)); return $additions; } } From 9cd179b3e1059b4f2a1329840e53862cb778a764 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 11:47:48 -0400 Subject: [PATCH 0744/3476] properly set default value to title. --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 7486a7f66..bfe82ce3c 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -255,7 +255,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { '#required' => TRUE, '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository. NOTE: You cannot change this, but you can delete and start over.Choose wisely.'), '#size' => 40, - '#default_value' => $project_node->project->title, + '#default_value' => $project_node->title, '#maxlength' => 255, ); From 8dbbad1bd7c824ee4e0e62c3995252ec268ad23a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 12:25:03 -0400 Subject: [PATCH 0745/3476] Remove NEW environment, fixing serialized data saving, fixing environments saving, cleaning up project context, changing schema to just site and platform, changing to git_ref. --- devshop_projects/devshop_projects.drush.inc | 6 -- devshop_projects/devshop_projects.install | 9 ++- devshop_projects/devshop_projects.module | 2 +- devshop_projects/inc/create-wizard.inc | 38 +++++----- devshop_projects/inc/nodes.inc | 80 +++++++++++++-------- 5 files changed, 77 insertions(+), 58 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 3428f5461..a740a31d3 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -299,12 +299,6 @@ function devshop_projects_post_hosting_verify_task($task, $data) { function devshop_projects_hosting_project_context_options(&$task) { $task->context_options['server'] = '@server_master'; - $task->context_options['project_name'] = $task->ref->title; - $task->context_options['install_profile'] = $task->ref->install_profile; - $task->context_options['base_url'] = $task->ref->base_url; - $task->context_options['code_path'] = trim($task->ref->code_path, " "); - $task->context_options['drupal_path'] = trim($task->ref->drupal_path, " "); - $task->context_options['git_url'] = $task->ref->git_url; $branches = getBranchesAndTags($task->ref->project->git_url); diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index c67fd5135..dd405ad2a 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -123,20 +123,23 @@ function devshop_projects_schema() { 'git_ref' => array( 'type' => 'varchar', 'length' => 128, - 'not null' => FALSE, + 'not null' => TRUE, + 'default' => '', 'description' => 'The current branch or tag of this site or platform.', ), - 'site_nid' => array( + 'site' => array( 'type' => 'int', 'not null' => FALSE, 'default' => 0, 'unsigned' => TRUE, + 'description' => 'The node ID of the site for this environment.', ), - 'platform_nid' => array( + 'platform' => array( 'type' => 'int', 'not null' => FALSE, 'default' => 0, 'unsigned' => TRUE, + 'description' => 'The node ID of the platform for this environment.', ), 'data' => array( 'type' => 'text', diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 0bb86f984..08a2584e9 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -165,7 +165,7 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ '#node_type' => 'platform', '#type' => 'hidden', ); - $settings['git_branch'] = array( + $settings['git_ref'] = array( '#title' => t('Git Branch/Tag'), '#node_type' => 'platform', '#type' => 'select', diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index bfe82ce3c..94107938a 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -661,27 +661,29 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform_node = _devshop_projects_node_create('platform', $platform); } + $environment['platform'] = $platform_node->nid; $project_node->project->environments[$name] = $environment; } - // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); - - foreach ($removed_environments as $environment_name => $settings) { - // @TODO: Determine what to do here based on task status... - // if verify task hasn't even run yet (and has never run) we can just delete - // the platform node. - - // Create 'delete' task for the removed platform - $platform_nid = $settings['platform']; - if ($platform_nid){ - hosting_add_task($platform_nid, 'delete'); - } - } - -// dsm($platform_node, 'platform'); -// dsm($project_node, 'saving project'); + // @TODO: Put this back once creation works. +// // For all removed platforms, trigger a delete task +// $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); +// +// foreach ($removed_environments as $environment_name => $settings) { +// // @TODO: Determine what to do here based on task status... +// // if verify task hasn't even run yet (and has never run) we can just delete +// // the platform node. +// +// // Create 'delete' task for the removed platform +// $platform_nid = $settings['platform']; +// if ($platform_nid){ +// hosting_add_task($platform_nid, 'delete'); +// } +// } + + // Somehow, NEW environment still appears! + unset($project_node->project->environments['NEW']); node_save($project_node); } @@ -697,8 +699,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); - dsm($project_node, 'project'); - $profiles = array(); $available_profiles = array(); $completed = TRUE; diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index e40e83370..590c969a8 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -34,18 +34,23 @@ function devshop_projects_node_info() { * @see hosting_platform_insert() */ function devshop_projects_insert($node) { + if (!isset($node->no_verify)) { hosting_add_task($node->nid, 'verify'); } $info = new stdClass(); $info->nid = $node->nid; - $info->git_url = $node->git_url; - $info->code_path = hosting_path_normalize($node->code_path); - $info->drupal_path = hosting_path_normalize($node->drupal_path); - $info->base_url = $node->base_url; - $info->install_profile = $node->install_profile; - $info->data = serialize($node->project); + $info->git_url = $node->project->git_url; + $info->code_path = hosting_path_normalize($node->project->code_path); + $info->drupal_path = hosting_path_normalize($node->project->drupal_path); + $info->base_url = $node->project->base_url; + $info->install_profile = $node->project->install_profile; + + // Save serialized data, minus environments + $data = $node->project; + unset($data->environments); + $info->data = serialize($data); drupal_write_record('hosting_devshop_project', $info); @@ -64,8 +69,8 @@ function devshop_projects_insert($node) { $info->project_nid = $node->nid; $info->environment = $env; $info->git_ref = $environment['git_ref']; - $info->site_nid = $environment['site_nid']; - $info->platform_nid = $environment['platform_nid']; + $info->site = $environment['site']; + $info->platform = $environment['platform']; $info->data = serialize($environment); drupal_write_record('hosting_devshop_project_environment', $info); @@ -81,40 +86,58 @@ function devshop_projects_insert($node) { * */ function devshop_projects_update($node) { + if (!$node->no_verify) { hosting_add_task($node->nid, 'verify'); } $info = new stdClass(); $info->nid = $node->nid; - $info->git_url = $node->git_url; - $info->code_path = hosting_path_normalize($node->code_path); - $info->drupal_path = hosting_path_normalize($node->drupal_path); - $info->base_url = $node->base_url; - $info->install_profile = $node->install_profile; - $info->data = serialize($node->project); + $info->git_url = $node->project->git_url; + $info->code_path = hosting_path_normalize($node->project->code_path); + $info->drupal_path = hosting_path_normalize($node->project->drupal_path); + $info->base_url = $node->project->base_url; + $info->install_profile = $node->project->install_profile; + + $environments = $node->project->environments; + + // Save serialized data, minus environments + $data = $node->project; + unset($data->environments); + $info->data = serialize($data); drupal_write_record('hosting_devshop_project', $info, 'nid'); // If using environment aliases... save them. devshop_project_save_domain_aliases($node); - // Delete existing environment records - db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); + // Save Environment records. - foreach ($node->project->environments as $env => $environment){ - $info = new stdClass(); - $info->project_nid = $node->nid; - $info->environment = $env; - $info->git_ref = $environment['git_ref']; - $info->site_nid = $environment['site']; - $info->platform_nid = $environment['platform']; - $info->data = serialize($environment); - - drupal_write_record('hosting_devshop_project_environment', $info); - } + if (!empty($environments)) { + // Delete existing environment records + db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); + // Save each environment + foreach ($environments as $env => $environment){ + $info = new stdClass(); + $info->project_nid = $node->nid; + $info->environment = $env; + $info->git_ref = $environment['git_ref']; + $info->site = $environment['site']; + $info->platform = $environment['platform']; + + unset($environment->data); + $info->data = serialize($environment); + + if (drupal_write_record('hosting_devshop_project_environment', $info)){ + drupal_set_message(t('Environment %name saved.', array('%name' => $env))); + } + else { + drupal_set_message(t('Something went wrong saving environment %name.', array('%name' => $env)), 'error'); + } + } + } } @@ -196,7 +219,6 @@ function devshop_projects_load($node) { while ($environment_data = db_fetch_array($query)) { $environment_data['data'] = unserialize($environment_data['data']); $environments[$environment_data['environment']] = $environment_data; - $environments[$environment_data['environment']]['in_db'] = TRUE; } $project->environments = $environments; @@ -221,7 +243,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} where {$node->type}_nid = %d", $node->nid)); + $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} where {$node->type} = %d", $node->nid)); return $additions; } } From 1fe76c26712a9c324f39e8dc0bb5523897c5399d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 12:27:38 -0400 Subject: [PATCH 0746/3476] Adding project info to platform/site "environment" property. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 590c969a8..169ce1a20 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -243,7 +243,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} where {$node->type} = %d", $node->nid)); + $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_devshop_project} p on e.project_nid = p.nid where {$node->type} = %d", $node->nid)); return $additions; } } From d8fc3b752aaafd25b21830e3d90cce788ddb7bd2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 17:40:09 -0400 Subject: [PATCH 0747/3476] Fixing platform verification to properly find the git url and clone it. --- devshop_projects/devshop_projects.drush.inc | 89 +++++++-------------- devshop_projects/inc/nodes.inc | 2 +- 2 files changed, 32 insertions(+), 59 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index a740a31d3..6eccc295e 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -19,12 +19,15 @@ */ function drush_devshop_projects_pre_hosting_task() { + drush_log(dt("[DEVSHOP] Hello. Pre-Task check...")); + $task =& drush_get_context('HOSTING_TASK'); + // Verify Platform // For our platforms, we have to clone it if it has a git_remote // If it has a git branch, we should checkout as well. - if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->git_url)) { - drush_devshop_provision_pre_hosting_task_platform_verify(); + if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->environment->git_url)) { + drush_devshop_platform_verify(); } // Pull @@ -80,27 +83,32 @@ function drush_devshop_projects_pre_hosting_task() { /** * Pre hosting task hook for "verify platform" task. * - Clones the repository on first run, checks out the selected after that. + * + * Not a hook! Called from drush_devshop_projects_pre_hosting_task */ -function drush_devshop_provision_pre_hosting_task_platform_verify(){ +function drush_devshop_platform_verify(){ + + drush_log(dt("[DEVSHOP] Hello. Verifying Platform...")); + // Verify Platform $task =& drush_get_context('HOSTING_TASK'); $platform = $task->ref; $root = $platform->publish_path; - $git_remote = $platform->git_url; - $git_branch = $platform->git_branch; + $git_remote = $platform->environment->git_url; + $git_branch = $platform->environment->git_ref; $create_branch = FALSE; $output = ''; //Remove drupal_path to clone. - if ($platform->drupal_path) { - $root = str_replace($platform->drupal_path, '', $root); + if ($platform->environment->drupal_path) { + $root = str_replace($platform->environment->drupal_path, '', $root); } - // Check if a repo exists + // Check if the platform code exists. If it doesn't, clone it. if (!is_dir($root) || !drush_shell_cd_and_exec($root, 'git status')) { - drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $platform->git_url, '!root' => $root))); + drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $git_remote, '!root' => $root))); // Build the command string $command = "git clone --recursive $git_remote $root"; @@ -108,25 +116,17 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ $command .= " --branch $git_branch"; } - //If is set site nid clone from old site and later create a new branch. - if ($platform->clone_nid) { - $site_source = node_load($platform->clone_nid); - $git_branch = $site_source->git_branch; - $create_branch = TRUE; - } + // @TODO: Gotta be a better way... + // If is set site nid clone from old site and later create a new branch. +// if ($platform->clone_nid) { +// $site_source = node_load($platform->clone_nid); +// $git_branch = $site_source->git_branch; +// $create_branch = TRUE; +// } } // If the platform has been verified and has a branch and git url else { - drush_log(dt("[DEVSHOP] Existing Repo found at !root. Checking out branch !branch", array('!branch' => $platform->git_branch, '!root' => $root))); - - $root = $platform->publish_path; - $git_remote = $platform->git_url; - $git_branch = $platform->git_branch; - - //Remove drupal_path to clone. - if ($platform->drupal_path) { - $root = str_replace($platform->drupal_path, '', $root); - } + drush_log(dt("[DEVSHOP] Existing Repo found at !root. Checking out branch !branch", array('!branch' => $git_branch, '!root' => $root))); // Run git fetch to ensure we have all branches and tags $output .= _devshop_projects_git_execute('git fetch', $root); @@ -140,11 +140,11 @@ function drush_devshop_provision_pre_hosting_task_platform_verify(){ if ($create_branch) { //Create branch. - $command = "git checkout -b $platform->git_branch"; + $command = "git checkout -b $git_branch"; $output .= _devshop_projects_git_execute($command, $root); //Push the branch - $command = "git push -u origin $platform->git_branch"; + $command = "git push -u origin $git_branch"; $output .= _devshop_projects_git_execute($command, $root); } @@ -298,7 +298,6 @@ function devshop_projects_post_hosting_verify_task($task, $data) { */ function devshop_projects_hosting_project_context_options(&$task) { - $task->context_options['server'] = '@server_master'; $branches = getBranchesAndTags($task->ref->project->git_url); @@ -313,6 +312,8 @@ function devshop_projects_hosting_project_context_options(&$task) { // Save project object to context. if (isset($task->ref->project)) { + $task->context_options['server'] = '@server_master'; + $task->context_options['project_name'] = $task->ref->title; $task->context_options['project'] = $task->ref->project; } } @@ -326,6 +327,8 @@ function devshop_projects_drush_context_import($context, &$node) { if ($context->type == 'project') { $node->title = $context->project_name; $node->type = $context->type; + + // @TODO: Fix up for $node->project $node->code_path = $context->code_path; $node->drupal_path = $context->drupal_path; $node->base_url = $context->base_url; @@ -393,36 +396,6 @@ function getBranchesAndTags($git_url = NULL){ return array('branches' => $branches, 'tags' => $tags); } -/** - * Implements hook_hosting_site_context_options() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_site_context_options(&$task) { - $task->context_options['project'] = $task->ref->project; - $task->context_options['nerd'] = 'vision'; -} - -/** - * Implements hook_hosting_site_context_options() - * - * This transfers data from the node to the aegir context object (the alias!) - * For site entities. - */ -function devshop_projects_hosting_platform_context_options(&$task) { - if (!empty($task->ref->project)){ - $task->context_options['project'] = $task->ref->project; - $task->properties['task properties'] = 'works'; - $task->ref->properties['task ref properties'] = 'works'; - - d()->setProperty('setProperty', 'works'); - - // @TODO: none of these work --^ - } -} - - /** * Utility for execute git commands. */ diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 169ce1a20..5253b8687 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -243,7 +243,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $additions['environment'] = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_devshop_project} p on e.project_nid = p.nid where {$node->type} = %d", $node->nid)); + $additions['environment'] = db_fetch_object(db_query("SELECT * FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_devshop_project} p on e.project_nid = p.nid where {$node->type} = %d", $node->nid)); return $additions; } } From f6e2e8a4b14b363ba7269e62dcb935cef87daeb7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 17:42:25 -0400 Subject: [PATCH 0748/3476] Trying to avoid chdir() notice. --- devshop_projects/devshop_projects.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 6eccc295e..f76e04665 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -107,7 +107,7 @@ function drush_devshop_platform_verify(){ } // Check if the platform code exists. If it doesn't, clone it. - if (!is_dir($root) || !drush_shell_cd_and_exec($root, 'git status')) { + if (!is_dir($root)) { drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $git_remote, '!root' => $root))); // Build the command string From 786888eb8f0c25fb901212cd8d4ca8eea36c0fff Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 17:44:01 -0400 Subject: [PATCH 0749/3476] better comments. --- devshop_projects/inc/nodes.inc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 5253b8687..922396436 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -233,10 +233,7 @@ function devshop_projects_load($node) { * Implementation of hook_nodeapi() * * Handle project information for Platforms and Sites: - * $node->project : The context name of the project. - * $node->environment: The type of environment this platform or site is: - * can be dev, test, or live. - * $node->git_branch: The current git branch the project lives on. + * $node->environment: All information about this environment. */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { From e8d0f399f1a5d9872ea48d6c409e68efdf340e96 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 17:52:07 -0400 Subject: [PATCH 0750/3476] Fixing display of project, branch, and environment for sites and platforms. --- devshop_projects/inc/nodes.inc | 2 +- devshop_projects/inc/ui.inc | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 922396436..fb7e1827e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -240,7 +240,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $additions['environment'] = db_fetch_object(db_query("SELECT * FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_devshop_project} p on e.project_nid = p.nid where {$node->type} = %d", $node->nid)); + $additions['environment'] = db_fetch_object(db_query("SELECT e.*, n.title AS project FROM {hosting_devshop_project_environment} e LEFT JOIN {node} n ON e.project_nid = n.nid WHERE {$node->type} = %d", $node->nid)); return $additions; } } diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index f78cb12c8..fd843a7e8 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -343,26 +343,25 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { * Implements hook_nodeapi_TYPE_OP() */ function devshop_projects_nodeapi_site_view(&$node, $a3, $a4) { - if (!empty($node->project)){ - + if (!empty($node->environment)){ // Display Project, Environment and Branch. $node->content['info']['project'] = array( '#type' => 'item', '#title' => t('Project'), - '#value' => l($node->project, "node/{$node->project_nid}"), + '#value' => l($node->environment->project, "node/{$node->environment->project_nid}"), '#weight' => -12, ); $node->content['info']['env'] = array( '#type' => 'item', '#title' => t('Environment'), - '#value' => $node->environment, + '#value' => $node->environment->environment, '#weight' => -11, ); $node->content['info']['branch'] = array( '#type' => 'item', - '#title' => t('Branch'), - '#value' => $node->git_branch, + '#title' => t('Branch/Tag'), + '#value' => $node->environment->git_ref, '#weight' => -11, ); From 90013fd7f5cfd959b2695bdab675f46576aac28b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:04:31 -0400 Subject: [PATCH 0751/3476] Moving and fixing finish wizard callback. --- devshop_projects/inc/create-wizard.inc | 101 ++++++++++--------------- 1 file changed, 41 insertions(+), 60 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 94107938a..71d389c80 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -144,46 +144,6 @@ function devshop_projects_create_wizard_next(&$form_state) { } -/** - * FINISH callback - * Callback generated when the add page process is finished. - * this is where you'd normally save. - */ -function devshop_projects_create_wizard_finish(&$form_state) { - global $user; - - $project = &$form_state['project']; - - // Save the extra options to the project node. - $node = node_load($project->project_nid); - $node->code_path = $project->code_path; - $node->base_url = $project->base_url; - $node->install_profile = $project->install_profile; - $node->live_domain = $project->live_domain; - $node->drupal_path = $project->drupal_path; - $node->uid = $user->uid; - $node->status = 1; - $node->no_verify = TRUE; - node_save($node); - - // Create the site nodes - // @TODO: Can we speed things up here by only running install for the first, - // then "Cloning" to create the rest? - foreach ($node->project->environments as $environment_name => $environment) { - // @TODO: Does this set the http_server as well?? Doesn't look like it. - $db_server = $environment['db_server']; - devshop_projects_create_site($node, node_load($environment['platform']), $environment_name, $db_server); - } - - ctools_object_cache_clear('project', $form_state['cache name']); - $form_state['redirect'] = 'node/' . $node->nid; - - // Removing last step session variable. - unset($_SESSION['last_step']); - - // Friendly message - drupal_set_message(t('Your project has been created. Once installed, your sites will be available.')); -} /** @@ -472,16 +432,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; $project->no_next = TRUE; - // If we are all queued, show a friendly message - if ($project->verify_task_status == HOSTING_TASK_QUEUED){ - $task = node_load($project->verify_task_nid); - $time_ago = time() - $task->updated; - if ($time_ago > 60) { - // @TODO: This doesn't work quite right yet. - // $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; - } - } - $form['note'] = array( '#type' => 'markup', '#value' => $note, @@ -801,13 +751,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $project->no_finish = TRUE; $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; - // If we are all queued, show a friendly message - - $time_ago = time() - $task->created; - if ($all_tasks_queued && $time_ago > 120) { - $note .= '

' . t('Your tasks have been queued for %time. You should check your Hosting Task Queue.', array('%time' => format_interval($time_ago))) . '

'; - } - $form['help'] = array( '#type' => 'markup', '#value' => $note, @@ -817,8 +760,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { return $form; } // If no available profiles: - // @TODO: This fails sometimes because the list of install profiles is not quite available. - elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; @@ -842,7 +783,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $default_profile = 'drupal'; } - // @TODO: Handle no available profiles $form['install_profile'] = array( '#type' => 'radios', '#options' => $available_profiles, @@ -877,6 +817,47 @@ function devshop_project_create_step_sites_submit(&$from, &$form_state) { } +/** + * FINISH callback + * Callback generated when the add page process is finished. + */ +function devshop_projects_create_wizard_finish(&$form_state) { + + $project = &$form_state['project']; + + // Save the extra options to the project node. + $node = node_load($project->project_nid); + $node->project->install_profile = $project->install_profile; + $node->no_verify = TRUE; + + // Create the site nodes + // @TODO: Can we speed things up here by only running install for the first, + // then "Cloning" to create the rest? + dsm($node, 'node for environments'); + + foreach ($node->project->environments as $environment_name => &$environment) { + // @TODO: Does this set the http_server as well?? Doesn't look like it. + $db_server = $environment['db_server']; + $site_node = devshop_projects_create_site($node, node_load($environment['platform']), $environment_name, $db_server); + $environment['site'] = $site->nid; + } + + node_save($node); + + + ctools_object_cache_clear('project', $form_state['cache name']); + $form_state['redirect'] = 'node/' . $node->nid; + + // Removing last step session variable. + unset($_SESSION['last_step']); + + // Friendly message + drupal_set_message(t('Your project has been created. Your sites are being installed.')); +} + +/** + * Returns JSON showing the state of the project + */ function devshop_projects_add_status($type = 'platform'){ $return = array(); From c75d97dffa2a9b24d603f5e6d6c3db317087ce00 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:11:58 -0400 Subject: [PATCH 0752/3476] loading project into platform/site nodes. --- devshop_projects/devshop_projects.drush.inc | 11 +++++++---- devshop_projects/inc/nodes.inc | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index f76e04665..72817f159 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -26,7 +26,7 @@ function drush_devshop_projects_pre_hosting_task() { // Verify Platform // For our platforms, we have to clone it if it has a git_remote // If it has a git branch, we should checkout as well. - if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->environment->git_url)) { + if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->project->git_url)) { drush_devshop_platform_verify(); } @@ -95,15 +95,18 @@ function drush_devshop_platform_verify(){ $platform = $task->ref; $root = $platform->publish_path; - $git_remote = $platform->environment->git_url; $git_branch = $platform->environment->git_ref; + + $git_remote = $platform->project->git_url; + $drupal_path = $platform->project->drupal_path; + $create_branch = FALSE; $output = ''; //Remove drupal_path to clone. - if ($platform->environment->drupal_path) { - $root = str_replace($platform->environment->drupal_path, '', $root); + if ($drupal_path) { + $root = str_replace($drupal_path, '', $root); } // Check if the platform code exists. If it doesn't, clone it. diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index fb7e1827e..cc78640f7 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -241,6 +241,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); $additions['environment'] = db_fetch_object(db_query("SELECT e.*, n.title AS project FROM {hosting_devshop_project_environment} e LEFT JOIN {node} n ON e.project_nid = n.nid WHERE {$node->type} = %d", $node->nid)); + + $additions['project'] = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $additions['environment']->project_nid)); + + $additions['environment']->data = unserialize($additions['environment']->data); + $additions['project']->data = unserialize($additions['project']->data); + return $additions; } } From 0f2429e41c29f25c2cfb34be628f041d37a097bc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:22:13 -0400 Subject: [PATCH 0753/3476] Fixing site installation to use new project object. --- devshop_projects/devshop_projects.module | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 08a2584e9..fa48ec727 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -366,7 +366,7 @@ function devshop_projects_hosting_drush_aliases_name($node) { * Helper function to create a site in a project. * Used by Wizard & "Create Platform" > Post Verify */ -function devshop_projects_create_site($project_node, $platform_node, $platform_name, $db_server = NULL) { +function devshop_projects_create_site($project_node, $platform_node, $environment_name, $db_server = NULL) { global $user; // Create the site nodes @@ -374,7 +374,7 @@ function devshop_projects_create_site($project_node, $platform_node, $platform_n $node->type = 'site'; $node->status = 1; $node->uid = $user->uid; - $node->title = $platform_name .'.'. $project_node->base_url; + $node->title = $environment_name .'.'. $project_node->project->base_url; // Aegir properties // @TODO: better client & DB support @@ -386,11 +386,11 @@ function devshop_projects_create_site($project_node, $platform_node, $platform_n $node->platform = $platform_node->nid; // Lookup this platforms install profile - $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->install_profile)); + $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->project->install_profile)); - $node->environment = $platform_name; - $node->git_branch = $platform_node->git_branch; - $node->project = $project_node->title; +// $node->environment = $environment_name; +// $node->git_branch = $platform_node->git_branch; +// $node->project = $project_node->title; // @TODO: Improve site language handling? $node->site_language = !empty($user->language)? $user->language: 'en'; From e193bf4556c3bfb1c242fc4429cc6c237a9ce9ad Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:26:50 -0400 Subject: [PATCH 0754/3476] Fixing saving of an environments sites on project creation. --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 71d389c80..f4be061a1 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -839,7 +839,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { // @TODO: Does this set the http_server as well?? Doesn't look like it. $db_server = $environment['db_server']; $site_node = devshop_projects_create_site($node, node_load($environment['platform']), $environment_name, $db_server); - $environment['site'] = $site->nid; + $environment['site'] = $site_node->nid; } node_save($node); From e8a6ac2c2e993a4b6df24b5be83b07d3319a4a3c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:43:31 -0400 Subject: [PATCH 0755/3476] Use $project_node for consistency. Adding no verify. --- devshop_projects/inc/create-wizard.inc | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f4be061a1..175437a71 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -412,6 +412,7 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { $project_node->project->drupal_path = $form_state['values']['drupal_path']; $project_node->project->base_url = $form_state['values']['base_url']; + $project_node->no_verify; node_save($project_node); } @@ -634,6 +635,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Somehow, NEW environment still appears! unset($project_node->project->environments['NEW']); + $project_node->no_verify = TRUE; node_save($project_node); } @@ -824,29 +826,30 @@ function devshop_project_create_step_sites_submit(&$from, &$form_state) { function devshop_projects_create_wizard_finish(&$form_state) { $project = &$form_state['project']; + $project_node = node_load($project->project_nid); // Save the extra options to the project node. - $node = node_load($project->project_nid); - $node->project->install_profile = $project->install_profile; - $node->no_verify = TRUE; + $project_node->project->install_profile = $project->install_profile; - // Create the site nodes + // Create the site nodes, saving to the environment. // @TODO: Can we speed things up here by only running install for the first, // then "Cloning" to create the rest? - dsm($node, 'node for environments'); + dsm($project_node, 'node for environments'); - foreach ($node->project->environments as $environment_name => &$environment) { + foreach ($project_node->project->environments as $environment_name => &$environment) { // @TODO: Does this set the http_server as well?? Doesn't look like it. $db_server = $environment['db_server']; - $site_node = devshop_projects_create_site($node, node_load($environment['platform']), $environment_name, $db_server); + $site_node = devshop_projects_create_site($project_node, node_load($environment['platform']), $environment_name, $db_server); $environment['site'] = $site_node->nid; } - node_save($node); - + // Set to not verify and to publish. + $project_node->no_verify = TRUE; + $project_node->status = 1; + node_save($project_node); ctools_object_cache_clear('project', $form_state['cache name']); - $form_state['redirect'] = 'node/' . $node->nid; + $form_state['redirect'] = 'node/' . $project_node->nid; // Removing last step session variable. unset($_SESSION['last_step']); From b7bc97893a5e5e6da537be3d723e283492294809 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:44:02 -0400 Subject: [PATCH 0756/3476] removing dsm --- devshop_projects/inc/create-wizard.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 175437a71..638d87eb5 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -834,8 +834,6 @@ function devshop_projects_create_wizard_finish(&$form_state) { // Create the site nodes, saving to the environment. // @TODO: Can we speed things up here by only running install for the first, // then "Cloning" to create the rest? - dsm($project_node, 'node for environments'); - foreach ($project_node->project->environments as $environment_name => &$environment) { // @TODO: Does this set the http_server as well?? Doesn't look like it. $db_server = $environment['db_server']; From 88560af7da96db0f7aa4bdbe802836a8eab7f569 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:45:20 -0400 Subject: [PATCH 0757/3476] moving hook_load to the top since its the most used. --- devshop_projects/inc/nodes.inc | 80 +++++++++++++++++----------------- devshop_projects/inc/ui.inc | 39 ++++++++--------- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index cc78640f7..685955022 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -24,6 +24,47 @@ function devshop_projects_node_info() { return $types; } + +/** + * Implementation of hook_load(). + * + * Loads data for project and environments. + * + * @param node + * Node object + */ +function devshop_projects_load($node) { + + // Load the "hosting context". The unique name in the aegir system. + $additions['hosting_name'] = 'project_' . db_result(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); + + // Load project data + $project_data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); + + // Load up all project settings, merging db table and serialized data. + $data = $project_data['data']; + unset($project_data['data']); + $project = (object) array_merge((array) $project_data, (array) unserialize($data)); + + // @TODO: Create a good status system. + // Set status + // $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); + + // Load Environments Data + $query = db_query("SELECT * FROM {hosting_devshop_project_environment} WHERE project_nid = %d ORDER BY environment", $node->nid); + $environments = array(); + while ($environment_data = db_fetch_array($query)) { + $environment_data['data'] = unserialize($environment_data['data']); + $environments[$environment_data['environment']] = $environment_data; + } + $project->environments = $environments; + + // Save project object be available at $node->project. + $additions['project'] = $project; + + return $additions; +} + /** * Implementation of hook_insert(). * @@ -188,45 +229,6 @@ function devshop_projects_delete($node) { hosting_context_delete($node->nid); } -/** - * Implementation of hook_load(). - * - * Loads data for project and environments. - * - * @param node - * Node object - */ -function devshop_projects_load($node) { - - // Load the "hosting context". The unique name in the aegir system. - $additions['hosting_name'] = 'project_' . db_result(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); - - // Load project data - $project_data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); - - // Load up all project settings, merging db table and serialized data. - $data = $project_data['data']; - unset($project_data['data']); - $project = (object) array_merge((array) $project_data, (array) unserialize($data)); - - // @TODO: Create a good status system. - // Set status - // $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); - - // Load Environments Data - $query = db_query("SELECT * FROM {hosting_devshop_project_environment} WHERE project_nid = %d ORDER BY environment", $node->nid); - $environments = array(); - while ($environment_data = db_fetch_array($query)) { - $environment_data['data'] = unserialize($environment_data['data']); - $environments[$environment_data['environment']] = $environment_data; - } - $project->environments = $environments; - - // Save project object be available at $node->project. - $additions['project'] = $project; - - return $additions; -} /** diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index fd843a7e8..33f12568d 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -21,41 +21,41 @@ function devshop_projects_projects_page() { 'Environments', ); - $r = db_query("SELECT hdp.* FROM {hosting_devshop_project} hdp LEFT JOIN {node} n ON hdp.nid = n.nid WHERE n.status = 1 ORDER BY n.title"); + $query = db_query("SELECT n.nid FROM {hosting_devshop_project} p LEFT JOIN {node} n ON n.nid = p.nid WHERE n.status = 1 ORDER BY n.changed"); $rows = array(); - while(($proj = db_fetch_object($r))) { - $node = node_load($proj->nid); - if (!empty($node->project_objects['platform'])){ - $platform_node = node_load(key($node->project_objects['platform'])); - } + while ($result = db_fetch_object($query)) { + $project_node = node_load($result->nid); + $project = $project_node->project; + + // Load an environment for extra info like version. + // @TODO: load the chosen live environment. + $environment = array_pop($project->environments); + $platform_node = node_load($environment->platform); $row = array(); // Link to Project page - $row[] = '' . l($node->title, "node/$proj->nid") . ''; + $row[] = '' . l($project->title, "node/$project->nid") . ''; // Install Profile - $row[] = $node->install_profile; + $row[] = $project->install_profile; // Drupal Version $row[] = $platform_node->release->version; - // Number of environments - $num = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} hdpo LEFT JOIN {hosting_site} hs ON hdpo.object_nid = hs.nid WHERE hdpo.project_nid = %d AND hdpo.object_type='site' AND hs.status=1", $node->nid)); - $row[] = format_plural($num, t('1 site'), t('!num sites', array('!num' => $num))); - // Git URL - $row[] = strtr("", array('!url' => $node->git_url)); + $row[] = strtr("", array('!url' => $project->git_url)); // Links to all sites $actions = array(); $count = 0; - foreach ($node->project->environments as $env => $details){ - $dev_site_url = url("http://$env.$node->base_url", array('absolute' => TRUE)); + foreach ($project->environments as $env => $details){ + // @TODO: Handle aliases. + $site_url = url("http://$env.$project->base_url", array('absolute' => TRUE)); $actions[] = array( - 'title' => $dev_site_url, - 'href' => $dev_site_url, + 'title' => $site_url, + 'href' => $site_url, 'attributes' => array('target' => '_blank'), ); $count++; @@ -63,7 +63,7 @@ function devshop_projects_projects_page() { $label = format_plural($count, t('1 Environment'), t('@num Environments', array('@num' => $count))); $row[] = theme('ctools_dropdown', $label, $actions); - $rows[$proj->nid] = $row; + $rows[] = $row; } // No Projects @@ -344,7 +344,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { */ function devshop_projects_nodeapi_site_view(&$node, $a3, $a4) { if (!empty($node->environment)){ - // Display Project, Environment and Branch. $node->content['info']['project'] = array( '#type' => 'item', @@ -369,7 +368,7 @@ function devshop_projects_nodeapi_site_view(&$node, $a3, $a4) { $crumbs = array(); $crumbs[] = l(t('Home'), ''); $crumbs[] = l(t('Projects'), 'hosting/projects'); - $crumbs[] = l($node->project, "node/" . $node->project_nid); + $crumbs[] = l($node->environment->project, "node/" . $node->project_nid); drupal_set_breadcrumb($crumbs); } } From ed5791a7d94296418d5c0535cb81042cd6d40dcc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 18:50:15 -0400 Subject: [PATCH 0758/3476] Improving node_load for projects. --- devshop_projects/inc/nodes.inc | 17 +++++++++-------- devshop_projects/inc/ui.inc | 3 ++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 685955022..3f6cf1872 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -39,26 +39,27 @@ function devshop_projects_load($node) { $additions['hosting_name'] = 'project_' . db_result(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); // Load project data - $project_data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); + $project = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); // Load up all project settings, merging db table and serialized data. - $data = $project_data['data']; - unset($project_data['data']); - $project = (object) array_merge((array) $project_data, (array) unserialize($data)); + $project->data = unserialize($project->data); // @TODO: Create a good status system. // Set status // $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); - // Load Environments Data + // Load Environments $query = db_query("SELECT * FROM {hosting_devshop_project_environment} WHERE project_nid = %d ORDER BY environment", $node->nid); $environments = array(); - while ($environment_data = db_fetch_array($query)) { - $environment_data['data'] = unserialize($environment_data['data']); - $environments[$environment_data['environment']] = $environment_data; + while ($environment = db_fetch_object($query)) { + $environment->data = unserialize($environment->data); + $environments[$environment->environment] = $environment; } $project->environments = $environments; + // Make project name available. + $project->name = $node->title; + // Save project object be available at $node->project. $additions['project'] = $project; diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 33f12568d..7ead808f3 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -32,11 +32,12 @@ function devshop_projects_projects_page() { // @TODO: load the chosen live environment. $environment = array_pop($project->environments); $platform_node = node_load($environment->platform); + dsm($environment); $row = array(); // Link to Project page - $row[] = '' . l($project->title, "node/$project->nid") . ''; + $row[] = '' . l($project->name, "node/$project->nid") . ''; // Install Profile $row[] = $project->install_profile; From ed62c773fdb658f15100ed3040d85da70926b408 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 19:08:12 -0400 Subject: [PATCH 0759/3476] Making environments objects. Fixing serialized data for good. --- devshop_projects/inc/nodes.inc | 56 ++++++++++++++++------------------ 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3f6cf1872..0d4bd840e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -110,9 +110,11 @@ function devshop_projects_insert($node) { $info = new stdClass(); $info->project_nid = $node->nid; $info->environment = $env; - $info->git_ref = $environment['git_ref']; - $info->site = $environment['site']; - $info->platform = $environment['platform']; + $info->git_ref = $environment->git_ref; + $info->site = $environment->site; + $info->platform = $environment->platform; + + unset($environment->data); $info->data = serialize($environment); drupal_write_record('hosting_devshop_project_environment', $info); @@ -133,53 +135,49 @@ function devshop_projects_update($node) { hosting_add_task($node->nid, 'verify'); } + $project = $node->project; + $info = new stdClass(); $info->nid = $node->nid; - $info->git_url = $node->project->git_url; - $info->code_path = hosting_path_normalize($node->project->code_path); - $info->drupal_path = hosting_path_normalize($node->project->drupal_path); - $info->base_url = $node->project->base_url; - $info->install_profile = $node->project->install_profile; - - $environments = $node->project->environments; + $info->git_url = $project->git_url; + $info->code_path = hosting_path_normalize($project->code_path); + $info->drupal_path = hosting_path_normalize($project->drupal_path); + $info->base_url = $project->base_url; + $info->install_profile = $project->install_profile; - // Save serialized data, minus environments - $data = $node->project; - unset($data->environments); - $info->data = serialize($data); + // Save serialized data + $info->data = serialize($project->data); + // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); - // If using environment aliases... save them. - devshop_project_save_domain_aliases($node); - - - // Save Environment records. - if (!empty($environments)) { + if (!empty($project->environments )) { // Delete existing environment records db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); // Save each environment - foreach ($environments as $env => $environment){ + foreach ($project->environments as $name => $environment){ $info = new stdClass(); $info->project_nid = $node->nid; - $info->environment = $env; - $info->git_ref = $environment['git_ref']; - $info->site = $environment['site']; - $info->platform = $environment['platform']; + $info->environment = $name; + $info->git_ref = $environment->git_ref; + $info->site = $environment->site; + $info->platform = $environment->platform; - unset($environment->data); - $info->data = serialize($environment); + $info->data = serialize($environment->data); if (drupal_write_record('hosting_devshop_project_environment', $info)){ - drupal_set_message(t('Environment %name saved.', array('%name' => $env))); + drupal_set_message(t('Environment %name saved.', array('%name' => $name))); } else { - drupal_set_message(t('Something went wrong saving environment %name.', array('%name' => $env)), 'error'); + drupal_set_message(t('Something went wrong saving environment %name.', array('%name' => $name)), 'error'); } } } + + // If using environment aliases... save them. + devshop_project_save_domain_aliases($node); } From 137151409c799d3e06484f46761ac3dbc3fc25f5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 19:09:09 -0400 Subject: [PATCH 0760/3476] Making environments objects. --- devshop_projects/inc/create-wizard.inc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 638d87eb5..eedc18ad9 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -614,7 +614,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $environment['platform'] = $platform_node->nid; - $project_node->project->environments[$name] = $environment; + $project_node->project->environments[$name] = (object) $environment; } // @TODO: Put this back once creation works. @@ -669,7 +669,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { foreach ($project_node->project->environments as $name => $environment){ // Get platform and latest verify task. - $platform_nid = $environment['platform']; + $platform_nid = $environment->platform; $platform = node_load($platform_nid); $task = hosting_get_most_recent_task($platform_nid, 'verify'); @@ -836,9 +836,9 @@ function devshop_projects_create_wizard_finish(&$form_state) { // then "Cloning" to create the rest? foreach ($project_node->project->environments as $environment_name => &$environment) { // @TODO: Does this set the http_server as well?? Doesn't look like it. - $db_server = $environment['db_server']; - $site_node = devshop_projects_create_site($project_node, node_load($environment['platform']), $environment_name, $db_server); - $environment['site'] = $site_node->nid; + $db_server = $environment->db_server; + $site_node = devshop_projects_create_site($project_node, node_load($environment->platform), $environment_name, $db_server); + $environment->site = $site_node->nid; } // Set to not verify and to publish. From 9e0fc3c9d77cf63735b05e9bd1032966f39825e7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 19:09:25 -0400 Subject: [PATCH 0761/3476] Fixing UI pages to work with new projects and environments. --- devshop_projects/inc/ui.inc | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 7ead808f3..28e13e1f3 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -16,7 +16,6 @@ function devshop_projects_projects_page() { 'Name', 'Profile', 'Version', - '', 'Git URL', 'Environments', ); @@ -26,13 +25,13 @@ function devshop_projects_projects_page() { while ($result = db_fetch_object($query)) { $project_node = node_load($result->nid); + dsm($project_node, 'project node'); $project = $project_node->project; // Load an environment for extra info like version. // @TODO: load the chosen live environment. - $environment = array_pop($project->environments); + $environment = array_shift(array_values($project->environments)); $platform_node = node_load($environment->platform); - dsm($environment); $row = array(); @@ -51,6 +50,7 @@ function devshop_projects_projects_page() { // Links to all sites $actions = array(); $count = 0; + dsm($project); foreach ($project->environments as $env => $details){ // @TODO: Handle aliases. $site_url = url("http://$env.$project->base_url", array('absolute' => TRUE)); @@ -100,6 +100,7 @@ HTML; */ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { global $user; + $project = $node->project; // Check to see if this project is still in the wizard ctools_include('object-cache'); @@ -122,8 +123,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // '#weight' => -8 //); - if (!empty($node->live_domain)) { - $url = 'http://' . $node->live_domain; + if (!empty($project->live_domain)) { + $url = 'http://' . $project->live_domain; $node->content['info']['live_domain'] = array( '#type' => 'item', @@ -136,13 +137,13 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->content['info']['git_url'] = array( '#type' => 'item', '#title' => t('Git URL'), - '#value' => strtr("", array('!url' => $node->git_url)), + '#value' => strtr("", array('!url' => $project->git_url)), '#weight' => -10 ); // Branches display - if (!empty($node->project->git['branches'])){ - $items = theme_item_list($node->project->git['branches'], NULL, 'ul', array('class' => 'branches')); + if (!empty($project->data->git['branches'])){ + $items = theme_item_list($project->data->git['branches'], NULL, 'ul', array('class' => 'branches')); } else { $items = theme_item_list(array('No Branches Found!'), NULL, 'ul', array('class' => 'branches')); @@ -162,21 +163,22 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#weight' => -8, ); - if (!empty($node->install_profile)){ + // Install profile display + if (!empty($project->install_profile)){ $node->content['info']['install_profile'] = array( '#type' => 'item', '#title' => t('Install profile'), - '#value' => ($node->install_profile), + '#value' => ($project->install_profile), '#weight' => -7 ); } // Environments $rows = array(); - foreach ($node->project->environments as $env => $environment) { + foreach ($project->environments as $env => $environment) { - $site_nid = $environment['site']; - $platform_nid = $environment['platform']; + $site_nid = $environment->site; + $platform_nid = $environment->platform; $site = node_load($site_nid); $platform = node_load($platform_nid); @@ -197,7 +199,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } // Display current branch - $row[] = "$site->git_branch"; + $row[] = "{$environment->git_ref}"; // Create branch/tag chooser // @TODO: Uncomment once we have a link to go to. @@ -385,8 +388,8 @@ function devshop_projects_nodeapi_platform_view(&$node, $a3, $a4) { * Our own version of _hosting_site_goto_link() */ function devshop_hosting_site_goto_link($node) { - $project = node_load($node->project_nid); - $url = "{$node->environment}.{$project->base_url}"; +// $project = node_load($node->environment->project_nid); + $url = "{$node->environment->environment}.{$node->project->base_url}"; $cache = cache_get("hosting:site:" . $node->nid . ":login_link"); if (!is_null($cache) && (time() < $cache->data['expire'])) { $title = t("Log in: !url", array('!url' => $url)); From 13bf7004debb64ce9a9bf54c5eab6d1051ae5047 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 19:09:56 -0400 Subject: [PATCH 0762/3476] removing dsms --- devshop_projects/inc/ui.inc | 2 -- 1 file changed, 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 28e13e1f3..e8e32c618 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -25,7 +25,6 @@ function devshop_projects_projects_page() { while ($result = db_fetch_object($query)) { $project_node = node_load($result->nid); - dsm($project_node, 'project node'); $project = $project_node->project; // Load an environment for extra info like version. @@ -50,7 +49,6 @@ function devshop_projects_projects_page() { // Links to all sites $actions = array(); $count = 0; - dsm($project); foreach ($project->environments as $env => $details){ // @TODO: Handle aliases. $site_url = url("http://$env.$project->base_url", array('absolute' => TRUE)); From 798951467ec8c9937af4244af887383b2530f1e9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 5 Apr 2014 19:49:01 -0400 Subject: [PATCH 0763/3476] We moved git to $project->data. --- devshop_projects/devshop_projects.drush.inc | 4 ++-- devshop_projects/devshop_projects.module | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 72817f159..1e388d385 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -306,8 +306,8 @@ function devshop_projects_hosting_project_context_options(&$task) { // If something went wrong connecting to the git repo, don't wipe out our branches. if (!empty($branches['branches'])){ - $task->ref->project->git['branches'] = $branches['branches']; - $task->ref->project->git['tags'] = $branches['tags']; + $task->ref->project->data->git['branches'] = $branches['branches']; + $task->ref->project->data->git['tags'] = $branches['tags']; // Save the project node now that we have branches and tags. node_save($task->ref); diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index fa48ec727..8f587f140 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -138,17 +138,19 @@ function devshop_projects_menu() { /** * Implements hook_devshop_project_settings */ -function devshop_projects_devshop_project_settings($project_node = NULL){ +function devshop_projects_devshop_project_settings($node = NULL){ + $project = $node->project; + // Build branch options - if (is_array($project_node->project->git['branches']) && !empty($project_node->project->git['branches'])) { + if (is_array($project->data->git['branches']) && !empty($project->data->git['branches'])) { $branch_options = array( - 'Branches' => array_combine($project_node->project->git['branches'], $project_node->project->git['branches']), + 'Branches' => array_combine($project->data->git['branches'], $project->data->git['branches']), ); } // If there are tags... - if (is_array($project_node->project->git['tags']) && !empty($project_node->project->git['tags'])) { - $branch_options['Tags'] = array_combine($project_node->project->git['tags'], $project_node->project->git['tags']); + if (is_array($node->project->git['tags']) && !empty($node->project->git['tags'])) { + $branch_options['Tags'] = array_combine($node->project->git['tags'], $node->project->git['tags']); } // If there are none... From fc7973756ef55d82ff98e161b3bbaebc545cf7c8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 7 Apr 2014 13:59:53 -0400 Subject: [PATCH 0764/3476] Creating a function for getting branch/tag options. --- devshop_projects/devshop_projects.module | 43 ++++++++++++++---------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 8f587f140..1ecedaa92 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -141,23 +141,6 @@ function devshop_projects_menu() { function devshop_projects_devshop_project_settings($node = NULL){ $project = $node->project; - // Build branch options - if (is_array($project->data->git['branches']) && !empty($project->data->git['branches'])) { - $branch_options = array( - 'Branches' => array_combine($project->data->git['branches'], $project->data->git['branches']), - ); - } - - // If there are tags... - if (is_array($node->project->git['tags']) && !empty($node->project->git['tags'])) { - $branch_options['Tags'] = array_combine($node->project->git['tags'], $node->project->git['tags']); - } - - // If there are none... - if (empty($branch_options['Tags']) && empty($branch_options['Branches'])){ - $branch_options = array(t('No branches or tags! Re-validate the project.')); - } - $settings = array(); $settings['site'] = array( '#node_type' => 'site', @@ -171,7 +154,7 @@ function devshop_projects_devshop_project_settings($node = NULL){ '#title' => t('Git Branch/Tag'), '#node_type' => 'platform', '#type' => 'select', - '#options' => $branch_options, + '#options' => devshop_projects_git_options($project), ); $http_servers = hosting_get_servers('http'); @@ -404,6 +387,30 @@ function devshop_projects_create_site($project_node, $platform_node, $environmen return $node; } +/** + * Helper to get select #options for git ref. + */ +function devshop_projects_git_options($project){ + // Build branch options + if (is_array($project->data->git['branches']) && !empty($project->data->git['branches'])) { + $options = array( + 'Branches' => array_combine($project->data->git['branches'], $project->data->git['branches']), + ); + } + + // If there are tags... + if (is_array($project->git['tags']) && !empty($project->git['tags'])) { + $options['Tags'] = array_combine($project->git['tags'], $project->git['tags']); + } + + // If there are none... + if (empty($options['Tags']) && empty($options['Branches'])){ + $options = array(t('No branches or tags! Re-validate the project.')); + } + + return $options; +} + /** * Nodemaker */ From b4a1a32d06d145c5536d92589317f21ff35e6acf Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 22:24:15 -0400 Subject: [PATCH 0765/3476] Removing unused project status function. --- devshop_projects/devshop_projects.module | 53 ------------------------ devshop_projects/inc/nodes.inc | 4 -- 2 files changed, 57 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index fa48ec727..2076fbaa7 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -296,60 +296,7 @@ function devshop_hosting_task_menu_access($node, $task) { } } -/** - * Status of Project - * - * @TODO: When creating a new platform for an existing project, the status - * goes back to "platforms not-verified" - * - * @param $node - * a project node - */ -function devshop_project_status($node){ - - //Project status - $platforms_ready = TRUE; - $sites_ready = TRUE; - $sites_installing = FALSE; - // PROJECT STATUS - if (empty($node->project_objects) && empty($node->git_branches)){ - return 'preparing-project'; - } - - // PLATFORMS STATUS: Determine if all platforms are verified. - if (isset($node->project_objects['platform'])){ - foreach ($node->project_objects['platform'] as $nid => $env){ - $platform_nodes[$env] = node_load($nid); - if (!empty($platform_nodes[$env]) && $platform_nodes[$env]->platform_status == 0){ - $platforms_ready = FALSE; - } - } - } else { - $platforms_ready = FALSE; - } - // SITES STATUS: Determine if sites exist and are enabled - if ($platforms_ready && isset($node->project_objects['site'])){ - - foreach ($node->project_objects['site'] as $nid => $env){ - $site_nodes[$env] = node_load($nid); - if ($site_nodes[$env]->site_status == 0){ - $sites_ready = FALSE; - $sites_installing = TRUE; - } - } - } else { - $sites_ready = FALSE; - } - - - // @TODO: This is so rough! We can do better. - return $sites_ready? 'sites_ready': ( - $sites_installing? 'sites_installing': ( - $platforms_ready? 'platforms_ready': 'platforms_verifying' - ) - ); -} /** * Implements hook_hosting_drush_aliases_name() diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 0d4bd840e..73e3b4b82 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -44,10 +44,6 @@ function devshop_projects_load($node) { // Load up all project settings, merging db table and serialized data. $project->data = unserialize($project->data); - // @TODO: Create a good status system. - // Set status - // $project->status = devshop_project_status((object) (array_merge($additions, (array) $node))); - // Load Environments $query = db_query("SELECT * FROM {hosting_devshop_project_environment} WHERE project_nid = %d ORDER BY environment", $node->nid); $environments = array(); From 23f8ef448725b9b1691267445299adcac052b51d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 22:30:35 -0400 Subject: [PATCH 0766/3476] Removing project object from site_has_module --- devshop_projects/devshop_projects.module | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 2076fbaa7..403292f4b 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -369,9 +369,9 @@ function _devshop_projects_node_create($type, $node = stdClass){ /** * Check if a site has features diff enabled. */ -function _devshop_projects_site_has_module($node, $module) { +function _devshop_projects_site_has_module($nid, $module) { $param = array( - 'rid' => $node->nid, + 'rid' => $nid, 'p.short_name' => $module, ); $package = hosting_package_instance_load($param); @@ -382,12 +382,8 @@ function _devshop_projects_site_has_module($node, $module) { * Check if a site has features diff enabled. */ function _devshop_projects_project_has_module($node, $module) { - if (is_array($node->project_objects['site'])){ - $sites = array_flip($node->project_objects['site']); - } else { - $sites = array(); - } - return _devshop_projects_site_has_module(node_load($sites['dev']), $module); + $environment = key($node->project->environments); + return _devshop_projects_site_has_module($node->project->environments[$environment]['site'], $module); } /* From b551fdffb916eccb7648bf0a57046c7178f13204 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 22:34:12 -0400 Subject: [PATCH 0767/3476] Replacing project object n environment form. --- devshop_projects/inc/tasks.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index e77de777d..545e3fccd 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -57,9 +57,8 @@ function devshop_projects_hosting_tasks() { * Helper to provide a select list of environments for this project */ function devshop_projects_tasks_add_environment_to_form(&$form, $node, $description, $key = 'environment', $title = 'Environment', $type = 'radios') { - // @TODO: Add a check here. Sometimes we want to limit this list. - - $options = array_combine($node->project_objects['site'], $node->project_objects['site']); + $keys = array_keys($node->project->environments); + $options = array_combine($keys, $keys); $form[$key] = array( '#type' => $type, '#title' => t($title), From 1c35342d56c558956ca480e76283196fd7e48fa0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 22:34:27 -0400 Subject: [PATCH 0768/3476] Removing deletion from project object row. --- devshop_projects/inc/nodes.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 73e3b4b82..803917d37 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -218,9 +218,6 @@ function devshop_projects_delete($node) { db_query('DELETE FROM {hosting_devshop_project} WHERE nid = %d', $node->nid); db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); - // @TODO: Remove once table is deleted. - db_query('DELETE FROM {hosting_devshop_project_object} WHERE project_nid = %d', $node->nid); - hosting_context_delete($node->nid); } From af12fe1fbc58f06cf4dd21f826f2fc4399940a2c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 22:46:56 -0400 Subject: [PATCH 0769/3476] creating helper function to generate select options from git branches. --- devshop_projects/devshop_projects.module | 39 +++++++++++++----------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 403292f4b..e07ab5a62 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -139,22 +139,6 @@ function devshop_projects_menu() { * Implements hook_devshop_project_settings */ function devshop_projects_devshop_project_settings($project_node = NULL){ - // Build branch options - if (is_array($project_node->project->git['branches']) && !empty($project_node->project->git['branches'])) { - $branch_options = array( - 'Branches' => array_combine($project_node->project->git['branches'], $project_node->project->git['branches']), - ); - } - - // If there are tags... - if (is_array($project_node->project->git['tags']) && !empty($project_node->project->git['tags'])) { - $branch_options['Tags'] = array_combine($project_node->project->git['tags'], $project_node->project->git['tags']); - } - - // If there are none... - if (empty($branch_options['Tags']) && empty($branch_options['Branches'])){ - $branch_options = array(t('No branches or tags! Re-validate the project.')); - } $settings = array(); $settings['site'] = array( @@ -169,7 +153,7 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ '#title' => t('Git Branch/Tag'), '#node_type' => 'platform', '#type' => 'select', - '#options' => $branch_options, + '#options' => devshop_project_git_ref_options($project_node->project), ); $http_servers = hosting_get_servers('http'); @@ -195,6 +179,27 @@ function devshop_projects_devshop_project_settings($project_node = NULL){ return $settings; } +/** + * Generates options list for git branch/ref selector + */ +function devshop_project_git_ref_options($project){ + if (is_array($project->git['branches']) && !empty($project->git['branches'])) { + $options = array( + 'Branches' => array_combine($project->git['branches'], $project->git['branches']), + ); + } + + // If there are tags... + if (is_array($project->git['tags']) && !empty($project->git['tags'])) { + $options['Tags'] = array_combine($project->git['tags'], $project->git['tags']); + } + + // If there are none... + if (empty($options['Tags']) && empty($options['Branches'])){ + $options = array(t('No branches or tags! Re-validate the project.')); + } + return $options; +} /** * Page Callback for hosting/projects/add From 6364d0d8c748d9615826ac015fa9d3d93386944d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 22:50:50 -0400 Subject: [PATCH 0770/3476] Fixing pull code task to use project->environments. --- devshop_pull/devshop_pull.inc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 2294e17d7..d2cf17d90 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -206,13 +206,15 @@ function _devshop_pull_hash_create($node) { * Platforms in a project must be enabled to have this command run on them. */ function devshop_pull_project($project_nid) { - // Search platforms with pull enabled for this project - $results = db_query("SELECT environment FROM {hosting_devshop_pull_platforms} p LEFT JOIN {hosting_devshop_project_object} o ON p.platform_nid = o.object_nid WHERE pull_enabled = 1 AND p.project_nid = %d", $project_nid); + + $project_node = node_load($project_nid); $args = array('environments' => ''); - while ($info = db_fetch_object($results)){ - $args['environments'] .= $info->environment .' '; + foreach ($project_node->project->environments as $name => $environment){ + if (!$environment->data->pull_disabled){ + $args['environments'] .= $environment->environment .' '; + } } $args['environments'] = trim($args['environments']); if (!empty($args['environments'])){ From a3330644546524bb7356326f5a7ba2c77eac7ce4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 23:23:03 -0400 Subject: [PATCH 0771/3476] create project page padding. --- devshop.css | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop.css b/devshop.css index 41257b4f6..7c7becc6d 100644 --- a/devshop.css +++ b/devshop.css @@ -32,6 +32,7 @@ fieldset.project-environments { } .empty { text-align: center; + min-height: 165px; } .empty .empty-message { padding:30px; From dbabcc27bd53383edb3c62d8de3a3b5ce4e7c1f7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 23:24:11 -0400 Subject: [PATCH 0772/3476] Fixing error in project creation. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 803917d37..464bc6dc6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -194,7 +194,7 @@ function devshop_project_save_domain_aliases($project_node) { } } // If live_domain_aliases is checked, remove them from the nodes. - elseif (!$project_node->live_domain_aliases){ + elseif (!$project_node->live_domain_aliases && count($project_node->project->environments)){ foreach ($project_node->project->environments as $env => $details){ $domain = "$env.$project_node->live_domain"; $site_node = node_load($details['site_nid']); From 2b20bb8114eac00e77adb1ca74a16142e03700b5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 23:30:05 -0400 Subject: [PATCH 0773/3476] fixing bad merge --- devshop_projects/devshop_projects.module | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 883e9de50..7bf208ab6 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -154,7 +154,7 @@ function devshop_projects_devshop_project_settings($node = NULL){ '#title' => t('Git Branch/Tag'), '#node_type' => 'platform', '#type' => 'select', - '#options' => devshop_project_git_ref_options($project), + '#options' => devshop_projects_git_ref_options($project), ); $http_servers = hosting_get_servers('http'); @@ -180,28 +180,6 @@ function devshop_projects_devshop_project_settings($node = NULL){ return $settings; } -/** - * Generates options list for git branch/ref selector - */ -function devshop_project_git_ref_options($project){ - if (is_array($project->git['branches']) && !empty($project->git['branches'])) { - $options = array( - 'Branches' => array_combine($project->git['branches'], $project->git['branches']), - ); - } - - // If there are tags... - if (is_array($project->git['tags']) && !empty($project->git['tags'])) { - $options['Tags'] = array_combine($project->git['tags'], $project->git['tags']); - } - - // If there are none... - if (empty($options['Tags']) && empty($options['Branches'])){ - $options = array(t('No branches or tags! Re-validate the project.')); - } - return $options; -} - /** * Page Callback for hosting/projects/add */ From 3217817892fef769bca340cc53bdcf91ac115b91 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 23:54:23 -0400 Subject: [PATCH 0774/3476] Fixing create wizard. --- devshop_projects/inc/create-wizard.inc | 25 ++++++++++++++++--------- devshop_projects/inc/nodes.inc | 4 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index eedc18ad9..d35aaef25 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -466,10 +466,10 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); // Ensure a blank row exists (happens when using 'Back' button) - if (!is_array($project_node->project->environments['NEW'])){ - $project_node->project->environments['NEW'] = array(); + if (!is_array($project->environments['NEW'])){ + $project->environments['NEW'] = array(); } - foreach ($project_node->project->environments as $env => $env_settings) { + foreach ($project->environments as $env => $env_settings) { // No platforms exist yet if ($env == 'NEW') { $env_title = ''; @@ -491,6 +491,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#maxlength' => 64, '#attributes' => array( 'placeholder' => t('name'), + 'autofocus' => 'autofocus', ), '#field_prefix' => 'http://', '#field_suffix' => "." . $project_node->project->base_url, @@ -575,14 +576,14 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { foreach ($form_state['values']['project']['environments'] as $name => $environment) { // If platform exists, it's because user went back in the wizard. - $platform_nid = $project_node->project->environments[$name]['platform']; + $platform_nid = $project_node->project->environments[$name]->platform; // If the platform already exists, save it again with new settings. if ($platform_nid) { $platform_node = node_load($platform_nid); foreach ($settings as $setting_name => $element){ if ($element['#node_type'] == 'platform'){ - $platform_node->{$setting_name} = $environment[$setting_name]; + $platform_node->{$setting_name} = $environment->{$setting_name}; } } node_save($platform_node); @@ -591,14 +592,14 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { else { $platform = new stdClass; - $platform->title = $project_node->title . '_' . $environment['title']; + $platform->title = $project_node->title . '_' . $name; // Platform publish_path if (!empty($project_node->project->drupal_path)) { - $platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; + $platform->publish_path = $project_node->project->code_path . '/' . $environment->title . '/' . $project_node->project->drupal_path; } else { - $platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; + $platform->publish_path = $project_node->project->code_path . '/' . $environment->title; } // Other attributes @@ -932,9 +933,15 @@ function devshop_projects_create_wizard_add_new_environment($form, &$form_state) // All we are doing here is saving the project to cache. // devshop_project_create_step_environments_validate() handles putting data in $project. - $project = &$form_state['project']; + $project = $form_state['project']; + + // Save environment + $project->environments = &$form_state['values']['project']['environments']; + ctools_object_cache_set('project', NULL, $project); + + // Go back to the same page. drupal_goto('hosting/projects/add/environments'); } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 464bc6dc6..fc7824329 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -195,9 +195,9 @@ function devshop_project_save_domain_aliases($project_node) { } // If live_domain_aliases is checked, remove them from the nodes. elseif (!$project_node->live_domain_aliases && count($project_node->project->environments)){ - foreach ($project_node->project->environments as $env => $details){ + foreach ($project_node->project->environments as $env => $environment){ $domain = "$env.$project_node->live_domain"; - $site_node = node_load($details['site_nid']); + $site_node = node_load($environment->site); if (!empty($site_node->aliases)){ $i = array_search($domain, $site_node->aliases); if ($i !== FALSE) { From a1e1d62bb95066349baa296662a88df4eb389cef Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 8 Apr 2014 23:57:55 -0400 Subject: [PATCH 0775/3476] missed another environment object. --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index d35aaef25..26e93d127 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -882,7 +882,7 @@ function devshop_projects_add_status($type = 'platform'){ // When checking platforms... if ($type == 'platform') { foreach ($project_node->project->environments as $name => $environment){ - $nids[] = $environment['platform']; + $nids[] = $environment->platform; } } @@ -909,7 +909,7 @@ function devshop_projects_add_status($type = 'platform'){ */ function devshop_form_reloader(&$form, $type = 'platform'){ // Add JS that reloads the page when tasks finish. - $form[$element_id] = array( + $form['item'] = array( '#type' => 'item', '#value' => '', '#weight' => 10 From e8eda5efe97289e9d081cec6289f2317848e2633 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 00:08:50 -0400 Subject: [PATCH 0776/3476] Platforms only need verification when being updated. --- devshop_projects/inc/create-wizard.inc | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 26e93d127..a2424c668 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -578,15 +578,9 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // If platform exists, it's because user went back in the wizard. $platform_nid = $project_node->project->environments[$name]->platform; - // If the platform already exists, save it again with new settings. + // If the platform already exists, verify it again. if ($platform_nid) { - $platform_node = node_load($platform_nid); - foreach ($settings as $setting_name => $element){ - if ($element['#node_type'] == 'platform'){ - $platform_node->{$setting_name} = $environment->{$setting_name}; - } - } - node_save($platform_node); + hosting_add_task($platform_nid, 'verify'); } // If platform hasn't been created yet, do so now! else { @@ -935,13 +929,10 @@ function devshop_projects_create_wizard_add_new_environment($form, &$form_state) // devshop_project_create_step_environments_validate() handles putting data in $project. $project = $form_state['project']; - // Save environment + // Save environment to project cache. $project->environments = &$form_state['values']['project']['environments']; - ctools_object_cache_set('project', NULL, $project); - - // Go back to the same page. drupal_goto('hosting/projects/add/environments'); } From 9183de31db724bea2ba3c01e17217c03a0bf850f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 00:10:02 -0400 Subject: [PATCH 0777/3476] Adding more literal messaging. --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index a2424c668..f34265c05 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -746,7 +746,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Not completed means show all tasks are not completed (or errored) if (!$completed){ $project->no_finish = TRUE; - $note = '

' . t('Please wait while we download and verify your drupal code.') . '

'; + $note = '

' . t('Please wait while we clone your repo and verify your drupal code.') . '

'; $form['help'] = array( '#type' => 'markup', From de5424bdf1228fbb1b2cea1826653246792b0250 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 00:19:09 -0400 Subject: [PATCH 0778/3476] Fixing edit page. --- devshop_projects/inc/forms.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e74b0cb3b..1083434a2 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -163,10 +163,10 @@ function devshop_projects_form(&$node) { ); foreach ($settings as $setting_id => $setting){ $form['project']['environments'][$environment_name][$setting_id] = $setting; - $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $node->project->environments[$environment_name][$setting_id]; + $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $node->project->environments[$environment_name]->{$setting_id}; $form['project']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } - $site = node_load($environment['site']); + $site = node_load($environment->site); $live_alias_options[$environment_name] = $site->hosting_name; } $form['#submit'] = array('devshop_projects_submit_settings'); @@ -174,11 +174,11 @@ function devshop_projects_form(&$node) { // Save git branches and tags $form['project']['git']['branches'] = array( '#type' => 'value', - '#value' => $node->project->git['branches'], + '#value' => $node->project->data->git['branches'], ); $form['project']['git']['tags'] = array( '#type' => 'value', - '#value' => $node->project->git['tags'], + '#value' => $node->project->data->git['tags'], ); From 1c5b79b5a84da25ec83682007f63622ef242cd93 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 00:21:46 -0400 Subject: [PATCH 0779/3476] environment objects. --- devshop_projects/inc/forms.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 1083434a2..d1318cc49 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -257,7 +257,7 @@ function devshop_projects_form(&$node) { function devshop_projects_submit_settings($form, &$form_state) { // Go through and save our settings to site and platform nodes. -// $project_node = node_load($form_state['values']['nid']); + $project_node = node_load($form_state['values']['nid']); $settings_info = module_invoke_all('devshop_project_settings', $project_node); $nodes = array(); @@ -271,8 +271,8 @@ function devshop_projects_submit_settings($form, &$form_state) { $node_type = $settings_info[$setting_name]['#node_type']; // Load the site or platform node for this environment, then set the value and save. - if (!empty($project_node->project->environments[$environment_name][$node_type])){ - $nid = $project_node->project->environments[$environment_name][$node_type]; + if (!empty($project_node->project->environments[$environment_name]->{$node_type})){ + $nid = $project_node->project->environments[$environment_name]->{$node_type}; $nodes[$nid] = node_load($nid); $nodes[$nid]->no_verify = TRUE; From 7aee50c6a376d98963819bfa98d7d3e706652907 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 00:26:05 -0400 Subject: [PATCH 0780/3476] moving git to data property. --- devshop_projects/inc/forms.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index d1318cc49..1ffe826fb 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -172,16 +172,15 @@ function devshop_projects_form(&$node) { $form['#submit'] = array('devshop_projects_submit_settings'); // Save git branches and tags - $form['project']['git']['branches'] = array( + $form['project']['data']['git']['branches'] = array( '#type' => 'value', '#value' => $node->project->data->git['branches'], ); - $form['project']['git']['tags'] = array( + $form['project']['data']['git']['tags'] = array( '#type' => 'value', '#value' => $node->project->data->git['tags'], ); - // Don't allow editing if ($node->nid) { $locked = array('title', 'git_url', 'code_path', 'drupal_path', 'base_url'); From 520f1dfd1d15e590d7557a33e9e43abe174e4f9b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 21:42:23 -0400 Subject: [PATCH 0781/3476] Providing $environment->name. --- devshop_projects/inc/nodes.inc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index fc7824329..01f95bc9d 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -49,6 +49,11 @@ function devshop_projects_load($node) { $environments = array(); while ($environment = db_fetch_object($query)) { $environment->data = unserialize($environment->data); + + // @TODO: Remove when we change the environment schema to be "name" + $environment->name = $environment->environment; + + // Save to environments array $environments[$environment->environment] = $environment; } $project->environments = $environments; @@ -241,6 +246,8 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $additions['environment']->data = unserialize($additions['environment']->data); $additions['project']->data = unserialize($additions['project']->data); + // @TODO: Remove once we change environment schema. + $additions['environment']->name = $additions['environment']->environment; return $additions; } } From 3d784f80d6aff27055bce8b6fd6e98ce7159b8d1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 21:43:35 -0400 Subject: [PATCH 0782/3476] Fixing create environment form for new environment objects. --- devshop_projects/inc/tasks.inc | 89 ++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 545e3fccd..2a13d00c0 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -75,60 +75,58 @@ function devshop_projects_tasks_add_environment_to_form(&$form, $node, $descript * For "devshop-create" task for creating a platform and site. */ function hosting_task_devshop_create_form($node) { - $branch_options = array_combine($node->git_branches, $node->git_branches); - - $site_nid = arg(3); - $site = NULL; - - if ($site_nid && is_numeric($site_nid)) { - //Sanitize data. - $site_nid = check_plain($site_nid); - - //Check is a valid project site. - if (!empty($node->project_objects['site'][$site_nid])) { - $site = node_load($site_nid); + global $user; + $project = $node->project; + $branch_options = devshop_projects_git_ref_options($project); + + dsm($project, 'project'); + // Look for Site to Fork + // @TODO: Do this with hook_menu? + $site_to_fork = arg(3); + if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { + if (!empty($project->environments[$site->environment->environment])){ + + $form['branch_source'] = array( + '#type' => 'item', + '#title' => t('Fork from'), + '#value' => t('!env environment on branch !branch', array('!env' => "{$site->environment->name}", '!branch' => "{$site->environment->git_ref}")), + ); + $form['fork_source'] = array( + '#type' => 'hidden', + '#value' => $site->environment->name, + ); + $form['new_branch'] = array( + '#title' => t('New branch name'), + '#description' => t('Pick a name for your new git branch.'), + '#type' => 'textfield', + '#size' => 60, + '#maxlength' => 128, + '#required' => TRUE, + ); } - } - - if ($site) { - $form['branch_source'] = array( - '#type' => 'item', - '#title' => t('Fork from'), - '#value' => t('!env environment on branch !branch', array('!env' => "$site->environment", '!branch' => "$site->git_branch")), - ); - $form['fork_source'] = array( - '#type' => 'hidden', - '#value' => $site->environment, - ); - $form['new_branch'] = array( - '#title' => t('New branch name'), - '#description' => t('Choose the new Git branch you with to use for this new platform.'), - '#type' => 'textfield', - '#size' => 60, - '#maxlength' => 128, - '#required' => TRUE, - ); } + else { $form['branch'] = array( - '#title' => t('Branch'), - '#description' => t('Choose the Git branch you with to use for this new platform. Note: If you do not see all remote branches, You can !link', array('!link' => l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link modalframe-exclude', 'target' => '_parent'), 'query' => array('token' => drupal_get_token($user->uid)))))), + '#title' => t('Branch/Tag'), + '#description' => t('Choose the git branch or tag you wish to use for this new environment. Note: If you do not see all remote branches, You can !link', array('!link' => l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link modalframe-exclude', 'target' => '_parent'), 'query' => array('token' => drupal_get_token($user->uid)))))), '#type' => 'select', '#options' => $branch_options, '#required' => TRUE, ); } - // @TODO: Add "create branch" functionality. $form['environment_name'] = array( - '#title' => t('Environment Name'), + '#title' => t('New Environment Name'), '#type' => 'textfield', - '#description' => t('Enter a system name for your environment. For consistency, you should make this match the branch name.'), + '#description' => t('Enter a system name for your environment. For consistency, you might want to match the branch name.'), '#required' => TRUE, '#field_prefix' => 'http://', - '#field_suffix' => ".$node->base_url", + '#field_suffix' => "." . $node->project->base_url, '#size' => 6, '#maxlength' => 64, ); + + // @TODO: I don't think this is needed. hosting_task_devshop_create_form_submit() looks for nid, these values end up in $form_state['values']['paramters']; $form['project_nid'] = array( '#type' => 'value', '#value' => $node->nid, @@ -142,13 +140,20 @@ function hosting_task_devshop_create_form($node) { function hosting_task_devshop_create_form_validate($form, &$form_state){ $params = $form_state['values']['parameters']; - // Check for existing environments for this project. - $object_nid = db_result(db_query('SELECT object_nid FROM {hosting_devshop_project_object} d LEFT JOIN {hosting_platform} h ON d.object_nid = h.nid WHERE project_nid = %d AND object_type = "%s" AND environment = "%s" AND status != %d', $params['project_nid'], 'platform', $params['environment_name'], HOSTING_PLATFORM_DELETED)); - - if ($object_nid) { + // Load Project + $node = node_load($form_state['values']['nid']); + $project = $node->project; + + // Check existence of the environment name + if (!empty($project->environments[$params['environment_name']])){ form_set_error('environment_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['environment_name']))); } + // Check existence of the git branch + if (in_array($params['new_branch'], $project->data->git['branches'])){ + form_set_error('new_branch', t("The project already has a branch named %name. Choose a git branch name that doesn't already exist.", array('%name' => $params['new_branch']))); + } + // Check for illegal chars if (!preg_match('!^[a-z0-9_]+$!', $params['environment_name'])) { form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); From 24104714c7ac917c5a16f4642cd721d5351a4011 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 21:44:57 -0400 Subject: [PATCH 0783/3476] removing dsm --- devshop_projects/inc/tasks.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/inc/tasks.inc index 2a13d00c0..50317c2a9 100644 --- a/devshop_projects/inc/tasks.inc +++ b/devshop_projects/inc/tasks.inc @@ -79,7 +79,6 @@ function hosting_task_devshop_create_form($node) { $project = $node->project; $branch_options = devshop_projects_git_ref_options($project); - dsm($project, 'project'); // Look for Site to Fork // @TODO: Do this with hook_menu? $site_to_fork = arg(3); From e9ab936c7b6745e7233ce20774edbebb2363e3cb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 21:58:49 -0400 Subject: [PATCH 0784/3476] Moving tasks.inc file. --- devshop_projects/{inc => tasks}/tasks.inc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename devshop_projects/{inc => tasks}/tasks.inc (100%) diff --git a/devshop_projects/inc/tasks.inc b/devshop_projects/tasks/tasks.inc similarity index 100% rename from devshop_projects/inc/tasks.inc rename to devshop_projects/tasks/tasks.inc From ebfa0dc9d7cfcb1b71b678529b4451d288fdc0ee Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:02:21 -0400 Subject: [PATCH 0785/3476] Breaking tasks code into individual files. --- devshop_projects/devshop_projects.module | 9 +- devshop_projects/tasks/commit.inc | 43 +++ devshop_projects/tasks/create.inc | 171 +++++++++++ devshop_projects/tasks/pull.inc | 64 ++++ devshop_projects/tasks/sync.inc | 99 +++++++ devshop_projects/tasks/tasks.inc | 357 ----------------------- 6 files changed, 385 insertions(+), 358 deletions(-) create mode 100644 devshop_projects/tasks/commit.inc create mode 100644 devshop_projects/tasks/create.inc create mode 100644 devshop_projects/tasks/pull.inc create mode 100644 devshop_projects/tasks/sync.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 7bf208ab6..71a1a5d80 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -7,10 +7,17 @@ include_once('inc/forms.inc'); include_once('inc/nodes.inc'); -include_once('inc/tasks.inc'); include_once('inc/theme.inc'); include_once('inc/ui.inc'); +// Tasks Code +include_once('tasks/tasks.inc'); +include_once('tasks/commit.inc'); +include_once('tasks/create.inc'); +include_once('tasks/pull.inc'); +include_once('tasks/sync.inc'); + + /** * Implementation of hook_init() * diff --git a/devshop_projects/tasks/commit.inc b/devshop_projects/tasks/commit.inc new file mode 100644 index 000000000..579290f38 --- /dev/null +++ b/devshop_projects/tasks/commit.inc @@ -0,0 +1,43 @@ +hosting_name/features/diff/all", array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . ' Be patient. It takes a few moments for the diffs to be generatred.'; + } + else { + $descr .= 'enable the Features Diff module for this site, Verify the site, and select this task again.'; + } + + $form['message'] = array( + '#title' => t('Commit Message'), + '#type' => 'textarea', + '#description' => $descr, + ); + $form['push'] = array( + '#title' => t('Push code after commit?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + $form['revert'] = array( + '#title' => t('Force revert features after commit?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + // @TODO: Provide a DIFF display to give the user an idea of what has changed. + return $form; +} diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc new file mode 100644 index 000000000..4017f334b --- /dev/null +++ b/devshop_projects/tasks/create.inc @@ -0,0 +1,171 @@ +project; + $branch_options = devshop_projects_git_ref_options($project); + + // Look for Site to Fork + // @TODO: Do this with hook_menu? + $site_to_fork = arg(3); + if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { + if (!empty($project->environments[$site->environment->environment])){ + + $form['branch_source'] = array( + '#type' => 'item', + '#title' => t('Fork from'), + '#value' => t('!env environment on branch !branch', array('!env' => "{$site->environment->name}", '!branch' => "{$site->environment->git_ref}")), + ); + $form['fork_source'] = array( + '#type' => 'hidden', + '#value' => $site->environment->name, + ); + $form['new_branch'] = array( + '#title' => t('New branch name'), + '#description' => t('Pick a name for your new git branch.'), + '#type' => 'textfield', + '#size' => 60, + '#maxlength' => 128, + '#required' => TRUE, + ); + } + } + + else { + $form['branch'] = array( + '#title' => t('Branch/Tag'), + '#description' => t('Choose the git branch or tag you wish to use for this new environment. Note: If you do not see all remote branches, You can !link', array('!link' => l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link modalframe-exclude', 'target' => '_parent'), 'query' => array('token' => drupal_get_token($user->uid)))))), + '#type' => 'select', + '#options' => $branch_options, + '#required' => TRUE, + ); + } + $form['environment_name'] = array( + '#title' => t('New Environment Name'), + '#type' => 'textfield', + '#description' => t('Enter a system name for your environment. For consistency, you might want to match the branch name.'), + '#required' => TRUE, + '#field_prefix' => 'http://', + '#field_suffix' => "." . $node->project->base_url, + '#size' => 6, + '#maxlength' => 64, + ); + + // @TODO: I don't think this is needed. hosting_task_devshop_create_form_submit() looks for nid, these values end up in $form_state['values']['paramters']; + $form['project_nid'] = array( + '#type' => 'value', + '#value' => $node->nid, + ); + return $form; +} + +/** + * Validation hook for hosting_task_devshop_create_form() + */ +function hosting_task_devshop_create_form_validate($form, &$form_state){ + $params = $form_state['values']['parameters']; + + // Load Project + $node = node_load($form_state['values']['nid']); + $project = $node->project; + + // Check existence of the environment name + if (!empty($project->environments[$params['environment_name']])){ + form_set_error('environment_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['environment_name']))); + } + + // Check existence of the git branch + if (in_array($params['new_branch'], $project->data->git['branches'])){ + form_set_error('new_branch', t("The project already has a branch named %name. Choose a git branch name that doesn't already exist.", array('%name' => $params['new_branch']))); + } + + // Check for illegal chars + if (!preg_match('!^[a-z0-9_]+$!', $params['environment_name'])) { + form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); + } + + if (isset($params['new_branch']) && !preg_match('!^[a-z0-9_-]+$!', $params['new_branch'])) { + form_set_error('new_branch', t('The new branch name must contain only lowercase letters, numbers, dashes and underscores.')); + } +} + +/** + * Extra submit function for hosting_task_confirm_form() + * + * @see devshop_projects_form_alter(). We had to add the submit hadler there. + */ +function hosting_task_devshop_create_form_submit($form, &$form_state) { + + $project = node_load($form_state['values']['nid']); + $environment_name = $form_state['values']['parameters']['environment_name']; + $branch = $form_state['values']['parameters']['new_branch']? $form_state['values']['parameters']['new_branch']: $form_state['values']['parameters']['branch']; + $fork_source = $form_state['values']['parameters']['fork_source']; + + hosting_create_environment($project, $environment_name, $branch, $fork_source); + + // We are replacing hosting_confirm_form_submit here, so just do what it does, + // minus the hosting task creation! + $values = $form_state['values']; + $form_state['redirect'] = 'node/' . $values['nid']; + modalframe_close_dialog(); + + // Friendly message + drupal_set_message(t('Your environment is being created.')); +} + + +/** + * API-level function for creating a new environment. + * + * @TODO: Add more options like web server, etc. + * @param $project + * A full project node (for now. @TODO Allow project name (and/or nid) as parameters. + * @param $environment_name + * A new name for the environment + * @param $branch + * What git branch to track. + * @param $fork_source + * If desired, the environment to fork off of. (Copy the database and create a new branch from) + */ +function hosting_create_environment($project_node, $environment_name, $branch, $fork_source = NULL) { + + $servers = hosting_get_servers('http'); + $server = variable_get('devshop_projects_default_web_server', key($servers)); + + // hosting_platform fields + $platform = new stdClass; + $platform->title = $project_node->title . '_' . $environment_name; + $platform->publish_path = $project->code_path . '/' . $environment_name; + $platform->web_server = $server; + $platform->git_branch = $branch; + $platform->project = $project->title; + $platform->environment = $environment_name; + $platform->drupal_path = $project->drupal_path; + + if ($project->drupal_path) { + $platform->publish_path = $project->code_path . '/' . $environment_name . '/' . $project->drupal_path; + } + + //Check if we are forking another site. + if (isset($project_node->project->environments[$fork_source])) { + $platform->clone_nid = $project_node->project->environments[$fork_source]['site']; + $platform->git_branch = $branch; + $platform->old_branch = $project_node->project->environments[$fork_source]['git_branch']; + } + + watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); + + // Create the platform node. The platform and site creation handle it from there. + $platform_node = _devshop_projects_node_create('platform', $platform); + + return $platform_node; +} diff --git a/devshop_projects/tasks/pull.inc b/devshop_projects/tasks/pull.inc new file mode 100644 index 000000000..c6fe38e29 --- /dev/null +++ b/devshop_projects/tasks/pull.inc @@ -0,0 +1,64 @@ +project_settings['environments'] as $env => $settings){ + if (isset($form['environments']['#options'][$env]) && ($settings['pull_disabled'] || in_array($settings['git_branch'], $node->project_settings['git_tags']))){ + unset($form['environments']['#options'][$env]); + } + } + + // If empty, send them to project page. + if (empty($form['environments']['#options'][$env])){ + drupal_set_message(t('You do not have any environments with Pull Code enabled, or they are all tracking tags. !link to be able to pull code.', array('!link' => l(t('Edit the project settings'), "node/$node->nid/edit")))); + // @TODO: Close modal window instead? + return $form; + } + + $form['update'] = array( + '#title' => t('Run update.php after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + + if (_devshop_projects_project_has_module($node, 'features')){ + $form['revert'] = array( + '#title' => t('Revert all features after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + } + $form['cache'] = array( + '#title' => t('Clear cache after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + + // Add validator for environments + //$form['#validate'] = array('hosting_task_devshop_pull_form'); + return $form; +} + +/** + * Extra submit function for hosting_task_confirm_form() + * + * @see devshop_projects_form_alter(). We had to add the submit hadler there. + */ +function hosting_task_devshop_pull_form_validate($form, &$form_state) { + $value = implode(' ', array_filter($form_state['values']['parameters']['environments'])); + form_set_value($form['parameters']['environments'], $value, $form_state); +} + diff --git a/devshop_projects/tasks/sync.inc b/devshop_projects/tasks/sync.inc new file mode 100644 index 000000000..c4aa01a65 --- /dev/null +++ b/devshop_projects/tasks/sync.inc @@ -0,0 +1,99 @@ +nid; + + $form = array(); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment. Data from this environment will be DESTROYED.'), 'destination', 'Destination'); + + $form['source']['#prefix'] = '
'; + $form['source']['#suffix'] = '
'; + $form['destination']['#prefix'] = '
'; + $form['destination']['#suffix'] = '
'; + + if (!empty($form['destination']['#options'][$node->live_environment])){ + // Remove live site from destination options. + unset($form['destination']['#options'][$node->live_environment]); + + $source_options = $form['source']['#options']; + unset($source_options[$node->live_environment]); + $form['source']['#options'] = array_merge(array($node->live_environment => $node->live_environment . " (LIVE ENVIRONMENT)"), $source_options); + $form['source']['#default_value'] = $node->live_environment; + } + elseif (!empty($node->live_environment_alias)){ + $form['source']['#options']["@" . $node->live_environment_alias] = "@" . $node->live_environment_alias . " (LIVE ENVIRONMENT)"; + } + + $form['database'] = array( + '#title' => t('Copy database from source to destination.'), + '#type' => 'checkbox', + '#default_value' => 1, + '#prefix' => '
What to sync', + ); + + $form['files'] = array( + '#title' => t('Copy files folder from source to destination.'), + '#type' => 'checkbox', + '#default_value' => 0, + '#suffix' => '
', + ); + + + $form['pull'] = array( + '#title' => t('Pull code on Destination'), + '#type' => 'checkbox', + '#default_value' => 1, + '#prefix' => '
Actions', + ); + $form['update'] = array( + '#title' => t('Run update.php on Destination'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + if (_devshop_projects_project_has_module($node, 'features')){ + $form['revert'] = array( + '#title' => t('Revert all features on Destination'), + '#type' => 'checkbox', + '#default_value' => $has_features, + '#access' => $has_features, + ); + } else{ + // I think its better UI just to hide it? If they need features it will be enabled! + //$form['actions']['revert'] = array( + // '#title' => t('Revert all features on Destination after content sync?'), + // '#type' => 'checkbox', + // '#description' => t('This site doesn\'t have features.module enabled. Please enable and then "Verify" the site.'), + // '#default_value' => FALSE, + // '#disabled' => TRUE, + // ); + } + $form['cache'] = array( + '#title' => t('Clear cache on Destination'), + '#type' => 'checkbox', + '#default_value' => 1, + '#suffix' => '
', + ); + $form['#validate'][] = 'hosting_task_devshop_sync_form_validate'; + return $form; +} + +/** + * Validation for hosting_task_devshop_sync_form() + */ +function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ + // Can't sync to self + if ($form_state['values']['parameters']['source'] == $form_state['values']['parameters']['destination']){ + form_set_error('source', t('The source cannot be the same as the destination.')); + } +} \ No newline at end of file diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index 50317c2a9..5b4e17f81 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -69,360 +69,3 @@ function devshop_projects_tasks_add_environment_to_form(&$form, $node, $descript ); } -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "devshop-create" task for creating a platform and site. - */ -function hosting_task_devshop_create_form($node) { - global $user; - $project = $node->project; - $branch_options = devshop_projects_git_ref_options($project); - - // Look for Site to Fork - // @TODO: Do this with hook_menu? - $site_to_fork = arg(3); - if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { - if (!empty($project->environments[$site->environment->environment])){ - - $form['branch_source'] = array( - '#type' => 'item', - '#title' => t('Fork from'), - '#value' => t('!env environment on branch !branch', array('!env' => "{$site->environment->name}", '!branch' => "{$site->environment->git_ref}")), - ); - $form['fork_source'] = array( - '#type' => 'hidden', - '#value' => $site->environment->name, - ); - $form['new_branch'] = array( - '#title' => t('New branch name'), - '#description' => t('Pick a name for your new git branch.'), - '#type' => 'textfield', - '#size' => 60, - '#maxlength' => 128, - '#required' => TRUE, - ); - } - } - - else { - $form['branch'] = array( - '#title' => t('Branch/Tag'), - '#description' => t('Choose the git branch or tag you wish to use for this new environment. Note: If you do not see all remote branches, You can !link', array('!link' => l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link modalframe-exclude', 'target' => '_parent'), 'query' => array('token' => drupal_get_token($user->uid)))))), - '#type' => 'select', - '#options' => $branch_options, - '#required' => TRUE, - ); - } - $form['environment_name'] = array( - '#title' => t('New Environment Name'), - '#type' => 'textfield', - '#description' => t('Enter a system name for your environment. For consistency, you might want to match the branch name.'), - '#required' => TRUE, - '#field_prefix' => 'http://', - '#field_suffix' => "." . $node->project->base_url, - '#size' => 6, - '#maxlength' => 64, - ); - - // @TODO: I don't think this is needed. hosting_task_devshop_create_form_submit() looks for nid, these values end up in $form_state['values']['paramters']; - $form['project_nid'] = array( - '#type' => 'value', - '#value' => $node->nid, - ); - return $form; -} - -/** - * Validation hook for hosting_task_devshop_create_form() - */ -function hosting_task_devshop_create_form_validate($form, &$form_state){ - $params = $form_state['values']['parameters']; - - // Load Project - $node = node_load($form_state['values']['nid']); - $project = $node->project; - - // Check existence of the environment name - if (!empty($project->environments[$params['environment_name']])){ - form_set_error('environment_name', t('%name is already in use. Your environment name must be unique within the project.', array('%name' => $params['environment_name']))); - } - - // Check existence of the git branch - if (in_array($params['new_branch'], $project->data->git['branches'])){ - form_set_error('new_branch', t("The project already has a branch named %name. Choose a git branch name that doesn't already exist.", array('%name' => $params['new_branch']))); - } - - // Check for illegal chars - if (!preg_match('!^[a-z0-9_]+$!', $params['environment_name'])) { - form_set_error('type', t('The environment name must contain only lowercase letters, numbers, and underscores.')); - } - - if (isset($params['new_branch']) && !preg_match('!^[a-z0-9_-]+$!', $params['new_branch'])) { - form_set_error('new_branch', t('The new branch name must contain only lowercase letters, numbers, dashes and underscores.')); - } -} - -/** - * Extra submit function for hosting_task_confirm_form() - * - * @see devshop_projects_form_alter(). We had to add the submit hadler there. - */ -function hosting_task_devshop_create_form_submit($form, &$form_state) { - - $project = node_load($form_state['values']['nid']); - $environment_name = $form_state['values']['parameters']['environment_name']; - $branch = $form_state['values']['parameters']['new_branch']? $form_state['values']['parameters']['new_branch']: $form_state['values']['parameters']['branch']; - $fork_source = $form_state['values']['parameters']['fork_source']; - - hosting_create_environment($project, $environment_name, $branch, $fork_source); - - // We are replacing hosting_confirm_form_submit here, so just do what it does, - // minus the hosting task creation! - $values = $form_state['values']; - $form_state['redirect'] = 'node/' . $values['nid']; - modalframe_close_dialog(); - - // Friendly message - drupal_set_message(t('Your environment is being created.')); -} - - -/** - * API-level function for creating a new environment. - * - * @TODO: Add more options like web server, etc. - * @param $project - * A full project node (for now. @TODO Allow project name (and/or nid) as parameters. - * @param $environment_name - * A new name for the environment - * @param $branch - * What git branch to track. - * @param $fork_source - * If desired, the environment to fork off of. (Copy the database and create a new branch from) - */ -function hosting_create_environment($project_node, $environment_name, $branch, $fork_source = NULL) { - - $servers = hosting_get_servers('http'); - $server = variable_get('devshop_projects_default_web_server', key($servers)); - - // hosting_platform fields - $platform = new stdClass; - $platform->title = $project_node->title . '_' . $environment_name; - $platform->publish_path = $project->code_path . '/' . $environment_name; - $platform->web_server = $server; - $platform->git_branch = $branch; - $platform->project = $project->title; - $platform->environment = $environment_name; - $platform->drupal_path = $project->drupal_path; - - if ($project->drupal_path) { - $platform->publish_path = $project->code_path . '/' . $environment_name . '/' . $project->drupal_path; - } - - //Check if we are forking another site. - if (isset($project_node->project->environments[$fork_source])) { - $platform->clone_nid = $project_node->project->environments[$fork_source]['site']; - $platform->git_branch = $branch; - $platform->old_branch = $project_node->project->environments[$fork_source]['git_branch']; - } - - watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); - - // Create the platform node. The platform and site creation handle it from there. - $platform_node = _devshop_projects_node_create('platform', $platform); - - return $platform_node; -} - - -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Commit" task. - */ -function hosting_task_devshop_commit_form($node) { - - $form = array(); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to commit features from.')); - - $descr = 'A message describing this commit. Too see a diff output off all of the features, '; - - if (_devshop_projects_site_has_module($node, 'features_diff')) { - $descr .= 'click ' . l(t('here.'), "http://$node->hosting_name/features/diff/all", array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . ' Be patient. It takes a few moments for the diffs to be generatred.'; - } - else { - $descr .= 'enable the Features Diff module for this site, Verify the site, and select this task again.'; - } - - $form['message'] = array( - '#title' => t('Commit Message'), - '#type' => 'textarea', - '#description' => $descr, - ); - $form['push'] = array( - '#title' => t('Push code after commit?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - $form['revert'] = array( - '#title' => t('Force revert features after commit?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - // @TODO: Provide a DIFF display to give the user an idea of what has changed. - return $form; -} - -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Pull Code" task. - */ -function hosting_task_devshop_pull_form($node) { - - $form = array(); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environments to pull code to. NOTE: Environments that are tracking a tag or have pull disabled are not listed.'), 'environments', 'Environments', 'checkboxes'); - - // Remove any environments where pull is disabled or is tracking a tag. - foreach ($node->project_settings['environments'] as $env => $settings){ - if (isset($form['environments']['#options'][$env]) && ($settings['pull_disabled'] || in_array($settings['git_branch'], $node->project_settings['git_tags']))){ - unset($form['environments']['#options'][$env]); - } - } - - // If empty, send them to project page. - if (empty($form['environments']['#options'][$env])){ - drupal_set_message(t('You do not have any environments with Pull Code enabled, or they are all tracking tags. !link to be able to pull code.', array('!link' => l(t('Edit the project settings'), "node/$node->nid/edit")))); - // @TODO: Close modal window instead? - return $form; - } - - $form['update'] = array( - '#title' => t('Run update.php after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - - if (_devshop_projects_project_has_module($node, 'features')){ - $form['revert'] = array( - '#title' => t('Revert all features after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - } - $form['cache'] = array( - '#title' => t('Clear cache after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - - // Add validator for environments - //$form['#validate'] = array('hosting_task_devshop_pull_form'); - return $form; -} - -/** - * Extra submit function for hosting_task_confirm_form() - * - * @see devshop_projects_form_alter(). We had to add the submit hadler there. - */ -function hosting_task_devshop_pull_form_validate($form, &$form_state) { - $value = implode(' ', array_filter($form_state['values']['parameters']['environments'])); - form_set_value($form['parameters']['environments'], $value, $form_state); -} - - -/** - * Implementation of hook_hosting_task_TASK_TYPE_form(). - * - * For "Sync Content" task. - */ -function hosting_task_devshop_sync_form($node) { - // Get nid - $nid = $node->nid; - - $form = array(); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment. Data from this environment will be DESTROYED.'), 'destination', 'Destination'); - - $form['source']['#prefix'] = '
'; - $form['source']['#suffix'] = '
'; - $form['destination']['#prefix'] = '
'; - $form['destination']['#suffix'] = '
'; - - if (!empty($form['destination']['#options'][$node->live_environment])){ - // Remove live site from destination options. - unset($form['destination']['#options'][$node->live_environment]); - - $source_options = $form['source']['#options']; - unset($source_options[$node->live_environment]); - $form['source']['#options'] = array_merge(array($node->live_environment => $node->live_environment . " (LIVE ENVIRONMENT)"), $source_options); - $form['source']['#default_value'] = $node->live_environment; - } - elseif (!empty($node->live_environment_alias)){ - $form['source']['#options']["@" . $node->live_environment_alias] = "@" . $node->live_environment_alias . " (LIVE ENVIRONMENT)"; - } - - $form['database'] = array( - '#title' => t('Copy database from source to destination.'), - '#type' => 'checkbox', - '#default_value' => 1, - '#prefix' => '
What to sync', - ); - - $form['files'] = array( - '#title' => t('Copy files folder from source to destination.'), - '#type' => 'checkbox', - '#default_value' => 0, - '#suffix' => '
', - ); - - - $form['pull'] = array( - '#title' => t('Pull code on Destination'), - '#type' => 'checkbox', - '#default_value' => 1, - '#prefix' => '
Actions', - ); - $form['update'] = array( - '#title' => t('Run update.php on Destination'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - if (_devshop_projects_project_has_module($node, 'features')){ - $form['revert'] = array( - '#title' => t('Revert all features on Destination'), - '#type' => 'checkbox', - '#default_value' => $has_features, - '#access' => $has_features, - ); - } else{ - // I think its better UI just to hide it? If they need features it will be enabled! - //$form['actions']['revert'] = array( - // '#title' => t('Revert all features on Destination after content sync?'), - // '#type' => 'checkbox', - // '#description' => t('This site doesn\'t have features.module enabled. Please enable and then "Verify" the site.'), - // '#default_value' => FALSE, - // '#disabled' => TRUE, - // ); - } - $form['cache'] = array( - '#title' => t('Clear cache on Destination'), - '#type' => 'checkbox', - '#default_value' => 1, - '#suffix' => '
', - ); - $form['#validate'][] = 'hosting_task_devshop_sync_form_validate'; - return $form; -} - -/** - * Validation for hosting_task_devshop_sync_form() - */ -function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ - // Can't sync to self - if ($form_state['values']['parameters']['source'] == $form_state['values']['parameters']['destination']){ - form_set_error('source', t('The source cannot be the same as the destination.')); - } -} From 70fe145748aea7226f0948e614b1532db42892ec Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:03:11 -0400 Subject: [PATCH 0786/3476] comments cleanup. --- devshop_projects/tasks/tasks.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index 5b4e17f81..593dabf25 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -1,8 +1,7 @@ Date: Wed, 9 Apr 2014 22:09:22 -0400 Subject: [PATCH 0787/3476] Adding comments. --- devshop_projects/devshop_projects.drush.inc | 13 ++++++------- devshop_projects/tasks/pull.inc | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 1e388d385..4081a53c4 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -1,19 +1,17 @@ ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->project->git_url)) { drush_devshop_platform_verify(); } diff --git a/devshop_projects/tasks/pull.inc b/devshop_projects/tasks/pull.inc index c6fe38e29..2df78326e 100644 --- a/devshop_projects/tasks/pull.inc +++ b/devshop_projects/tasks/pull.inc @@ -8,6 +8,8 @@ * Implementation of hook_hosting_task_TASK_TYPE_form(). * * For "Pull Code" task. + * + * @see drush_devshop_projects_pre_hosting_task() */ function hosting_task_devshop_pull_form($node) { From 77f83fe5380d708f4f8c94f341305dc7e9472a83 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:17:21 -0400 Subject: [PATCH 0788/3476] Adding comments about adding environment status. --- devshop_projects/inc/nodes.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 01f95bc9d..3a2a99cd7 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -248,6 +248,8 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // @TODO: Remove once we change environment schema. $additions['environment']->name = $additions['environment']->environment; + + // @TODO: Determine environment status from site status. return $additions; } } From c9497e1b37fa8125cced11229b78186b465b21b8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:24:04 -0400 Subject: [PATCH 0789/3476] Make status available in project object. --- devshop_projects/inc/nodes.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3a2a99cd7..fdf12b5f6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -58,8 +58,9 @@ function devshop_projects_load($node) { } $project->environments = $environments; - // Make project name available. + // Make project name and status available. $project->name = $node->title; + $project->status = $node->status; // Save project object be available at $node->project. $additions['project'] = $project; @@ -241,7 +242,7 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $additions = array(); $additions['environment'] = db_fetch_object(db_query("SELECT e.*, n.title AS project FROM {hosting_devshop_project_environment} e LEFT JOIN {node} n ON e.project_nid = n.nid WHERE {$node->type} = %d", $node->nid)); - $additions['project'] = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $additions['environment']->project_nid)); + $additions['project'] = db_fetch_object(db_query('SELECT p.*, n.status FROM {hosting_devshop_project} p LEFT JOIN {node} n ON p.nid = n.nid WHERE n.nid = %d', $additions['environment']->project_nid)); $additions['environment']->data = unserialize($additions['environment']->data); $additions['project']->data = unserialize($additions['project']->data); From e8068b9a6763f886397c7f7e003d89b9b93f5c81 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:31:49 -0400 Subject: [PATCH 0790/3476] Fixing drush hooks to use environment objects. --- devshop_projects/devshop_projects.drush.inc | 78 ++++++--------------- 1 file changed, 21 insertions(+), 57 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 4081a53c4..2f2e7578d 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -154,29 +154,27 @@ function drush_devshop_platform_verify(){ } /** - * Implementation of hook_post_hosting_TASK_TYPE_task - * - * Runs after project delete + * Implementation of hook_post_hosting_TASK_TYPE_task() for delete tasks. */ function devshop_projects_post_hosting_delete_task($task, $data) { + + // When a project is deleted... if ($task->ref->type == 'project') { - // We just trigger site deletion here. - // The Post Deletion hook must take care of platforms, since this must finish first. - $project = $task->ref; - if (!empty($project->project_objects['site'])) { - foreach ($project->project_objects['site'] as $nid => $name) { - hosting_add_task($nid, 'delete'); - } + // Queue site deletion for each environment. + $project = $task->ref->project; + foreach ($project->environments as $environment){ + hosting_add_task($environment->site, 'delete'); } // @TODO: Should probably add our own status column - // The last step set status = 0 project node + // Unpublish the project node. $task->ref->status = 0; $task->ref->no_verify = TRUE; node_save($task->ref); } - // When a site is deleted... delete the platform + // When a site is deleted (if it is in a project, delete the platform it is on. + // @TODO: Check for another site on this platform? if ($task->ref->type == 'site' && !empty($task->ref->project)) { // We trigger platform deletion here. hosting_add_task($task->ref->platform, 'delete'); @@ -185,35 +183,10 @@ function devshop_projects_post_hosting_delete_task($task, $data) { // When a platform is deleted, if it is the last in the project, // and the project has been unpublished, delete the directory. if ($task->ref->type == 'platform' && !empty($task->ref->project)) { - $project = node_load($task->ref->project_nid); - drush_log('[DEVSHOP] Checking for other platforms...', 'ok'); - - if ($project->status == 0){ - // Don't know if this platform is still here or not... - unset($project->project_objects['platform'][$task->ref]); - - //Check if all platforms were deleted. If is the last platform delete root folder. - $last = TRUE; - if (!empty($node->project_objects['platform'])) { - foreach ($project->project_objects['platform'] as $nid => $type) { - $platform_status = db_result(db_query("SELECT status FROM {hosting_platform} WHERE nid = %d", $nid)); - if ($nid != $task->ref && $platform_status != HOSTING_PLATFORM_DELETED) { - $last = FALSE; - } - } - } - if ($last) { - drush_log('[DEVSHOP] Last Platform! Removing code_path folder.', 'ok'); - - //Is the drupal directory isn't empty then we need to delete recursively - if ($project->drupal_path) { - _devshop_rrmdir($project->code_path); - } - else { - rmdir($project->code_path); - } - } - } + if ($task->ref->project->status == 0 && count($task->ref->project->environments) == 1){ + // Delete the project folder. + exec("rm -rf {$project->code_path}"); + } } } @@ -274,22 +247,13 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } // When a project is verified, queue a verification for all platforms - // @TODO: Figure out a better way to verify all the things. -// if ($task->ref->type == 'project' && isset($task->ref->project_objects['platform'])){ -// $project = node_load($task->ref->nid); -// $platform_nids = array_keys($project->project_objects['platform']); -// foreach ($project->project_objects['platform'] as $nid => $name) { -// //Check if a platform was deleted for exclude from verify. -// $status = db_result(db_query("SELECT status FROM {hosting_platform} WHERE nid = %d", $nid)); -// if ($status != HOSTING_PLATFORM_DELETED) { -// drush_log('[DEVSHOP] Running verify on project: ' . $name, 'ok'); -// hosting_add_task($nid, 'verify'); -// } -// else { -// drush_log('[DEVSHOP] Can\'t run verify on project: ' . $name, 'ok'); -// } -// } -// } + // @TODO: Figure out a better way to verify, only when needed. + if ($task->ref->type == 'project'){ + foreach ($task->ref->project->environments as $environment) { + drush_log('[DEVSHOP] Running verify on environment ' . $environment->name, 'ok'); + hosting_add_task($nid, 'verify'); + } + } } /** From 1b5c99ed96f88899ef36c8f194df65568980706d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:32:17 -0400 Subject: [PATCH 0791/3476] Removing unneeded "recursive rmdir" --- devshop_projects/devshop_projects.module | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 71a1a5d80..4989db592 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -400,19 +400,3 @@ function _devshop_projects_project_has_module($node, $module) { $environment = key($node->project->environments); return _devshop_projects_site_has_module($node->project->environments[$environment]['site'], $module); } - - /* - * Delete a directory recursively - */ -function _devshop_rrmdir($dir) { - if (is_dir($dir)) { - $objects = scandir($dir); - foreach ($objects as $object) { - if ($object != "." && $object != "..") { - if (filetype($dir."/".$object) == "dir") _devshop_rrmdir($dir."/".$object); else unlink($dir."/".$object); - } - } - reset($objects); - rmdir($dir); - } -} From ebe766b2bff9f1f580d989b950bd86f468f6efc2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:41:48 -0400 Subject: [PATCH 0792/3476] Fixing up nodes reducing duplicate code for loading projects, sites, and platforms. --- devshop_projects/inc/nodes.inc | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index fdf12b5f6..79aba1206 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -45,7 +45,8 @@ function devshop_projects_load($node) { $project->data = unserialize($project->data); // Load Environments - $query = db_query("SELECT * FROM {hosting_devshop_project_environment} WHERE project_nid = %d ORDER BY environment", $node->nid); + // @TODO: Remove environments where the site has been deleted. + $query = db_query("SELECT e.*, s.status FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid WHERE project_nid = %d ORDER BY environment", $node->nid); $environments = array(); while ($environment = db_fetch_object($query)) { $environment->data = unserialize($environment->data); @@ -231,26 +232,21 @@ function devshop_projects_delete($node) { /** * Implementation of hook_nodeapi() + * For site and platform nodes, load project and environment objects. * - * Handle project information for Platforms and Sites: - * $node->environment: All information about this environment. + * @see devshop_projects_load() */ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $additions['environment'] = db_fetch_object(db_query("SELECT e.*, n.title AS project FROM {hosting_devshop_project_environment} e LEFT JOIN {node} n ON e.project_nid = n.nid WHERE {$node->type} = %d", $node->nid)); + $result = db_fetch_object(db_query("SELECT project_nid, environment FROM {hosting_devshop_project_environment} e WHERE {$node->type} = %d", $node->nid)); - $additions['project'] = db_fetch_object(db_query('SELECT p.*, n.status FROM {hosting_devshop_project} p LEFT JOIN {node} n ON p.nid = n.nid WHERE n.nid = %d', $additions['environment']->project_nid)); - - $additions['environment']->data = unserialize($additions['environment']->data); - $additions['project']->data = unserialize($additions['project']->data); - - // @TODO: Remove once we change environment schema. - $additions['environment']->name = $additions['environment']->environment; - - // @TODO: Determine environment status from site status. + // Load project and environment + $project_node = node_load($result->project_nid); + $additions['project'] = $project_node->project; + $additions['environment'] = $project_node->project->environments[$result->environment]; return $additions; } } From e1f37e1e774bcbb5ae6225da339f0d331b784d61 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 22:45:14 -0400 Subject: [PATCH 0793/3476] Fixing platform/site display. --- devshop_projects/inc/ui.inc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index e8e32c618..ee4e9d545 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -342,21 +342,21 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } /** - * Implements hook_nodeapi_TYPE_OP() + * Implements hook_nodeapi_TYPE_OP() for site nodes and view op. */ function devshop_projects_nodeapi_site_view(&$node, $a3, $a4) { - if (!empty($node->environment)){ + if (!empty($node->project)){ // Display Project, Environment and Branch. $node->content['info']['project'] = array( '#type' => 'item', '#title' => t('Project'), - '#value' => l($node->environment->project, "node/{$node->environment->project_nid}"), + '#value' => l($node->project->name, "node/{$node->project->nid}"), '#weight' => -12, ); $node->content['info']['env'] = array( '#type' => 'item', '#title' => t('Environment'), - '#value' => $node->environment->environment, + '#value' => $node->environment->name, '#weight' => -11, ); $node->content['info']['branch'] = array( @@ -370,7 +370,7 @@ function devshop_projects_nodeapi_site_view(&$node, $a3, $a4) { $crumbs = array(); $crumbs[] = l(t('Home'), ''); $crumbs[] = l(t('Projects'), 'hosting/projects'); - $crumbs[] = l($node->environment->project, "node/" . $node->project_nid); + $crumbs[] = l($node->project->name, "node/" . $node->project->nid); drupal_set_breadcrumb($crumbs); } } From 47e8f93eb37caf5fbdc4f7eae35f5efe559f8a2f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 23:18:34 -0400 Subject: [PATCH 0794/3476] Fixing up platform creation. to use environments object. --- devshop_projects/tasks/create.inc | 48 ++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 4017f334b..aa4c98629 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -137,35 +137,51 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { * If desired, the environment to fork off of. (Copy the database and create a new branch from) */ function hosting_create_environment($project_node, $environment_name, $branch, $fork_source = NULL) { + global $user; + $project = $project_node->project; - $servers = hosting_get_servers('http'); - $server = variable_get('devshop_projects_default_web_server', key($servers)); - - // hosting_platform fields - $platform = new stdClass; + // First, create a platform node. Only aegir properties are needed. + $platform = new stdClass(); + $platform->type = 'platform'; $platform->title = $project_node->title . '_' . $environment_name; - $platform->publish_path = $project->code_path . '/' . $environment_name; - $platform->web_server = $server; - $platform->git_branch = $branch; - $platform->project = $project->title; - $platform->environment = $environment_name; - $platform->drupal_path = $project->drupal_path; + $platform->status = 1; + $platform->uid = $user->uid; + $platform->name = $user->name; + + // Set servers + $servers = hosting_get_servers('http'); + $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers));; + // If drupal path, append to code path/environment name if ($project->drupal_path) { $platform->publish_path = $project->code_path . '/' . $environment_name . '/' . $project->drupal_path; } + else { + $platform->publish_path = $project->code_path . '/' . $environment_name; + } - //Check if we are forking another site. + // Check if we are forking another site. + // @see ... if (isset($project_node->project->environments[$fork_source])) { $platform->clone_nid = $project_node->project->environments[$fork_source]['site']; $platform->git_branch = $branch; $platform->old_branch = $project_node->project->environments[$fork_source]['git_branch']; } - watchdog('debug', 'Attempting to create: ' . print_r($platform,1)); + // Save the platform node. + if ($platform = node_submit($platform)) { + node_save($platform); + } + + // Next, add the environment record. + $environment = new stdClass(); + $environment->git_ref = $branch; + $environment->platform = $platform->nid; + + $project->environments[$environment_name] = $environment; - // Create the platform node. The platform and site creation handle it from there. - $platform_node = _devshop_projects_node_create('platform', $platform); + // Save the project node. + $project_node->project = $project; + node_save($project_node); - return $platform_node; } From ac87d75f66d6e7bff4dbbf325bb1c379ef0f565b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 23:23:38 -0400 Subject: [PATCH 0795/3476] Fixing environment site creation. --- devshop_projects/devshop_projects.drush.inc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 2f2e7578d..d63288415 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -210,7 +210,8 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } // Get the project - $project = node_load($platform->project_nid); + $project_node = node_load($platform->project_nid); + $project = $project_node->project; // If the project doesn't have an install profile chosen yet, bail. if (empty($project->install_profile)){ @@ -229,19 +230,16 @@ function devshop_projects_post_hosting_verify_task($task, $data) { return; } - // live. Let's create a site based off of this platform. - drush_log('[DEVSHOP] Platform verified. Creating your site.'); - //Check if clone or create a new site. if ($platform->clone_nid) { $servers = hosting_get_servers('db'); $args['target_platform'] = $platform->nid; - $args['new_uri'] = $platform->environment .'.'. $project->base_url; + $args['new_uri'] = $platform->environment->name .'.'. $project->base_url; $args['new_db_server'] = key($servers); hosting_add_task($platform->clone_nid, 'clone', $args); } else { - $site_node = devshop_projects_create_site($project, $platform, $platform->environment); + $site_node = devshop_projects_create_site($project_node, $platform, $platform->environment->name); drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); } } From 0c67a70e75df5f25485822c896445dcae90845b3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 23:34:07 -0400 Subject: [PATCH 0796/3476] Fixing environment site creation. --- devshop_projects/devshop_projects.drush.inc | 20 +++++++------------- devshop_projects/devshop_projects.module | 6 +++--- devshop_projects/inc/create-wizard.inc | 2 +- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index d63288415..83fbfdfcb 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -118,7 +118,7 @@ function drush_devshop_platform_verify(){ $command .= " --branch $git_branch"; } - // @TODO: Gotta be a better way... + // @TODO: Gotta be a better way... I don't want a clone_nid field in devshop_project_objects table. // If is set site nid clone from old site and later create a new branch. // if ($platform->clone_nid) { // $site_source = node_load($platform->clone_nid); @@ -199,22 +199,15 @@ function devshop_projects_post_hosting_verify_task($task, $data) { if ($task->ref->type == 'platform') { drush_log('[DEVSHOP] Platform Verification complete.', 'notice'); - // Get objects - $nid = $task->ref->nid; - $platform = node_load($nid); - // If this platform isn't in a project, bail. - if (empty($platform->project_nid)){ + $platform = $task->ref; + if (empty($platform->project)){ drush_log('[DEVSHOP] No project found for this platform.', 'notice'); return; } - // Get the project - $project_node = node_load($platform->project_nid); - $project = $project_node->project; - // If the project doesn't have an install profile chosen yet, bail. - if (empty($project->install_profile)){ + if (empty($platform->project->install_profile)){ drush_log('[DEVSHOP] No install profile found for this platform\'s project.', 'notice'); return; } @@ -234,12 +227,13 @@ function devshop_projects_post_hosting_verify_task($task, $data) { if ($platform->clone_nid) { $servers = hosting_get_servers('db'); $args['target_platform'] = $platform->nid; - $args['new_uri'] = $platform->environment->name .'.'. $project->base_url; + $args['new_uri'] = $platform->environment->name .'.'. $platform->project->base_url; $args['new_db_server'] = key($servers); + drush_log('[DEVSHOP] Cloning site...'); hosting_add_task($platform->clone_nid, 'clone', $args); } else { - $site_node = devshop_projects_create_site($project_node, $platform, $platform->environment->name); + $site_node = devshop_projects_create_site($platform->project, $platform, $platform->environment->name); drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); } } diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 4989db592..616c18c89 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -304,7 +304,7 @@ function devshop_projects_hosting_drush_aliases_name($node) { * Helper function to create a site in a project. * Used by Wizard & "Create Platform" > Post Verify */ -function devshop_projects_create_site($project_node, $platform_node, $environment_name, $db_server = NULL) { +function devshop_projects_create_site($project, $platform_node, $environment_name, $db_server = NULL) { global $user; // Create the site nodes @@ -312,7 +312,7 @@ function devshop_projects_create_site($project_node, $platform_node, $environmen $node->type = 'site'; $node->status = 1; $node->uid = $user->uid; - $node->title = $environment_name .'.'. $project_node->project->base_url; + $node->title = $environment_name .'.'. $project->base_url; // Aegir properties // @TODO: better client & DB support @@ -324,7 +324,7 @@ function devshop_projects_create_site($project_node, $platform_node, $environmen $node->platform = $platform_node->nid; // Lookup this platforms install profile - $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project_node->project->install_profile)); + $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project->install_profile)); // $node->environment = $environment_name; // $node->git_branch = $platform_node->git_branch; diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f34265c05..c29e3486f 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -832,7 +832,7 @@ function devshop_projects_create_wizard_finish(&$form_state) { foreach ($project_node->project->environments as $environment_name => &$environment) { // @TODO: Does this set the http_server as well?? Doesn't look like it. $db_server = $environment->db_server; - $site_node = devshop_projects_create_site($project_node, node_load($environment->platform), $environment_name, $db_server); + $site_node = devshop_projects_create_site($project_node->project, node_load($environment->platform), $environment_name, $db_server); $environment->site = $site_node->nid; } From c5c21e043d56408fb262dc0d920c76fd1cd52064 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 23:34:53 -0400 Subject: [PATCH 0797/3476] properly verify platform. --- devshop_projects/devshop_projects.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 83fbfdfcb..0883e8ae6 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -243,7 +243,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { if ($task->ref->type == 'project'){ foreach ($task->ref->project->environments as $environment) { drush_log('[DEVSHOP] Running verify on environment ' . $environment->name, 'ok'); - hosting_add_task($nid, 'verify'); + hosting_add_task($environment->platform, 'verify'); } } } From 0e2b2cade822698a65d0eca2736c0f56a4d82160 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 23:37:41 -0400 Subject: [PATCH 0798/3476] Saving project node to save the new site nid. --- devshop_projects/devshop_projects.module | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 616c18c89..a343c40de 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -326,17 +326,20 @@ function devshop_projects_create_site($project, $platform_node, $environment_nam // Lookup this platforms install profile $node->profile = db_result(db_query('SELECT nid FROM {hosting_package} WHERE short_name = "%s"', $project->install_profile)); -// $node->environment = $environment_name; -// $node->git_branch = $platform_node->git_branch; -// $node->project = $project_node->title; - // @TODO: Improve site language handling? $node->site_language = !empty($user->language)? $user->language: 'en'; - // Save the node + // Save the site node if ($node = node_submit($node)) { node_save($node); } + + // Once we have the nid, save the environment object. + $project->environments[$environment_name]->site = $node->nid; + $project_node = node_load($project->nid); + $project_node->project = $project; + node_save($project_node); + return $node; } From d79dbfb690c98401b3e5ffb12cb77a5fba6567ab Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 23:40:42 -0400 Subject: [PATCH 0799/3476] No verify when creating a new environment. --- devshop_projects/devshop_projects.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index a343c40de..6fdcd6765 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -338,6 +338,7 @@ function devshop_projects_create_site($project, $platform_node, $environment_nam $project->environments[$environment_name]->site = $node->nid; $project_node = node_load($project->nid); $project_node->project = $project; + $project_node->no_verify = TRUE; node_save($project_node); return $node; From 72d489eb3afb06b2b9d63e1c8520ecb35e747243 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 9 Apr 2014 23:59:07 -0400 Subject: [PATCH 0800/3476] adding comment about loss of branches. --- devshop_projects/inc/forms.inc | 1 + devshop_pull/devshop_pull.module | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 1ffe826fb..7d6882649 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -172,6 +172,7 @@ function devshop_projects_form(&$node) { $form['#submit'] = array('devshop_projects_submit_settings'); // Save git branches and tags + // @TODO: Properly prevent loss of git branches and tags. $form['project']['data']['git']['branches'] = array( '#type' => 'value', '#value' => $node->project->data->git['branches'], diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index bef36abcc..bb27890e7 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -267,6 +267,12 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { * @TODO Convert to check pull queue sites. */ function devshop_pull_get_platforms($limit = 5) { + + $results = db_query('SELECT * FROM {hosting_devshop_project}'); + while ($project = db_fetch_object($results)){ + $project->data = unserialize($project->data); + dsm($project->data); + } $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); return db_result($result); } From 48cd98bca3beaabe83c5962ab6942d8a450b9f95 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 00:20:32 -0400 Subject: [PATCH 0801/3476] Starting to fix settings page. --- devshop_projects/inc/forms.inc | 80 +++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 7d6882649..97f7b087b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -88,17 +88,16 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ */ function devshop_projects_form(&$node) { - $form['git_url'] = array( - '#type' => 'textfield', - '#title' => t('Git URL'), - '#required' => TRUE, - '#description' => t(''), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $node->git_url, - '#maxlength' => 255, - '#weight' => 1, + $project = $node->project; + + // Project Settings + // Every value under $form['project'] gets serialized and saved into a project's "data" column. + // @TODO: Add ['data'] so only items under 'data' get serialized. + $form['project'] = array( + '#tree' => TRUE, ); + + // Hidden fields that can't change. $form['title'] = array( '#type' => 'textfield', '#title' => t('Project Code Name'), @@ -108,41 +107,61 @@ function devshop_projects_form(&$node) { '#default_value' => $node->title, '#maxlength' => 255, ); - $form['code_path'] = array( + $form['project']['git_url'] = array( + '#type' => 'textfield', + '#title' => t('Git URL'), + '#required' => TRUE, + '#description' => t(''), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $project->git_url, + '#maxlength' => 255, + '#weight' => 1, + ); + $form['project']['code_path'] = array( '#type' => 'textfield', '#title' => t('Code path'), '#description' => t('The absolute path on the filesystem that will be used to create the platform in the directory specified above.'), '#required' => TRUE, '#size' => 40, - '#default_value' => $node->code_path, + '#default_value' => $project->code_path, '#maxlength' => 255, '#weight' => 2, ); - $form['drupal_path'] = array( + $form['project']['drupal_path'] = array( '#type' => 'textfield', '#title' => t('Path to Drupal'), '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), '#size' => 40, - '#default_value' => $node->drupal_path, + '#default_value' => $project->drupal_path, '#maxlength' => 255, '#weight' => 3, ); - $form['base_url'] = array( + $form['project']['base_url'] = array( '#type' => 'textfield', '#title' => t('Primary Domain'), '#description' => t('The domain name all sites will be built under.'), '#required' => TRUE, '#size' => 40, - '#default_value' => $node->base_url, + '#default_value' => $project->base_url, '#maxlength' => 255, '#weight' => 4, ); - // Project Settings - // Every value under $form['project'] gets serialized and saved into a project's "data" column. - $form['project'] = array( - '#tree' => TRUE, - ); + // Don't allow editing + if ($node->nid) { + + // Title + $form['title']['#value'] = $form['title']['#default_value']; + $form['title']['#type'] = 'value'; + + // Other fields + $locked = array('git_url', 'code_path', 'drupal_path', 'base_url'); + foreach ($locked as $field){ + $form['project'][$field]['#value'] = $form[$field]['#default_value']; + $form['project'][$field]['#type'] = 'value'; + } + } // Environment settings $form['project']['environments'] = array( @@ -173,31 +192,21 @@ function devshop_projects_form(&$node) { // Save git branches and tags // @TODO: Properly prevent loss of git branches and tags. - $form['project']['data']['git']['branches'] = array( + $form['project']['git']['branches'] = array( '#type' => 'value', '#value' => $node->project->data->git['branches'], ); - $form['project']['data']['git']['tags'] = array( + $form['project']['git']['tags'] = array( '#type' => 'value', '#value' => $node->project->data->git['tags'], ); - // Don't allow editing - if ($node->nid) { - $locked = array('title', 'git_url', 'code_path', 'drupal_path', 'base_url'); - foreach ($locked as $field){ - $form[$field]['#value'] = $form[$field]['#default_value']; - $form[$field]['#type'] = 'value'; - } - } - - //All settings git pull in project page + // Live Environment settings. $form['project']['live'] = array( '#type' => 'fieldset', '#title' => t('Live Environment Settings'), ); - // Master Site alias $live_alias_options['other'] = 'Other...'; if (empty($live_alias_options[$node->project->live['live_environment']])){ // No live environment @@ -260,6 +269,9 @@ function devshop_projects_submit_settings($form, &$form_state) { $project_node = node_load($form_state['values']['nid']); $settings_info = module_invoke_all('devshop_project_settings', $project_node); $nodes = array(); + // Save all items under project to data + + // $form_state['values']['project']['data'] = (object) $form_state['values']['project']; // Go through each environment foreach ($form_state['values']['project']['environments'] as $environment_name => $settings){ From 06be969484b5642e9abf25238a3e37f006f4bbce Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 10:47:09 -0400 Subject: [PATCH 0802/3476] Putting back platform deletion in project creation. --- devshop_projects/inc/create-wizard.inc | 29 +++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index c29e3486f..4b439af41 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -612,21 +612,20 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project_node->project->environments[$name] = (object) $environment; } - // @TODO: Put this back once creation works. -// // For all removed platforms, trigger a delete task -// $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); -// -// foreach ($removed_environments as $environment_name => $settings) { -// // @TODO: Determine what to do here based on task status... -// // if verify task hasn't even run yet (and has never run) we can just delete -// // the platform node. -// -// // Create 'delete' task for the removed platform -// $platform_nid = $settings['platform']; -// if ($platform_nid){ -// hosting_add_task($platform_nid, 'delete'); -// } -// } + // For all removed platforms, trigger a delete task + $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); + + foreach ($removed_environments as $environment_name => $settings) { + // @TODO: Determine what to do here based on task status... + // if verify task hasn't even run yet (and has never run) we can just delete + // the platform node. + + // Create 'delete' task for the removed platform + $platform_nid = $settings['platform']; + if ($platform_nid){ + hosting_add_task($platform_nid, 'delete'); + } + } // Somehow, NEW environment still appears! unset($project_node->project->environments['NEW']); From 4869c0fbd5a448168df7464e3b05f3e64fecb498 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 10:52:20 -0400 Subject: [PATCH 0803/3476] Switching schema field to environment.name. --- devshop_projects/devshop_projects.install | 2 +- devshop_projects/inc/nodes.inc | 7 ++----- devshop_projects/inc/ui.inc | 2 +- devshop_projects/tasks/create.inc | 2 +- devshop_pull/devshop_pull.inc | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 2095c9af8..eb2997c41 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -113,7 +113,7 @@ function devshop_projects_schema() { 'default' => 0, 'description' => "The project's Node ID.", ), - 'environment' => array( + 'name' => array( 'type' => 'varchar', 'not null' => TRUE, 'length' => 64, diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 79aba1206..4bf58a4c6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -49,13 +49,10 @@ function devshop_projects_load($node) { $query = db_query("SELECT e.*, s.status FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid WHERE project_nid = %d ORDER BY environment", $node->nid); $environments = array(); while ($environment = db_fetch_object($query)) { - $environment->data = unserialize($environment->data); - - // @TODO: Remove when we change the environment schema to be "name" - $environment->name = $environment->environment; // Save to environments array - $environments[$environment->environment] = $environment; + $environment->data = unserialize($environment->data); + $environments[$environment->name] = $environment; } $project->environments = $environments; diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index ee4e9d545..84599b326 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -387,7 +387,7 @@ function devshop_projects_nodeapi_platform_view(&$node, $a3, $a4) { */ function devshop_hosting_site_goto_link($node) { // $project = node_load($node->environment->project_nid); - $url = "{$node->environment->environment}.{$node->project->base_url}"; + $url = "{$node->environment->name}.{$node->project->base_url}"; $cache = cache_get("hosting:site:" . $node->nid . ":login_link"); if (!is_null($cache) && (time() < $cache->data['expire'])) { $title = t("Log in: !url", array('!url' => $url)); diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index aa4c98629..54dfaaf2b 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -18,7 +18,7 @@ function hosting_task_devshop_create_form($node) { // @TODO: Do this with hook_menu? $site_to_fork = arg(3); if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { - if (!empty($project->environments[$site->environment->environment])){ + if (!empty($project->environments[$site->environment->name])){ $form['branch_source'] = array( '#type' => 'item', diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index d2cf17d90..5f3137783 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -213,7 +213,7 @@ function devshop_pull_project($project_nid) { foreach ($project_node->project->environments as $name => $environment){ if (!$environment->data->pull_disabled){ - $args['environments'] .= $environment->environment .' '; + $args['environments'] .= $environment->name .' '; } } $args['environments'] = trim($args['environments']); From c4e5524208ee571089b9d30f8676d81d01343bfc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 11:03:54 -0400 Subject: [PATCH 0804/3476] Updating schemas to use "settings" field. --- devshop_projects/devshop_projects.install | 26 +++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index eb2997c41..e85eb0580 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -42,11 +42,11 @@ function devshop_projects_schema() { 'size' => 'big', 'not null' => FALSE, ), - 'data' => array( + 'settings' => array( 'type' => 'text', 'not null' => FALSE, 'size' => 'big', - 'description' => 'A serialized array of data for this project.', + 'description' => 'A serialized array of settings for this project.', ), ), 'primary key' => array('nid'), @@ -141,11 +141,11 @@ function devshop_projects_schema() { 'unsigned' => TRUE, 'description' => 'The node ID of the platform for this environment.', ), - 'data' => array( + 'settings' => array( 'type' => 'text', 'not null' => FALSE, 'size' => 'big', - 'description' => 'A serialized array of data for this project.', + 'description' => 'A serialized array of settings for this environment.', ), ), 'indexes' => array( @@ -302,4 +302,22 @@ function devshop_projects_update_11() { function devshop_projects_update_12() { module_enable(array('hosting_alias')); return array(); +} + +/** + * Change "data" column to "settings". + */ +function devshop_projects_update_13(){ + $ret = array(); + $schema = devshop_projects_schema(); + + // Projects schema + $spec = $schema['hosting_devshop_project']['fields']['settings']; + db_change_field($ret, 'hosting_devshop_project', 'data', 'settings', $spec, $keys_new = array()); + + // Environments schema + $spec = $schema['hosting_devshop_project_environment']['fields']['settings']; + db_change_field($ret, 'hosting_devshop_project_environment', 'data', 'settings', $spec, $keys_new = array()); + + return $ret; } \ No newline at end of file From 91302b9d951ea6ff866b3af1c4739721a795e080 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 11:05:59 -0400 Subject: [PATCH 0805/3476] ensure settings is always an object. --- devshop_projects/inc/nodes.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 4bf58a4c6..a54d75d9b 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -42,7 +42,7 @@ function devshop_projects_load($node) { $project = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); // Load up all project settings, merging db table and serialized data. - $project->data = unserialize($project->data); + $project->settings = (object) unserialize($project->settings); // Load Environments // @TODO: Remove environments where the site has been deleted. @@ -51,7 +51,7 @@ function devshop_projects_load($node) { while ($environment = db_fetch_object($query)) { // Save to environments array - $environment->data = unserialize($environment->data); + $environment->settings = (object) unserialize($environment->settings); $environments[$environment->name] = $environment; } $project->environments = $environments; From d9a20257feaae487c0f757ad1bc6c67746635376 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 11:20:02 -0400 Subject: [PATCH 0806/3476] Fixing settings table theme to use "settings" sub element. --- devshop_projects/inc/create-wizard.inc | 14 +++++++++----- devshop_projects/inc/theme.inc | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 4b439af41..1f19a789d 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -499,10 +499,10 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // Add environment settings form elements foreach ($settings as $setting_id => $setting){ - $form['project']['environments'][$env][$setting_id] = $setting; - $form['project']['environments'][$env][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; - $form['project']['environments'][$env][$setting_id]['#attributes']['title'] = $setting['#description']; - $form['project']['environments'][$env][$setting_id]['#description'] = ''; + $form['project']['environments'][$env]['settings'][$setting_id] = $setting; + $form['project']['environments'][$env]['settings'][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; + $form['project']['environments'][$env]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project']['environments'][$env]['settings'][$setting_id]['#description'] = ''; } //Now add button. @@ -578,8 +578,9 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // If platform exists, it's because user went back in the wizard. $platform_nid = $project_node->project->environments[$name]->platform; - // If the platform already exists, verify it again. + // If the platform already exists, update settings and verify it again. if ($platform_nid) { + // @TODO: Apply settings and re-save platform. hosting_add_task($platform_nid, 'verify'); } // If platform hasn't been created yet, do so now! @@ -608,6 +609,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } + dsm($environment, "environment $name"); $environment['platform'] = $platform_node->nid; $project_node->project->environments[$name] = (object) $environment; } @@ -627,6 +629,8 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } } + dsm($project_node->project, 'saving project'); + // Somehow, NEW environment still appears! unset($project_node->project->environments['NEW']); $project_node->no_verify = TRUE; diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 46f28c4b9..73687d448 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -28,7 +28,7 @@ function theme_devshop_projects_settings_form($form) { foreach (element_children($form) as $env_name) { $row = array(); $row[] = $env_name; - foreach(element_children($form[$env_name]) as $setting){ + foreach(element_children($form[$env_name]['settings']) as $setting){ if (!isset($header[$setting])){ $header[$setting] = $form[$env_name][$setting]['#title']; } @@ -43,18 +43,22 @@ function theme_devshop_projects_settings_form($form) { /** * Theme function for create environments settings + * @TODO: Fold into theme_devshop_projects_settings_form() */ function theme_devshop_projects_create_settings_form($form) { $rows = array(); $header = array(); foreach (element_children($form) as $env_name) { $row = array(); - foreach(element_children($form[$env_name]) as $setting){ + $header['title'] = 'Name'; + $row[] = drupal_render($form[$env_name]['title']); + + foreach(element_children($form[$env_name]['settings']) as $setting){ if (!isset($header[$setting])){ - $header[$setting] = $form[$env_name][$setting]['#title']; + $header[$setting] = $form[$env_name]['settings'][$setting]['#title']; } - $form[$env_name][$setting]['#title'] = ''; - $row[] = drupal_render($form[$env_name][$setting]); + $form[$env_name]['settings'][$setting]['#title'] = ''; + $row[] = drupal_render($form[$env_name]['settings'][$setting]); } $rows[] = $row; } From 92f17d2dab0e7d3db12f98b42d500339dcc9f679 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 11:28:18 -0400 Subject: [PATCH 0807/3476] Changing data to settings for git branches/tags. --- devshop_projects/devshop_projects.drush.inc | 4 ++-- devshop_projects/devshop_projects.module | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 0883e8ae6..cc2b32e8b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -261,8 +261,8 @@ function devshop_projects_hosting_project_context_options(&$task) { // If something went wrong connecting to the git repo, don't wipe out our branches. if (!empty($branches['branches'])){ - $task->ref->project->data->git['branches'] = $branches['branches']; - $task->ref->project->data->git['tags'] = $branches['tags']; + $task->ref->project->settings->git['branches'] = $branches['branches']; + $task->ref->project->settings->git['tags'] = $branches['tags']; // Save the project node now that we have branches and tags. node_save($task->ref); diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6fdcd6765..06cc17f59 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -348,20 +348,21 @@ function devshop_projects_create_site($project, $platform_node, $environment_nam * Helper to get select #options for git ref. */ function devshop_projects_git_ref_options($project){ + // Build branch options - if (is_array($project->data->git['branches']) && !empty($project->data->git['branches'])) { + if (is_array($project->settings->git['branches']) && !empty($project->settings->git['branches'])) { $options = array( - 'Branches' => array_combine($project->data->git['branches'], $project->data->git['branches']), + 'Branches' => array_combine($project->settings->git['branches'], $project->settings->git['branches']), ); - } - // If there are tags... - if (is_array($project->git['tags']) && !empty($project->git['tags'])) { - $options['Tags'] = array_combine($project->git['tags'], $project->git['tags']); + // If there are tags... + if (is_array($project->settings->git['tags']) && !empty($project->settings->git['tags'])) { + $options['Tags'] = array_combine($project->settings->git['tags'], $project->settings->git['tags']); + } } // If there are none... - if (empty($options['Tags']) && empty($options['Branches'])){ + if (!isset($options)){ $options = array(t('No branches or tags! Re-validate the project.')); } From b5ef06f5e934153295f3b11c546de5998cb47390 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 11:58:45 -0400 Subject: [PATCH 0808/3476] Fixing up node insert and load for settings. --- devshop_projects/inc/nodes.inc | 64 ++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index a54d75d9b..e61bb38b3 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -41,12 +41,12 @@ function devshop_projects_load($node) { // Load project data $project = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); - // Load up all project settings, merging db table and serialized data. + // Load up all project settings. $project->settings = (object) unserialize($project->settings); // Load Environments // @TODO: Remove environments where the site has been deleted. - $query = db_query("SELECT e.*, s.status FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid WHERE project_nid = %d ORDER BY environment", $node->nid); + $query = db_query("SELECT e.*, s.status FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid WHERE project_nid = %d ORDER BY name", $node->nid); $environments = array(); while ($environment = db_fetch_object($query)) { @@ -90,9 +90,7 @@ function devshop_projects_insert($node) { $info->install_profile = $node->project->install_profile; // Save serialized data, minus environments - $data = $node->project; - unset($data->environments); - $info->data = serialize($data); + $info->settings = serialize($node->project->settings); drupal_write_record('hosting_devshop_project', $info); @@ -106,17 +104,21 @@ function devshop_projects_insert($node) { // Save Environment records. if (!empty($node->project->environments)) { - foreach ($node->project->environments as $env => $environment){ + foreach ($node->project->environments as $name => $environment){ $info = new stdClass(); $info->project_nid = $node->nid; - $info->environment = $env; - $info->git_ref = $environment->git_ref; - $info->site = $environment->site; - $info->platform = $environment->platform; - - unset($environment->data); - $info->data = serialize($environment); - + $info->name = $name; + $info->git_ref = $environment->settings['git_ref']; + $info->site = $environment->settings['site']; + $info->platform = $environment->settings['platform']; + + // Remove primary settings from settings array before saving. + unset($environment->settings['git_ref']); + unset($environment->settings['site']); + unset($environment->settings['platform']); + $info->settings = serialize($environment->settings); + + // Save environment record. drupal_write_record('hosting_devshop_project_environment', $info); } } @@ -145,8 +147,8 @@ function devshop_projects_update($node) { $info->base_url = $project->base_url; $info->install_profile = $project->install_profile; - // Save serialized data - $info->data = serialize($project->data); + // Save serialized data, minus environments + $info->settings = serialize($node->project->settings); // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); @@ -160,19 +162,19 @@ function devshop_projects_update($node) { foreach ($project->environments as $name => $environment){ $info = new stdClass(); $info->project_nid = $node->nid; - $info->environment = $name; - $info->git_ref = $environment->git_ref; - $info->site = $environment->site; - $info->platform = $environment->platform; - - $info->data = serialize($environment->data); - - if (drupal_write_record('hosting_devshop_project_environment', $info)){ - drupal_set_message(t('Environment %name saved.', array('%name' => $name))); - } - else { - drupal_set_message(t('Something went wrong saving environment %name.', array('%name' => $name)), 'error'); - } + $info->name = $name; + $info->git_ref = $environment->settings['git_ref']; + $info->site = $environment->settings['site']; + $info->platform = $environment->settings['platform']; + + // Remove primary settings from settings array before saving. + unset($environment->settings['git_ref']); + unset($environment->settings['site']); + unset($environment->settings['platform']); + $info->settings = serialize($environment->settings); + + // Save environment record. + drupal_write_record('hosting_devshop_project_environment', $info); } } @@ -238,12 +240,12 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Load environment info into platforms and sites. if ($op == 'load' && $node->type == 'platform' || $node->type == 'site') { $additions = array(); - $result = db_fetch_object(db_query("SELECT project_nid, environment FROM {hosting_devshop_project_environment} e WHERE {$node->type} = %d", $node->nid)); + $result = db_fetch_object(db_query("SELECT project_nid, name FROM {hosting_devshop_project_environment} e WHERE {$node->type} = %d", $node->nid)); // Load project and environment $project_node = node_load($result->project_nid); $additions['project'] = $project_node->project; - $additions['environment'] = $project_node->project->environments[$result->environment]; + $additions['environment'] = $project_node->project->environments[$result->name]; return $additions; } } From 7332434db54cab76f3ccb87342ee638d2d4cd673 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 11:59:15 -0400 Subject: [PATCH 0809/3476] Replacing environment for name. --- devshop_projects/devshop_projects.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index e85eb0580..925c6c811 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -149,7 +149,7 @@ function devshop_projects_schema() { ), ), 'indexes' => array( - 'project_environment' => array('project_nid', 'environment'), + 'project_environment' => array('project_nid', 'name'), ), ); return $schema; From a90c077a72ebb5003aefa0c664da1412d5efc520 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:09:04 -0400 Subject: [PATCH 0810/3476] Fixing default values --- devshop_projects/inc/create-wizard.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 1f19a789d..1e1e72b06 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -500,7 +500,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // Add environment settings form elements foreach ($settings as $setting_id => $setting){ $form['project']['environments'][$env]['settings'][$setting_id] = $setting; - $form['project']['environments'][$env]['settings'][$setting_id]['#default_value'] = $project->environments[$env][$setting_id]; + $form['project']['environments'][$env]['settings'][$setting_id]['#default_value'] = $project->environments[$env]['settings'][$setting_id]; $form['project']['environments'][$env]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; $form['project']['environments'][$env]['settings'][$setting_id]['#description'] = ''; } @@ -602,14 +602,12 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { foreach ($settings as $setting_name => $element){ if ($element['#node_type'] == 'platform'){ - $platform->{$setting_name} = $environment[$setting_name]; + $platform->{$setting_name} = $environment['settings'][$setting_name]; } } $platform_node = _devshop_projects_node_create('platform', $platform); } - - dsm($environment, "environment $name"); $environment['platform'] = $platform_node->nid; $project_node->project->environments[$name] = (object) $environment; } From 6d67a75e09084439dfe01c1064d6671dec60d8cc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:15:28 -0400 Subject: [PATCH 0811/3476] Fixing saving platform nid. --- devshop_projects/inc/create-wizard.inc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 1e1e72b06..d8ce80e47 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -608,7 +608,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform_node = _devshop_projects_node_create('platform', $platform); } - $environment['platform'] = $platform_node->nid; + $environment['settings']['platform'] = $platform_node->nid; $project_node->project->environments[$name] = (object) $environment; } @@ -627,8 +627,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } } - dsm($project_node->project, 'saving project'); - // Somehow, NEW environment still appears! unset($project_node->project->environments['NEW']); $project_node->no_verify = TRUE; From 7d7c04dbf1fd343094457b7f4c4dd3da0ab88290 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:17:09 -0400 Subject: [PATCH 0812/3476] Ensure object before saving. --- devshop_projects/inc/nodes.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index e61bb38b3..ef3222c59 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -105,6 +105,8 @@ function devshop_projects_insert($node) { // Save Environment records. if (!empty($node->project->environments)) { foreach ($node->project->environments as $name => $environment){ + $environment = (object) $environment; + $info = new stdClass(); $info->project_nid = $node->nid; $info->name = $name; @@ -160,6 +162,7 @@ function devshop_projects_update($node) { // Save each environment foreach ($project->environments as $name => $environment){ + $environment = (object) $environment; $info = new stdClass(); $info->project_nid = $node->nid; $info->name = $name; From 66d3319bd0004feaad2dc61ac01baa1ad7ea8b03 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:23:23 -0400 Subject: [PATCH 0813/3476] Ensure proper data types. --- devshop_projects/inc/nodes.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index ef3222c59..7e548744a 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -105,7 +105,9 @@ function devshop_projects_insert($node) { // Save Environment records. if (!empty($node->project->environments)) { foreach ($node->project->environments as $name => $environment){ + // Ensure correct data types $environment = (object) $environment; + $environment->settings = (array) $environment->settings; $info = new stdClass(); $info->project_nid = $node->nid; @@ -162,7 +164,10 @@ function devshop_projects_update($node) { // Save each environment foreach ($project->environments as $name => $environment){ + // Ensure correct data types $environment = (object) $environment; + $environment->settings = (array) $environment->settings; + $info = new stdClass(); $info->project_nid = $node->nid; $info->name = $name; From 0d6accceece0d84a7f85373d8b0af3c829d9bd5d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:23:35 -0400 Subject: [PATCH 0814/3476] git ref, not git branch. --- devshop_projects/inc/create-wizard.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index d8ce80e47..bcf54fa5c 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -670,7 +670,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Build a table. $row = array(); $row['name'] = $name; - $row['branch'] = $platform->git_branch; + $row['branch'] = $environment->git_ref; $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); // If platform verified successfully: From 0fba911ec4f5eb378de2a7129dd84ad695e94fbb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:25:56 -0400 Subject: [PATCH 0815/3476] Fixing git branch display. --- devshop_projects/inc/ui.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 84599b326..2ad7bf361 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -140,8 +140,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); // Branches display - if (!empty($project->data->git['branches'])){ - $items = theme_item_list($project->data->git['branches'], NULL, 'ul', array('class' => 'branches')); + if (!empty($project->settings->git['branches'])){ + $items = theme_item_list($project->settings->git['branches'], NULL, 'ul', array('class' => 'branches')); } else { $items = theme_item_list(array('No Branches Found!'), NULL, 'ul', array('class' => 'branches')); From aa704c24aedd032ceb91713ea4f79237bcbccbfb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:40:26 -0400 Subject: [PATCH 0816/3476] Fixing environment saving with new settings array. --- devshop_projects/inc/create-wizard.inc | 3 ++- devshop_projects/inc/nodes.inc | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index bcf54fa5c..21ea5e512 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -608,7 +608,8 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $platform_node = _devshop_projects_node_create('platform', $platform); } - $environment['settings']['platform'] = $platform_node->nid; + $environment['platform'] = $platform_node->nid; + $environment['git_ref'] = $environment['settings']['git_ref']; $project_node->project->environments[$name] = (object) $environment; } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 7e548744a..3fa8eff4e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -112,9 +112,9 @@ function devshop_projects_insert($node) { $info = new stdClass(); $info->project_nid = $node->nid; $info->name = $name; - $info->git_ref = $environment->settings['git_ref']; - $info->site = $environment->settings['site']; - $info->platform = $environment->settings['platform']; + $info->git_ref = $environment->git_ref; + $info->site = $environment->site; + $info->platform = $environment->platform; // Remove primary settings from settings array before saving. unset($environment->settings['git_ref']); @@ -171,9 +171,9 @@ function devshop_projects_update($node) { $info = new stdClass(); $info->project_nid = $node->nid; $info->name = $name; - $info->git_ref = $environment->settings['git_ref']; - $info->site = $environment->settings['site']; - $info->platform = $environment->settings['platform']; + $info->git_ref = $environment->git_ref; + $info->site = $environment->site; + $info->platform = $environment->platform; // Remove primary settings from settings array before saving. unset($environment->settings['git_ref']); From 2890bb6435a28209c99f901a591fd15deacfe6e2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:43:40 -0400 Subject: [PATCH 0817/3476] Fixing up create environment task with new settings array. --- devshop_projects/tasks/create.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 54dfaaf2b..548f513ea 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -84,7 +84,7 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ } // Check existence of the git branch - if (in_array($params['new_branch'], $project->data->git['branches'])){ + if (in_array($params['new_branch'], $project->settings->git['branches'])){ form_set_error('new_branch', t("The project already has a branch named %name. Choose a git branch name that doesn't already exist.", array('%name' => $params['new_branch']))); } @@ -149,6 +149,7 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ $platform->name = $user->name; // Set servers + // @TODO: Respect environment settings. $servers = hosting_get_servers('http'); $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers));; @@ -163,9 +164,9 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ // Check if we are forking another site. // @see ... if (isset($project_node->project->environments[$fork_source])) { - $platform->clone_nid = $project_node->project->environments[$fork_source]['site']; + $platform->clone_nid = $project_node->project->environments[$fork_source]->site; $platform->git_branch = $branch; - $platform->old_branch = $project_node->project->environments[$fork_source]['git_branch']; + $platform->old_branch = $project_node->project->environments[$fork_source]->git_ref; } // Save the platform node. From 87242629a17bb5b4eafbb7629063effc16612428 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 12:50:53 -0400 Subject: [PATCH 0818/3476] Fixing project settings form. --- devshop_projects/inc/forms.inc | 2 +- devshop_projects/inc/theme.inc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 97f7b087b..8a314c66d 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -182,7 +182,7 @@ function devshop_projects_form(&$node) { ); foreach ($settings as $setting_id => $setting){ $form['project']['environments'][$environment_name][$setting_id] = $setting; - $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $node->project->environments[$environment_name]->{$setting_id}; + $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; $form['project']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; } $site = node_load($environment->site); diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 73687d448..ed6ebcddc 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -28,7 +28,7 @@ function theme_devshop_projects_settings_form($form) { foreach (element_children($form) as $env_name) { $row = array(); $row[] = $env_name; - foreach(element_children($form[$env_name]['settings']) as $setting){ + foreach(element_children($form[$env_name]) as $setting){ if (!isset($header[$setting])){ $header[$setting] = $form[$env_name][$setting]['#title']; } From ede315e622aa136226be6ee24f04dfc579e87c0e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 13:06:26 -0400 Subject: [PATCH 0819/3476] Keeping settings and properties separate. --- devshop_projects/devshop_projects.module | 28 +++++++++++----------- devshop_projects/inc/forms.inc | 30 ++++++++++++++++++++---- devshop_projects/inc/theme.inc | 10 ++++---- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 06cc17f59..e8baf8e91 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -149,20 +149,20 @@ function devshop_projects_devshop_project_settings($node = NULL){ $project = $node->project; $settings = array(); - $settings['site'] = array( - '#node_type' => 'site', - '#type' => 'hidden', - ); - $settings['platform'] = array( - '#node_type' => 'platform', - '#type' => 'hidden', - ); - $settings['git_ref'] = array( - '#title' => t('Git Branch/Tag'), - '#node_type' => 'platform', - '#type' => 'select', - '#options' => devshop_projects_git_ref_options($project), - ); +// $settings['site'] = array( +// '#node_type' => 'site', +// '#type' => 'hidden', +// ); +// $settings['platform'] = array( +// '#node_type' => 'platform', +// '#type' => 'hidden', +// ); +// $settings['git_ref'] = array( +// '#title' => t('Git Branch/Tag'), +// '#node_type' => 'platform', +// '#type' => 'select', +// '#options' => devshop_projects_git_ref_options($project), +// ); $http_servers = hosting_get_servers('http'); if (count($http_servers)) { diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 8a314c66d..41cb82496 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -180,11 +180,30 @@ function devshop_projects_form(&$node) { '#title' => $environment_name, '#theme' => 'devshop_projects_settings_table', ); + + // Environment properties + $form['project']['environments'][$environment_name]['site'] = array( + '#type' => 'value', + '#value' => $environment->site, + ); + $form['project']['environments'][$environment_name]['platform'] = array( + '#type' => 'value', + '#value' => $environment->platform, + ); + $form['project']['environments'][$environment_name]['git_ref'] = array( + '#title' => t('Git Branch/Tag'), + '#type' => 'select', + '#options' => devshop_projects_git_ref_options($project), + '#default_value' => $environment->git_ref, + ); + + // Environment settings foreach ($settings as $setting_id => $setting){ - $form['project']['environments'][$environment_name][$setting_id] = $setting; - $form['project']['environments'][$environment_name][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; - $form['project']['environments'][$environment_name][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project']['environments'][$environment_name]['settings'][$setting_id] = $setting; + $form['project']['environments'][$environment_name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; + $form['project']['environments'][$environment_name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; } + $site = node_load($environment->site); $live_alias_options[$environment_name] = $site->hosting_name; } @@ -269,12 +288,13 @@ function devshop_projects_submit_settings($form, &$form_state) { $project_node = node_load($form_state['values']['nid']); $settings_info = module_invoke_all('devshop_project_settings', $project_node); $nodes = array(); - // Save all items under project to data + // Save all items under project to data // $form_state['values']['project']['data'] = (object) $form_state['values']['project']; // Go through each environment foreach ($form_state['values']['project']['environments'] as $environment_name => $settings){ + $form_state['values']['project']['environments'][$environment_name]['settings'] = $form_state['values']['project']['environments'][$environment_name]; // Then through each setting of each environment foreach ($settings as $setting_name => $setting_value){ @@ -305,6 +325,8 @@ function devshop_projects_submit_settings($form, &$form_state) { foreach ($nodes as $nid => $node){ node_save($node); } + + dsm($form_state['values']); } /** diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index ed6ebcddc..5d8a1fc66 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -28,12 +28,14 @@ function theme_devshop_projects_settings_form($form) { foreach (element_children($form) as $env_name) { $row = array(); $row[] = $env_name; - foreach(element_children($form[$env_name]) as $setting){ + $row[] = drupal_render($form[$env_name]['git_ref']); + $header['git_ref'] = t('Branch/Tag'); + foreach(element_children($form[$env_name]['settings']) as $setting){ if (!isset($header[$setting])){ - $header[$setting] = $form[$env_name][$setting]['#title']; + $header[$setting] = $form[$env_name]['settings'][$setting]['#title']; } - $form[$env_name][$setting]['#title'] = ''; - $row[] = drupal_render($form[$env_name][$setting]); + $form[$env_name]['settings'][$setting]['#title'] = ''; + $row[] = drupal_render($form[$env_name]['settings'][$setting]); } $rows[] = $row; } From 8bdce7a85757ac72386f5fe4812030aeaf777605 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 14:42:56 -0400 Subject: [PATCH 0820/3476] Keeping settings and properties separate. --- devshop_projects/inc/create-wizard.inc | 17 ++++ devshop_projects/inc/forms.inc | 110 ++++++++++++------------- devshop_projects/inc/nodes.inc | 6 +- devshop_pull/devshop_pull.module | 27 +++--- 4 files changed, 89 insertions(+), 71 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 21ea5e512..30827e172 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -483,6 +483,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#theme' => 'devshop_projects_settings_table', ); + // Environment properties $form['project']['environments'][$env]['title'] = array( '#type' => 'textfield', '#title' => t('Environment Name'), @@ -496,6 +497,22 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#field_prefix' => 'http://', '#field_suffix' => "." . $project_node->project->base_url, ); + $form['project']['environments'][$env]['site'] = array( + '#type' => 'value', + '#value' => $project->environments[$env]->site, + ); + $form['project']['environments'][$env]['platform'] = array( + '#type' => 'value', + '#value' => $project->environments[$env]->platform, + ); + $form['project']['environments'][$env]['git_ref'] = array( + '#title' => t('Git Branch/Tag'), + '#type' => 'select', + '#options' => devshop_projects_git_ref_options($project), + '#default_value' => $project->environments[$env]->git_ref, + ); + + // Environment settings // Add environment settings form elements foreach ($settings as $setting_id => $setting){ diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 41cb82496..e242b71c3 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -158,7 +158,7 @@ function devshop_projects_form(&$node) { // Other fields $locked = array('git_url', 'code_path', 'drupal_path', 'base_url'); foreach ($locked as $field){ - $form['project'][$field]['#value'] = $form[$field]['#default_value']; + $form['project'][$field]['#value'] = $form['project'][$field]['#default_value']; $form['project'][$field]['#type'] = 'value'; } } @@ -171,7 +171,6 @@ function devshop_projects_form(&$node) { '#tree' => TRUE, ); - // @TODO: Whenever an environment is created, make sure to resave the project so settings are captured in json. $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); foreach ($node->project->environments as $environment_name => $environment) { @@ -197,6 +196,8 @@ function devshop_projects_form(&$node) { '#default_value' => $environment->git_ref, ); + $form['project']['environments'][$environment_name]['settings'] = array(); + // Environment settings foreach ($settings as $setting_id => $setting){ $form['project']['environments'][$environment_name]['settings'][$setting_id] = $setting; @@ -207,21 +208,20 @@ function devshop_projects_form(&$node) { $site = node_load($environment->site); $live_alias_options[$environment_name] = $site->hosting_name; } - $form['#submit'] = array('devshop_projects_submit_settings'); + // Project Settings // Save git branches and tags - // @TODO: Properly prevent loss of git branches and tags. - $form['project']['git']['branches'] = array( + $form['project']['settings']['git']['branches'] = array( '#type' => 'value', '#value' => $node->project->data->git['branches'], ); - $form['project']['git']['tags'] = array( + $form['project']['settings']['git']['tags'] = array( '#type' => 'value', '#value' => $node->project->data->git['tags'], ); // Live Environment settings. - $form['project']['live'] = array( + $form['project']['settings']['live'] = array( '#type' => 'fieldset', '#title' => t('Live Environment Settings'), ); @@ -236,7 +236,7 @@ function devshop_projects_form(&$node) { $live_environment_default_value = 'other'; } - $form['project']['live']['live_environment'] = array( + $form['project']['settings']['live']['live_environment'] = array( '#type' => 'select', '#title' => t('Live Environment'), '#options' => $live_alias_options, @@ -244,7 +244,7 @@ function devshop_projects_form(&$node) { '#default_value' => $live_environment_default_value, '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), ); - $form['project']['live']['live_environment_alias'] = array( + $form['project']['settings']['live']['live_environment_alias'] = array( '#title' => t('Live Site Drush Alias'), '#type' => 'textfield', '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), @@ -256,7 +256,7 @@ function devshop_projects_form(&$node) { ); // Live Domain - $form['project']['live']['live_domain'] = array( + $form['project']['settings']['live']['live_domain'] = array( '#type' => 'textfield', '#title' => t('Live domain'), '#description' => t('The live domain for this project. Do not include "www".'), @@ -267,7 +267,7 @@ function devshop_projects_form(&$node) { // Use aliases if (module_exists('hosting_alias')){ - $form['project']['live']['live_domain_aliases'] = array( + $form['project']['settings']['live']['live_domain_aliases'] = array( '#type' => 'checkbox', '#title' => t('Use environment aliases'), '#description' => t('Create aliases for all environments as subdomains of the live domain.'), @@ -283,50 +283,50 @@ function devshop_projects_form(&$node) { * Submit function for save data of platforms. */ function devshop_projects_submit_settings($form, &$form_state) { - - // Go through and save our settings to site and platform nodes. - $project_node = node_load($form_state['values']['nid']); - $settings_info = module_invoke_all('devshop_project_settings', $project_node); - $nodes = array(); - - // Save all items under project to data - // $form_state['values']['project']['data'] = (object) $form_state['values']['project']; - - // Go through each environment - foreach ($form_state['values']['project']['environments'] as $environment_name => $settings){ - $form_state['values']['project']['environments'][$environment_name]['settings'] = $form_state['values']['project']['environments'][$environment_name]; - - // Then through each setting of each environment - foreach ($settings as $setting_name => $setting_value){ - - //Find the node type for this setting. - $node_type = $settings_info[$setting_name]['#node_type']; - - // Load the site or platform node for this environment, then set the value and save. - if (!empty($project_node->project->environments[$environment_name]->{$node_type})){ - $nid = $project_node->project->environments[$environment_name]->{$node_type}; - $nodes[$nid] = node_load($nid); - $nodes[$nid]->no_verify = TRUE; - - //If changed database then execute migrate task. - if ($setting_name == 'db_server' && $nodes[$nid]->{$setting_name} != $setting_value) { - $args['target_platform'] = $nodes[$nid]->platform; - $args['new_uri'] = $nodes[$nid]->title; - $args['new_db_server'] = $setting_value; - - hosting_add_task($nid, 'migrate', $args); - } - $nodes[$nid]->{$setting_name} = $setting_value; - } - } - } - - // Go save all nodes - foreach ($nodes as $nid => $node){ - node_save($node); - } - - dsm($form_state['values']); +// +// // Go through and save our settings to site and platform nodes. +// $project_node = node_load($form_state['values']['nid']); +// $settings_info = module_invoke_all('devshop_project_settings', $project_node); +// $nodes = array(); +// +// // Save all items under project to data +// // $form_state['values']['project']['data'] = (object) $form_state['values']['project']; +// +// // Go through each environment +// // @TODO: Move to devshop_project_update(); +// foreach ($form_state['values']['project']['environments'] as $environment_name => $settings){ +// //$form_state['values']['project']['environments'][$environment_name]['settings'] = $form_state['values']['project']['environments'][$environment_name]; +// +// // Then through each setting of each environment +// foreach ($settings as $setting_name => $setting_value){ +// +// //Find the node type for this setting. +// $node_type = $settings_info[$setting_name]['#node_type']; +// +// // Load the site or platform node for this environment, then set the value and save. +// if (!empty($project_node->project->environments[$environment_name]->{$node_type})){ +// $nid = $project_node->project->environments[$environment_name]->{$node_type}; +// $nodes[$nid] = node_load($nid); +// $nodes[$nid]->no_verify = TRUE; +// +// //If changed database then execute migrate task. +// if ($setting_name == 'db_server' && $nodes[$nid]->{$setting_name} != $setting_value) { +// $args['target_platform'] = $nodes[$nid]->platform; +// $args['new_uri'] = $nodes[$nid]->title; +// $args['new_db_server'] = $setting_value; +// +// hosting_add_task($nid, 'migrate', $args); +// } +// $nodes[$nid]->{$setting_name} = $setting_value; +// } +// } +// } +// +// // Go save all nodes +// foreach ($nodes as $nid => $node){ +// node_save($node); +// } +// } /** diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3fa8eff4e..a3b3cd277 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -102,6 +102,8 @@ function devshop_projects_insert($node) { // If using environment aliases... save them. devshop_project_save_domain_aliases($node); + // @TODO: The wizard always creates the project before the environments. + // Not sure if we need this, but we might to enable importing via drush. // Save Environment records. if (!empty($node->project->environments)) { foreach ($node->project->environments as $name => $environment){ @@ -141,7 +143,7 @@ function devshop_projects_update($node) { hosting_add_task($node->nid, 'verify'); } - $project = $node->project; + $project = (object) $node->project; $info = new stdClass(); $info->nid = $node->nid; @@ -152,7 +154,7 @@ function devshop_projects_update($node) { $info->install_profile = $project->install_profile; // Save serialized data, minus environments - $info->settings = serialize($node->project->settings); + $info->settings = serialize($project->settings); // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index bb27890e7..a4a483ef3 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -99,16 +99,16 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { $node = $form['#node']; //All settings git pull in project page - $form['project']['pull'] = array( + $form['project']['settings']['pull'] = array( '#type' => 'fieldset', '#title' => t('Git Settings'), ); - $form['project']['pull']['pull_enabled'] = array( + $form['project']['settings']['pull']['pull_enabled'] = array( '#title' => 'Pull on Commit', '#type' => 'checkbox', '#description' => t('Run a "Pull Code" task when a commit notification is received. All environments set to a branch will have their code updated.'), - '#default_value' => $node->project->pull['pull_enabled'], + '#default_value' => $node->project->settings->pull['pull_enabled'], ); // module_load_include('inc', 'devshop_pull'); @@ -123,29 +123,29 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { // ); // @TODO: is there a better way to save certain values? We lose data without these. - $form['project']['pull']['last_pull'] = array( + $form['project']['settings']['pull']['last_pull'] = array( '#type' => 'value', - '#value' => $node->project->pull['last_pull'], + '#value' => $node->project->settings->pull['last_pull'], ); - $form['project']['pull']['last_pull_status'] = array( + $form['project']['settings']['pull']['last_pull_status'] = array( '#type' => 'value', - '#value' => $node->project->pull['last_pull_status'], + '#value' => $node->project->settings->pull['last_pull_status'], ); - $form['project']['pull']['last_pull_ip'] = array( + $form['project']['settings']['pull']['last_pull_ip'] = array( '#type' => 'value', - '#value' => $node->project->pull['last_pull_ip'], + '#value' => $node->project->settings->pull['last_pull_ip'], ); - $form['project']['pull']['pull_request_environments'] = array( + $form['project']['settings']['pull']['pull_request_environments'] = array( '#type' => 'checkbox', '#title' => t('Create Environments for Pull Requests'), - '#default_value' => $node->project->pull['pull_request_environments'], + '#default_value' => $node->project->settings->pull['pull_request_environments'], '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), ); - $form['project_settings']['pull']['pull_request_environments_delete'] = array( + $form['project']['settings']['pull_request_environments_delete'] = array( '#type' => 'checkbox', '#title' => t('Delete Pull Request Environments'), - '#default_value' => $node->project_settings['pull']['pull_request_environments_delete'], + '#default_value' => $node->project->settings->pull['pull_request_environments_delete'], '#description' => t('When Pull Requests are closed, delete the environment.'), ); @@ -271,7 +271,6 @@ function devshop_pull_get_platforms($limit = 5) { $results = db_query('SELECT * FROM {hosting_devshop_project}'); while ($project = db_fetch_object($results)){ $project->data = unserialize($project->data); - dsm($project->data); } $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); return db_result($result); From 09f2d7942eb6a2e7c29bed859319a852cd1d7da6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 15:02:21 -0400 Subject: [PATCH 0821/3476] Fixing bad publish path. --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 30827e172..286c76966 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -608,10 +608,10 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Platform publish_path if (!empty($project_node->project->drupal_path)) { - $platform->publish_path = $project_node->project->code_path . '/' . $environment->title . '/' . $project_node->project->drupal_path; + $platform->publish_path = $project_node->project->code_path . '/' . $environment->name . '/' . $project_node->project->drupal_path; } else { - $platform->publish_path = $project_node->project->code_path . '/' . $environment->title; + $platform->publish_path = $project_node->project->code_path . '/' . $environment->name; } // Other attributes From a2162c80ef5b08b5fca2cb81e197b3216b03bd28 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 15:19:39 -0400 Subject: [PATCH 0822/3476] Refactoring create environments page. --- devshop_projects/inc/create-wizard.inc | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 286c76966..f1475617b 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -426,12 +426,13 @@ function devshop_project_create_step_settings_submit(&$from, &$form_state) { * STEP 3: Form */ function devshop_project_create_step_environments(&$form, &$form_state) { - $project = &$form_state['project']; - $project_node = node_load($project->project_nid); + $project_cache = &$form_state['project']; + $project_node = node_load($project_cache->project_nid); + $project = $project_node->project; - if ($project->verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) { + if ($project_cache->verify_task_status == HOSTING_TASK_QUEUED || $project_cache->verify_task_status == HOSTING_TASK_PROCESSING) { $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; - $project->no_next = TRUE; + $project_cache->no_next = TRUE; $form['note'] = array( '#type' => 'markup', @@ -469,22 +470,22 @@ function devshop_project_create_step_environments(&$form, &$form_state) { if (!is_array($project->environments['NEW'])){ $project->environments['NEW'] = array(); } - foreach ($project->environments as $env => $env_settings) { + foreach ($project->environments as $name => $environment) { // No platforms exist yet - if ($env == 'NEW') { + if ($name == 'NEW') { $env_title = ''; } else { - $env_title = $env; + $env_title = $name; } - $form['project']['environments'][$env] = array( + $form['project']['environments'][$name] = array( '#tree' => TRUE, '#type' => 'fieldset', '#theme' => 'devshop_projects_settings_table', ); // Environment properties - $form['project']['environments'][$env]['title'] = array( + $form['project']['environments'][$name]['title'] = array( '#type' => 'textfield', '#title' => t('Environment Name'), '#default_value' => $env_title, @@ -495,31 +496,30 @@ function devshop_project_create_step_environments(&$form, &$form_state) { 'autofocus' => 'autofocus', ), '#field_prefix' => 'http://', - '#field_suffix' => "." . $project_node->project->base_url, + '#field_suffix' => "." . $project->base_url, ); - $form['project']['environments'][$env]['site'] = array( + $form['project']['environments'][$name]['site'] = array( '#type' => 'value', - '#value' => $project->environments[$env]->site, + '#value' => $environment->site, ); - $form['project']['environments'][$env]['platform'] = array( + $form['project']['environments'][$name]['platform'] = array( '#type' => 'value', - '#value' => $project->environments[$env]->platform, + '#value' => $environment->platform, ); - $form['project']['environments'][$env]['git_ref'] = array( + $form['project']['environments'][$name]['git_ref'] = array( '#title' => t('Git Branch/Tag'), '#type' => 'select', '#options' => devshop_projects_git_ref_options($project), - '#default_value' => $project->environments[$env]->git_ref, + '#default_value' => $environment->git_ref, ); // Environment settings - // Add environment settings form elements foreach ($settings as $setting_id => $setting){ - $form['project']['environments'][$env]['settings'][$setting_id] = $setting; - $form['project']['environments'][$env]['settings'][$setting_id]['#default_value'] = $project->environments[$env]['settings'][$setting_id]; - $form['project']['environments'][$env]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; - $form['project']['environments'][$env]['settings'][$setting_id]['#description'] = ''; + $form['project']['environments'][$name]['settings'][$setting_id] = $setting; + $form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; + $form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; } //Now add button. @@ -626,7 +626,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } $environment['platform'] = $platform_node->nid; - $environment['git_ref'] = $environment['settings']['git_ref']; + $environment['git_ref'] = $environment['git_ref']; $project_node->project->environments[$name] = (object) $environment; } From f361bac20523388cab928c7cb2f76e105cccec13 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 15:20:09 -0400 Subject: [PATCH 0823/3476] Moving aegir object saving to node update hook. --- devshop_projects/inc/forms.inc | 12 +++++++---- devshop_projects/inc/nodes.inc | 38 ++++++++++++++++++++++++++++++++++ devshop_projects/inc/theme.inc | 2 ++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e242b71c3..49626e837 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -180,6 +180,12 @@ function devshop_projects_form(&$node) { '#theme' => 'devshop_projects_settings_table', ); + // Save current environment settings to detect differences later. + $form['project']['environments'][$environment_name]['orig'] = array( + '#type' => 'value', + '#value' => $environment, + ); + // Environment properties $form['project']['environments'][$environment_name]['site'] = array( '#type' => 'value', @@ -341,13 +347,11 @@ function devshop_projects_validate($node, &$form) { $node = node_load($node->nid); - // No validation if op == Delete or project_status == site_ready. The - // latter case happens when the user is editing the tests to run fied. - if ($node->op == t('Delete') || $node->project_status == 'sites_ready') { + // No validation if op == Delete + if ($node->op == t('Delete')) { return; } - // Full validation on when a creating a new node $add = (arg(1) == 'add' ? TRUE : FALSE); diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index a3b3cd277..a642c8896 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -178,6 +178,7 @@ function devshop_projects_update($node) { $info->platform = $environment->platform; // Remove primary settings from settings array before saving. + // @TODO: These values shouldn't show up anymore. Once tested, remove. unset($environment->settings['git_ref']); unset($environment->settings['site']); unset($environment->settings['platform']); @@ -185,6 +186,43 @@ function devshop_projects_update($node) { // Save environment record. drupal_write_record('hosting_devshop_project_environment', $info); + + // Save aegir objects + if ($environment->site){ + $objects = array(); + $objects['site'] = node_load($environment->site); + $objects['platform'] = node_load($environment->platform); + $settings_info = module_invoke_all('devshop_project_settings', $node); + + // Go through each setting, applying it to either the site or the platform. + foreach ($settings_info as $setting_name => $setting_value) { + $node_type = $settings_info[$setting_name]['#node_type']; + + // Set new value only if it has changed. + if ($objects[$node_type]->{$setting_name} != $setting_value){ + $objects[$node_type]->{$setting_name} = $setting_value; + $objects[$node_type]->needs_save = TRUE; + + // If the database server is changed, a "migrate" task is needed. + if ($setting_name == 'db_server') { + $args = array(); + $args['target_platform'] = $objects['site']->platform; + $args['new_uri'] = $objects['site']->title; + $args['new_db_server'] = $setting_value; + hosting_add_task($objects['site']->nid, 'migrate', $args); + } + } + } + + // @TODO: If git_ref changed, flag needs_save for platform. + + // Save site and platform if needed. + foreach ($objects as $type => $object){ + if ($object->needs_save) { + node_save($object); + } + } + } } } diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 5d8a1fc66..b8a03b4c6 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -53,7 +53,9 @@ function theme_devshop_projects_create_settings_form($form) { foreach (element_children($form) as $env_name) { $row = array(); $header['title'] = 'Name'; + $header['git_ref'] = t('Branch/Tag'); $row[] = drupal_render($form[$env_name]['title']); + $row[] = drupal_render($form[$env_name]['git_ref']); foreach(element_children($form[$env_name]['settings']) as $setting){ if (!isset($header[$setting])){ From afbd1b35eae8f9cec0db2bbb95ea65d3dc4e404a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 16:03:32 -0400 Subject: [PATCH 0824/3476] Fixing updating settings of aegir objects. --- devshop_projects/inc/nodes.inc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index a642c8896..a89865a51 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -188,19 +188,20 @@ function devshop_projects_update($node) { drupal_write_record('hosting_devshop_project_environment', $info); // Save aegir objects - if ($environment->site){ + if (!empty($environment->site)){ $objects = array(); $objects['site'] = node_load($environment->site); $objects['platform'] = node_load($environment->platform); $settings_info = module_invoke_all('devshop_project_settings', $node); // Go through each setting, applying it to either the site or the platform. - foreach ($settings_info as $setting_name => $setting_value) { + foreach ($settings_info as $setting_name => $setting_element) { $node_type = $settings_info[$setting_name]['#node_type']; // Set new value only if it has changed. - if ($objects[$node_type]->{$setting_name} != $setting_value){ - $objects[$node_type]->{$setting_name} = $setting_value; + $old_value = $environment->orig->settings->{$setting_name}; + if (isset($old_value) && $objects[$node_type]->{$setting_name} != $old_value){ + $objects[$node_type]->{$setting_name} = $environment->settings[$setting_name]; $objects[$node_type]->needs_save = TRUE; // If the database server is changed, a "migrate" task is needed. @@ -208,13 +209,16 @@ function devshop_projects_update($node) { $args = array(); $args['target_platform'] = $objects['site']->platform; $args['new_uri'] = $objects['site']->title; - $args['new_db_server'] = $setting_value; + $args['new_db_server'] = $environment->settings['db_server']; hosting_add_task($objects['site']->nid, 'migrate', $args); } } } - // @TODO: If git_ref changed, flag needs_save for platform. + // If git_ref changed, flag needs_save for platform. + if ($environment->orig->git_ref != $environment->git_ref){ + $objects['platform']->needs_save = TRUE; + } // Save site and platform if needed. foreach ($objects as $type => $object){ From baf7ed9a7e58bf5334f9ca4e5d15029a1fcc00ea Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 16:12:03 -0400 Subject: [PATCH 0825/3476] Fixing up add environment process. --- devshop_projects/inc/create-wizard.inc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index f1475617b..7bfca9e16 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -938,17 +938,16 @@ function devshop_form_reloader(&$form, $type = 'platform'){ * Functionality for add a new environment. */ function devshop_projects_create_wizard_add_new_environment($form, &$form_state) { - // Get Project Cache - ctools_include('wizard'); - ctools_include('object-cache'); - // All we are doing here is saving the project to cache. - // devshop_project_create_step_environments_validate() handles putting data in $project. - $project = $form_state['project']; + // Load project node + $project_node = node_load($form_state['values']['nid']); + $environments = $form_state['values']['project']['environments']; + unset($environments['NEW']); - // Save environment to project cache. - $project->environments = &$form_state['values']['project']['environments']; - ctools_object_cache_set('project', NULL, $project); + // Set environments and no_verify + $project_node->project->environments = $environments; + $project_node->no_verify = TRUE; + node_save($project_node); // Go back to the same page. drupal_goto('hosting/projects/add/environments'); From b8ee6f3c29a1534e9c8f2953b7125c25ab3e9bbd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 16:16:13 -0400 Subject: [PATCH 0826/3476] Duh. environment is still form values at this point. --- devshop_projects/inc/create-wizard.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index 7bfca9e16..e11948995 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -608,10 +608,10 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Platform publish_path if (!empty($project_node->project->drupal_path)) { - $platform->publish_path = $project_node->project->code_path . '/' . $environment->name . '/' . $project_node->project->drupal_path; + $platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; } else { - $platform->publish_path = $project_node->project->code_path . '/' . $environment->name; + $platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; } // Other attributes From e2cf01041ae876d24890f5263a74ac9b3abf967c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 18:53:46 -0400 Subject: [PATCH 0827/3476] Removing redundant "# of sites" display. --- devshop_projects/inc/ui.inc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 6b3340061..f36fac7db 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -41,10 +41,6 @@ function devshop_projects_projects_page() { // Drupal Version $row[] = $platform_node->release->version; - // Number of environments - $num = db_result(db_query("SELECT COUNT(*) FROM {hosting_devshop_project_object} hdpo LEFT JOIN {hosting_site} hs ON hdpo.object_nid = hs.nid WHERE hdpo.project_nid = %d AND hdpo.object_type='site' AND hs.status=1", $node->nid)); - $row[] = format_plural($num, t('1 site'), t('!num sites', array('!num' => $num))); - // Git URL $row[] = strtr("", array('!url' => $node->git_url)); From cb591cf9c4d8f79a6a5de0fd88d85312fb4e9083 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 10 Apr 2014 19:06:08 -0400 Subject: [PATCH 0828/3476] Removing empty header. --- devshop_projects/inc/ui.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index f36fac7db..f755292f1 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -16,7 +16,6 @@ function devshop_projects_projects_page() { 'Name', 'Profile', 'Version', - '', 'Git URL', 'Environments', ); From b62cfab699ce3a9e999ecae45639fac6d0cd3747 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 12 Apr 2014 14:28:27 -0400 Subject: [PATCH 0829/3476] Fixing detection of environment's site. --- devshop_projects/devshop_projects.drush.inc | 38 +++++++++++---------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index cc2b32e8b..16717089b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -135,6 +135,7 @@ function drush_devshop_platform_verify(){ // Build the command string $command = "git checkout $git_branch"; + } // Execute @@ -194,6 +195,8 @@ function devshop_projects_post_hosting_delete_task($task, $data) { * Implementation of hook_post_hosting_TASK_TYPE_task */ function devshop_projects_post_hosting_verify_task($task, $data) { + drush_log('[DEVSHOP] devshop_projects_post_hosting_verify_task', 'notice'); + drush_log(print_r($task, 1), 'notice'); // If this is a new platform created by a project, we will create a site here. if ($task->ref->type == 'platform') { @@ -213,28 +216,27 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } // If the platform has a site already, trigger verification, then bail. - $sites = hosting_get_sites_by_status($platform->nid, HOSTING_SITE_ENABLED); - if (!empty($sites)){ + if ($platform->environment->site){ drush_log('[DEVSHOP] Platform already has a site.', 'notice'); - foreach($sites as $site_nid => $site){ - hosting_add_task($site_nid, 'verify'); - drush_log(t('[DEVSHOP] Queued verification for !site', array('!site' => $site->title)), 'notice'); - } + hosting_add_task($platform->environment->site, 'verify'); return; } - - //Check if clone or create a new site. - if ($platform->clone_nid) { - $servers = hosting_get_servers('db'); - $args['target_platform'] = $platform->nid; - $args['new_uri'] = $platform->environment->name .'.'. $platform->project->base_url; - $args['new_db_server'] = key($servers); - drush_log('[DEVSHOP] Cloning site...'); - hosting_add_task($platform->clone_nid, 'clone', $args); - } else { - $site_node = devshop_projects_create_site($platform->project, $platform, $platform->environment->name); - drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. !link', array('!link' => l('node/' . $site_node->nid))); + drush_log('[DEVSHOP] No site found for this environment. Creating...', 'notice'); + + //Check if clone or create a new site. + // if ($platform->clone_nid) { + // $servers = hosting_get_servers('db'); + // $args['target_platform'] = $platform->nid; + // $args['new_uri'] = $platform->environment->name .'.'. $platform->project->base_url; + // $args['new_db_server'] = key($servers); + // drush_log('[DEVSHOP] Cloning site...'); + // hosting_add_task($platform->clone_nid, 'clone', $args); + // } + // else { + + devshop_projects_create_site($platform->project, $platform, $platform->environment->name); + drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. '); } } From 29725822dab3f8e03dd849963181d0e642d82232 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 12 Apr 2014 14:34:30 -0400 Subject: [PATCH 0830/3476] Commenting out automatic verification for now. --- devshop_projects/devshop_projects.drush.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 16717089b..50ceb7b62 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -195,8 +195,6 @@ function devshop_projects_post_hosting_delete_task($task, $data) { * Implementation of hook_post_hosting_TASK_TYPE_task */ function devshop_projects_post_hosting_verify_task($task, $data) { - drush_log('[DEVSHOP] devshop_projects_post_hosting_verify_task', 'notice'); - drush_log(print_r($task, 1), 'notice'); // If this is a new platform created by a project, we will create a site here. if ($task->ref->type == 'platform') { @@ -218,7 +216,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // If the platform has a site already, trigger verification, then bail. if ($platform->environment->site){ drush_log('[DEVSHOP] Platform already has a site.', 'notice'); - hosting_add_task($platform->environment->site, 'verify'); + // hosting_add_task($platform->environment->site, 'verify'); return; } else { @@ -245,7 +243,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { if ($task->ref->type == 'project'){ foreach ($task->ref->project->environments as $environment) { drush_log('[DEVSHOP] Running verify on environment ' . $environment->name, 'ok'); - hosting_add_task($environment->platform, 'verify'); +// hosting_add_task($environment->platform, 'verify'); } } } From 497ea27ee7f7606fb22816615bc062f45e4aa474 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 12 Apr 2014 15:25:11 -0400 Subject: [PATCH 0831/3476] Adding TODO about cloning additional environments. --- devshop_projects/devshop_projects.drush.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 50ceb7b62..f33245502 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -112,6 +112,8 @@ function drush_devshop_platform_verify(){ if (!is_dir($root)) { drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $git_remote, '!root' => $root))); + // @TODO: If another environment exists, copy the repo locally, + // git clean, and checkout the proper branch. // Build the command string $command = "git clone --recursive $git_remote $root"; if ($git_branch) { From 92be7a1943daeb30ef080ef81c1f3f99d9781eb3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 12 Apr 2014 15:37:41 -0400 Subject: [PATCH 0832/3476] Loading platform and site status into environment. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index a89865a51..931ef99ad 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -46,7 +46,7 @@ function devshop_projects_load($node) { // Load Environments // @TODO: Remove environments where the site has been deleted. - $query = db_query("SELECT e.*, s.status FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid WHERE project_nid = %d ORDER BY name", $node->nid); + $query = db_query("SELECT e.*, s.status as site_status, p.status as platform_status FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid LEFT JOIN {hosting_platform} p ON e.platform = p.nid WHERE project_nid = %d ORDER BY name", $node->nid); $environments = array(); while ($environment = db_fetch_object($query)) { From a1ae3cc6e09413e71e3002f736d01e87cc59b155 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 12 Apr 2014 15:47:29 -0400 Subject: [PATCH 0833/3476] Removing unneeded node load for site, now that site and platform status exists. --- devshop_projects/inc/ui.inc | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 2ad7bf361..34e317bc4 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -175,22 +175,20 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $rows = array(); foreach ($project->environments as $env => $environment) { - $site_nid = $environment->site; - $platform_nid = $environment->platform; - $site = node_load($site_nid); - $platform = node_load($platform_nid); - // Skip this if it is not enabled. - if ($site->site_status == HOSTING_SITE_DELETED && $platform->platform_status == HOSTING_PLATFORM_DELETED) { + if ($environment->site_status == HOSTING_SITE_DELETED && $environment->platform_status == HOSTING_PLATFORM_DELETED) { continue; } + // @TODO: Remove. Only devshop_hosting_site_goto_link() requires site node. + $site = node_load($environment->site); + $row = array(); $row[] = "$env"; - if ($site->site_status == HOSTING_SITE_DISABLED){ - $row[] = devshop_hosting_site_goto_link($site) . '' . t('Disabled') . ''; - } elseif ($site->site_status == HOSTING_SITE_QUEUED){ + if ($environment->site_status == HOSTING_SITE_DISABLED){ + $row[] = '' . t('Disabled') . ''; + } elseif ($environment->site_status == HOSTING_SITE_QUEUED || $environment->site_status == HOSTING_SITE_PROCESSING ){ $row[] = '' . t('Installing...') . ''; } else { $row[] = devshop_hosting_site_goto_link($site); @@ -218,20 +216,20 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // $row[] = theme('ctools_dropdown', $site->git_branch, $actions); if (module_exists('devshop_log')) { - $row[] =l(t('Commits'), "node/$site->nid/logs/commits"); + $row[] =l(t('Commits'), "node/$environment->site/logs/commits"); } if (module_exists('hosting_logs')) { - $row[] = l(t('Errors'), "node/$site->nid/logs/errors"); + $row[] = l(t('Errors'), "node/$environment->site/logs/errors"); } if (module_exists('hosting_filemanager')) { - $row[] = l(t('Files'), "node/$site->nid/files/platform"); + $row[] = l(t('Files'), "node/$environment->site/files/platform"); } // Create actions dropdown. $actions = array(); $actions[] = array( 'title' => t('Fork environment'), - 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $site->nid, array( 'query' => array('token' => drupal_get_token($user->uid))), + 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $environment->site, array( 'query' => array('token' => drupal_get_token($user->uid))), 'attributes' => array( 'class' => 'hosting-button-dialog', ), @@ -239,7 +237,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Aegir Tasks - $site_tasklist = hosting_task_fetch_tasks($site->nid); + $site_tasklist = hosting_task_fetch_tasks($environment->site); $site_tasklist['restore']['title'] = t('Restore Backups'); // The actions we want @@ -249,7 +247,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if (!variable_get('hosting_require_disable_before_delete', TRUE)){ $site_actions[] = 'delete'; } else { - if ($site->site_status == HOSTING_SITE_DISABLED){ + if ($environment->site_status == HOSTING_SITE_DISABLED){ $site_actions[] = 'enable'; $site_actions[] = 'delete'; } else { @@ -262,7 +260,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($site_tasklist[$task_name]['task_permitted']){ $actions[] = array( 'title' => $site_tasklist[$task_name]['title'], - 'href' => sprintf("node/%d/%s_%s", $site->nid, $site->type, $task_name), + 'href' => sprintf("node/%d/%s_%s", $environment->site, 'site', $task_name), 'query' => array( 'token' => drupal_get_token($user->uid), 'destination' => "node/$node->nid", @@ -278,11 +276,11 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if (user_access('administer hosting')){ $actions[] = array( 'title' => t('Site Dashboard'), - 'href' => "node/$site->nid", + 'href' => "node/$environment->site", ); $actions[] = array( 'title' => t('Platform Dashboard'), - 'href' => "node/$site->platform", + 'href' => "node/$environment->platform", ); } From e1dd686a7ff4381cd76737a99dfd19e3ba22e743 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 12 Apr 2014 15:55:39 -0400 Subject: [PATCH 0834/3476] Adding TODO about tasks going wrong. --- devshop_projects/inc/nodes.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 931ef99ad..2249e61d9 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -53,6 +53,8 @@ function devshop_projects_load($node) { // Save to environments array $environment->settings = (object) unserialize($environment->settings); $environments[$environment->name] = $environment; + + // @TODO: If something went wrong, load the task status to display errors. } $project->environments = $environments; From 27685472b61e247922b651ec4acc0c1c0c791673 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 13 Apr 2014 10:32:03 -0400 Subject: [PATCH 0835/3476] Lookup install status on environments table. --- devshop_projects/inc/nodes.inc | 2 -- devshop_projects/inc/ui.inc | 12 +++++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2249e61d9..931ef99ad 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -53,8 +53,6 @@ function devshop_projects_load($node) { // Save to environments array $environment->settings = (object) unserialize($environment->settings); $environments[$environment->name] = $environment; - - // @TODO: If something went wrong, load the task status to display errors. } $project->environments = $environments; diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 34e317bc4..00cc8bf8a 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -187,7 +187,17 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row = array(); $row[] = "$env"; if ($environment->site_status == HOSTING_SITE_DISABLED){ - $row[] = '' . t('Disabled') . ''; + $output = '' . t('Disabled') . ''; + + // A "disabled" site might be a failed install. Load and display. + $task = hosting_get_most_recent_task($environment->site, 'install'); + if ($task->task_status == HOSTING_TASK_ERROR) { + $output = t('Site install failed.'); + $output .= ' ' . l(t('View the logs and retry'), "node/$task->nid") . '.'; + } + + $row[] = $output; + } elseif ($environment->site_status == HOSTING_SITE_QUEUED || $environment->site_status == HOSTING_SITE_PROCESSING ){ $row[] = '' . t('Installing...') . ''; } else { From 503b1f09e9502a5eed981cccbd03d44f20ff2c99 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 13 Apr 2014 10:34:45 -0400 Subject: [PATCH 0836/3476] Adding check for re-install. --- devshop_projects/inc/ui.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 00cc8bf8a..ba996445b 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -195,6 +195,9 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $output = t('Site install failed.'); $output .= ' ' . l(t('View the logs and retry'), "node/$task->nid") . '.'; } + elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { + $output = '' . t('Installing...') . ''; + } $row[] = $output; From becb416db24bd62ec49ae47797ba08959ee7a4ee Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 7 Jun 2014 11:58:57 -0400 Subject: [PATCH 0837/3476] Fixing Pull Code form to use new environments objects. --- devshop_projects/devshop_projects.module | 2 +- devshop_projects/tasks/pull.inc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index e8baf8e91..dae5b1694 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -403,5 +403,5 @@ function _devshop_projects_site_has_module($nid, $module) { */ function _devshop_projects_project_has_module($node, $module) { $environment = key($node->project->environments); - return _devshop_projects_site_has_module($node->project->environments[$environment]['site'], $module); + return _devshop_projects_site_has_module($node->project->environments[$environment]->site, $module); } diff --git a/devshop_projects/tasks/pull.inc b/devshop_projects/tasks/pull.inc index 2df78326e..d72d3f88c 100644 --- a/devshop_projects/tasks/pull.inc +++ b/devshop_projects/tasks/pull.inc @@ -17,14 +17,14 @@ function hosting_task_devshop_pull_form($node) { devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environments to pull code to. NOTE: Environments that are tracking a tag or have pull disabled are not listed.'), 'environments', 'Environments', 'checkboxes'); // Remove any environments where pull is disabled or is tracking a tag. - foreach ($node->project_settings['environments'] as $env => $settings){ - if (isset($form['environments']['#options'][$env]) && ($settings['pull_disabled'] || in_array($settings['git_branch'], $node->project_settings['git_tags']))){ - unset($form['environments']['#options'][$env]); + foreach ($node->project->environments as $name => $environment){ + if (isset($form['environments']['#options'][$name]) && ($environment->settings->pull_disabled || in_array($environment->git_ref, $node->project->settings->git['tags']))){ + unset($form['environments']['#options'][$name]); } } // If empty, send them to project page. - if (empty($form['environments']['#options'][$env])){ + if (empty($form['environments']['#options'])){ drupal_set_message(t('You do not have any environments with Pull Code enabled, or they are all tracking tags. !link to be able to pull code.', array('!link' => l(t('Edit the project settings'), "node/$node->nid/edit")))); // @TODO: Close modal window instead? return $form; From aa2ec8f19f3e555700e5f8c75eeeaff67330fcc0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 13:36:06 -0400 Subject: [PATCH 0838/3476] Removing unneeded update hook. --- devshop_projects/devshop_projects.install | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 925c6c811..3ed4d25a8 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -303,21 +303,3 @@ function devshop_projects_update_12() { module_enable(array('hosting_alias')); return array(); } - -/** - * Change "data" column to "settings". - */ -function devshop_projects_update_13(){ - $ret = array(); - $schema = devshop_projects_schema(); - - // Projects schema - $spec = $schema['hosting_devshop_project']['fields']['settings']; - db_change_field($ret, 'hosting_devshop_project', 'data', 'settings', $spec, $keys_new = array()); - - // Environments schema - $spec = $schema['hosting_devshop_project_environment']['fields']['settings']; - db_change_field($ret, 'hosting_devshop_project_environment', 'data', 'settings', $spec, $keys_new = array()); - - return $ret; -} \ No newline at end of file From 0a1093f30d23d3a9d684ab182f7e3eeecb2d6c2d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 14:04:20 -0400 Subject: [PATCH 0839/3476] making update more clear. --- devshop_projects/devshop_projects.install | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 3ed4d25a8..b948fc928 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -297,7 +297,8 @@ function devshop_projects_update_11() { } /** - * Enable hosting_alias. + * Enable hosting_alias module to allow custom domains on sites. + * */ function devshop_projects_update_12() { module_enable(array('hosting_alias')); From 7b52b2e96d698e04b2438e524ef47cab928dc86d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 14:55:20 -0400 Subject: [PATCH 0840/3476] Remove legacy `hosting_devshop_pull_platforms` table. --- devshop_pull/devshop_pull.install | 7 ++++ devshop_pull/devshop_pull.module | 57 ------------------------------- 2 files changed, 7 insertions(+), 57 deletions(-) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index bc63d7bd2..2b4944b9a 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -99,3 +99,10 @@ function devshop_pull_update_6001(){ db_add_field($ret, 'hosting_devshop_pull_projects', 'last_pull_ip', array('type' => 'varchar', 'not null' => TRUE, 'default' => '', 'length' => 15)); return $ret; } + +/** + * Remove legacy `hosting_devshop_pull_platforms` table. + */ +function devshop_pull_update_6002(){ + db_drop_table('hosting_devshop_pull_platforms'); +} \ No newline at end of file diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index a4a483ef3..983e6b8dd 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -196,63 +196,6 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { '#value' => $output, ); } - - // Load Project - elseif ($op == 'load'){ -// $data = db_fetch_array(db_query('SELECT * FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid)); -// if (!empty($data)) { -// unset($data['project_nid']); -// return $data; -// } - } - - // Insert Project - elseif ($op == 'insert'){ -// db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, %d, "%s")', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); - - } - // Update Project - elseif ($op == 'update'){ - // We can't update because devshop_pull might have been enabled after - // project exists -// db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); -// db_query('INSERT INTO {hosting_devshop_pull_projects} (project_nid, pull_method, last_pull, last_pull_status, last_pull_ip) VALUES (%d, %d, %d, "%s", %d)', $node->nid, $node->pull_method, $node->last_pull, $node->last_pull_status, $node->last_pull_ip); - - } - // Delete Project - elseif ($op == 'delete'){ -// db_query('DELETE FROM {hosting_devshop_pull_projects} WHERE project_nid = %d', $node->nid); - - } - } - - // PLATFORMS - elseif ($node->type == 'platform'){ - - // Load Platform - if ($node->type == 'platform' && $op == 'load'){ -// $data = db_fetch_array(db_query("SELECT * FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d", $node->nid)); -// if (!empty($data)){ -// unset($data['project_nid']); -// return $data; -// } - } - - // Insert Platform - elseif ($op == 'insert'){ -// db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); - } - - // Update Platform - elseif ($op == 'update'){ -// db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); -// db_query('INSERT INTO {hosting_devshop_pull_platforms} (platform_nid, project_nid, pull_enabled, pull_reset) VALUES (%d, %d, %d, %d)', $node->nid, $node->project_nid, $node->pull_enabled, $node->pull_reset); - } - - // Delete Project - elseif ($op == 'delete'){ -// db_query('DELETE FROM {hosting_devshop_pull_platforms} WHERE platform_nid = %d', $node->nid); - } } } From e678c0d11e0c5f9740e3c9698fd216679172ae67 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:03:35 -0400 Subject: [PATCH 0841/3476] Removing calls to hosting_devshop_project_object. Just returning 0 for now. --- devshop_pull/devshop_pull.module | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 983e6b8dd..d15dee2f3 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -211,12 +211,8 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { */ function devshop_pull_get_platforms($limit = 5) { - $results = db_query('SELECT * FROM {hosting_devshop_project}'); - while ($project = db_fetch_object($results)){ - $project->data = unserialize($project->data); - } - $result = db_query("SELECT COUNT(dpo.object_nid) FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); - return db_result($result); + // @TODO: Rethink the pull queue altogether. + return 0; } /** From 32858f5eaebf283df86becde36323815bc8b3726 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:18:18 -0400 Subject: [PATCH 0842/3476] Properly using db_drop_table --- devshop_pull/devshop_pull.install | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index 2b4944b9a..7f9dd2e63 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -104,5 +104,7 @@ function devshop_pull_update_6001(){ * Remove legacy `hosting_devshop_pull_platforms` table. */ function devshop_pull_update_6002(){ - db_drop_table('hosting_devshop_pull_platforms'); + $ret = array(); + db_drop_table($ret, 'hosting_devshop_pull_platforms'); + return $ret; } \ No newline at end of file From 10debd038022d9805e10c7d7be28d5323b0fe3ba Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:43:13 -0400 Subject: [PATCH 0843/3476] Fixing up the queue to use the new project and environment classes --- devshop_pull/devshop_pull.module | 47 ++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index d15dee2f3..c76992ef8 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -66,7 +66,7 @@ function devshop_pull_hosting_queues() { 'type' => 'batch', 'name' => t('Pull queue'), 'description' => t('Run git pull on projects configured to do so.'), - 'total_items' => devshop_pull_get_platforms(10), + 'total_items' => count(devshop_pull_get_projects(5)), 'frequency' => strtotime("1 minute", 0), 'singular' => t('project'), 'plural' => t('projects'), @@ -121,6 +121,14 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { // 'onclick' => 'this.select()', // ), // ); + $queues = hosting_get_queues(); + $form['project']['settings']['pull']['queue_enabled'] = array( + '#title' => 'Pull on Queue', + '#type' => 'checkbox', + '#description' => t('Run a "Pull Code" task on a regular basis using Aegir Queues.'), + '#default_value' => $node->project->settings->pull['queue_enabled'], + '#access' => $queues['pull']['enabled'], + ); // @TODO: is there a better way to save certain values? We lose data without these. $form['project']['settings']['pull']['last_pull'] = array( @@ -207,12 +215,22 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { * @return * An array of site nodes that have a pull queue enabled. * - * @TODO Convert to check pull queue sites. */ -function devshop_pull_get_platforms($limit = 5) { +function devshop_pull_get_projects($limit = 5) { + + $results = db_query("SELECT * FROM {hosting_devshop_project} LIMIT %d", $limit); + + $projects = array(); + while ($result = db_fetch_object($results)) { + $node = node_load($result->nid); + $project = $node->project; - // @TODO: Rethink the pull queue altogether. - return 0; + $projects[] = $project; + if ($project->settings->pull['queue_enabled']){ + } + } + + return $projects; } /** @@ -220,24 +238,19 @@ function devshop_pull_get_platforms($limit = 5) { */ function hosting_pull_queue($count) { - $result = db_query("SELECT d.* FROM {hosting_devshop_project_object} dpo LEFT JOIN {hosting_devshop_pull_projects} d ON dpo.project_nid=d.project_nid WHERE d.pull_method = %d AND dpo.object_type='platform' ORDER BY d.last_pull ASC, dpo.object_nid ASC", array(DEVSHOP_PULL_QUEUE)); - - while ($project = db_fetch_object($result)) { - $project_node = node_load($project->project_nid); + $projects = devshop_pull_get_projects($count); - // Create the hosting task - // @TODO: We maybe don't need to pass args here? its saved in the context - // Check for environments set to pull + foreach ($projects as $project) { $environments_to_pull = array(); - foreach ($project_node->settings as $env => $settings) { - if ($settings['pull_enabled']) { - $environments_to_pull[] = $env; + foreach ($project->environments as $environment) { + if (!$environment->settings['pull_disabled']) { + $environments_to_pull[] = $environment->name; } } $args = array(); $args['environments'] = implode(' ', $environments_to_pull); - hosting_add_task($project_node->nid, 'devshop-pull', $args); + hosting_add_task($project->nid, 'devshop-pull', $args); - module_invoke_all('devshop_pull', $project_node); + module_invoke_all('devshop_pull', $project); } } From cf4e36572dd7fbfa7e779f0ec442813afc9644de Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:43:44 -0400 Subject: [PATCH 0844/3476] putting back dev code. --- devshop_pull/devshop_pull.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index c76992ef8..9185b0598 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -225,8 +225,8 @@ function devshop_pull_get_projects($limit = 5) { $node = node_load($result->nid); $project = $node->project; - $projects[] = $project; if ($project->settings->pull['queue_enabled']){ + $projects[] = $project; } } From 27d914978f6f241f066bd0516ddbbb2765922f9d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:44:10 -0400 Subject: [PATCH 0845/3476] Removing Cruft! --- devshop_projects/inc/nodes.inc | 67 ---------------------------------- 1 file changed, 67 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 931ef99ad..84ce92355 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -301,70 +301,3 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { return $additions; } } - -// @TODO: Remove. I believe this is all cruft from back things were a bit more decoupled. -// -// if ($op == 'load') { -// -// $data = db_fetch_array(db_query('SELECT d.project_nid, environment, n.title as project, git_url, git_branch, d.drupal_path, d.clone_nid FROM {hosting_devshop_project_object} d LEFT JOIN {node} n ON n.nid = d.project_nid LEFT JOIN {hosting_devshop_project} p ON n.nid = p.nid WHERE object_nid = %d', $node->nid)); -// -// $data[$node->type . "_nid"] = $node->nid; -// return $data; -// } -// -// // On insert or update, insert records saving this objects project and environment -// if ($op == 'update' || $op == 'insert') { -// //Special case for migrate site. Is need copy the information to the new site. -// if ($op == 'insert' && $node->type == 'site' && $node->import && !$node->verified) { -// $platform = node_load($node->platform); -// $node->project = $platform->project; -// $node->project_nid = $platform->project_nid; -// $node->environment = $platform->environment; -// $node->git_branch = $platform->git_branch; -// $node->drupal_path = $platform->drupal_path; -// $node->clone_nid = $platform->clone_nid; -// } -// if (!empty($node->project) && !empty($node->environment)) { -// // Get the project node by name. -// $project = hosting_context_load($node->project); -// -// // Save to table -// $data = new stdClass(); -// $data->project_nid = $project->nid; -// $data->object_nid = $node->nid; -// $data->object_type = $node->type; -// $data->environment = $node->environment; -// $data->git_branch = $node->git_branch; -// $data->drupal_path = $node->drupal_path; -// -// //Site used for cloned the new site or platorm -// if (isset($node->clone_nid)) { -// $data->clone_nid = $node->clone_nid; -// } -// -// if ($op == 'insert') { -// drupal_write_record('hosting_devshop_project_object', $data); -// } -// else { -// drupal_write_record('hosting_devshop_project_object', $data, 'object_nid'); -// } -// -// //If we are updating or inserting a platform, update all sites with the correct git_branch and environment -// if ($node->type == 'platform') { -// $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = %d", $node->nid); -// while ($site = db_fetch_object($result)) { -// db_query('UPDATE {hosting_devshop_project_object} SET git_branch = "%s", environment = "%s", drupal_path = "%s" WHERE object_nid = %d', $node->git_branch, $node->environment, $node->drupal_path, $site->nid); -// } -// } -// -// // If we are updating or inserting sites, check for live domain aliases. -// $live_domain_alias = "{$node->environment}.{$project->live_domain}"; -// -// // If project should use live_domain_aliases, and site does not have the alias already... -// if ($node->type == 'site' && $project->live_domain_aliases && array_search($live_domain_alias, $node->aliases) === FALSE) { -// $node->aliases[] = $live_domain_alias; -// } -// } -// } -// } -//} From 4287f4a05f3fdef8cbbb67859f8825ae88ed8714 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:46:20 -0400 Subject: [PATCH 0846/3476] Removing hosting_devshop_pull_platforms from schema! --- devshop_pull/devshop_pull.install | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/devshop_pull/devshop_pull.install b/devshop_pull/devshop_pull.install index 7f9dd2e63..27c8c6140 100644 --- a/devshop_pull/devshop_pull.install +++ b/devshop_pull/devshop_pull.install @@ -38,32 +38,6 @@ function devshop_pull_schema() { ), 'primary key' => array('project_nid'), ); - $schema['hosting_devshop_pull_platforms'] = array( - 'fields' => array( - 'project_nid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'platform_nid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'pull_enabled' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'pull_reset' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('platform_nid'), - ); - return $schema; } From 6346676fa3306f9ee3f9b20a105ceae3d0ed6bd8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:47:41 -0400 Subject: [PATCH 0847/3476] Removing hosting_devshop_project_object! --- devshop_projects/devshop_projects.install | 60 ++++------------------- 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index b948fc928..8493aa745 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -52,57 +52,6 @@ function devshop_projects_schema() { 'primary key' => array('nid'), ); - // @TODO: Delete this schema once update scripts work. - $schema['hosting_devshop_project_object'] = array( - 'fields' => array( - 'project_nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Project/Node ID.', - ), - 'object_nid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Site NID.', - ), - 'object_type' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => 10, - 'default' => '', - 'description' => 'The node ID of the platform.', - ), - 'environment' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => 64, - 'default' => '', - 'description' => 'Environment name. Either dev test live or platform name.', - ), - 'git_branch' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - 'description' => 'The current branch of this site or platform.', - ), - 'drupal_path' => array( - 'type' => 'text', - 'size' => 'big', - 'not null' => FALSE, - ), - 'clone_nid' => array( - 'type' => 'int', - 'not null' => FALSE, - 'default' => 0, - 'unsigned' => TRUE, - ), - ), - 'primary key' => array('object_nid'), - ); - // Table for tracking environments $schema['hosting_devshop_project_environment'] = array( 'fields' => array( @@ -304,3 +253,12 @@ function devshop_projects_update_12() { module_enable(array('hosting_alias')); return array(); } + +/** + * Remove legacy `hosting_devshop_pull_platforms` table. + */ +function devshop_pull_update_13(){ + $ret = array(); + db_drop_table($ret, 'hosting_devshop_project_object'); + return $ret; +} \ No newline at end of file From 43e568aa45087ad2952ef6c1c4e81adcb4edb395 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:48:40 -0400 Subject: [PATCH 0848/3476] uh, yeah. --- devshop_projects/devshop_projects.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 8493aa745..dcd08cf01 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -257,7 +257,7 @@ function devshop_projects_update_12() { /** * Remove legacy `hosting_devshop_pull_platforms` table. */ -function devshop_pull_update_13(){ +function devshop_projects_update_13(){ $ret = array(); db_drop_table($ret, 'hosting_devshop_project_object'); return $ret; From aa66331acda62592c9e772b5bb69e3c00cb1dc83 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:49:43 -0400 Subject: [PATCH 0849/3476] Fixing comment. --- devshop_projects/devshop_projects.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index dcd08cf01..e3ccac0c9 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -255,7 +255,7 @@ function devshop_projects_update_12() { } /** - * Remove legacy `hosting_devshop_pull_platforms` table. + * Remove legacy `hosting_devshop_project_object` table. */ function devshop_projects_update_13(){ $ret = array(); From 33ee0036222153abd5531d5329a7ede07a57b7af Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 15:58:29 -0400 Subject: [PATCH 0850/3476] Getting proper pull data. --- devshop_pull/devshop_pull.module | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 9185b0598..50265fbbd 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -171,26 +171,29 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { if ($node->type == 'project'){ // View Project - if ($op == 'view' && $node->project_settings['pull']['pull_enabled']){ + if ($op == 'view' && $node->project->settings->pull['pull_enabled']){ + + $pull_data = $node->project->settings->pull; + module_load_include('inc', 'devshop_pull'); $url = _devshop_pull_callback_url($node); $output = ''; - $status = (int) $node->project_settings['pull']['last_pull_status']; + $status = (int) $pull_data['last_pull_status']; // If access denied, provide link to settings page if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ $output = '' . t('Access Denied') . '
'; - $output .= '' . hosting_format_interval($node->project_settings['pull']['last_pull']) . '
'; + $output .= '' . hosting_format_interval($pull_data['last_pull']) . '
'; $output .= t('Commit notification recieved from %ip, but the IP is not allowed to trigger tasks. See !link.', array( '!link' => l(t('DevShop Pull Settings'), 'admin/hosting/devshop_pull'), - '%ip' => $node->project_settings['pull']['last_pull_ip'], + '%ip' => $pull_data['last_pull_ip'], )); } // If OK, show how much time has passed. elseif ($status == DEVSHOP_PULL_STATUS_OK) { - $output = hosting_format_interval($node->project_settings['pull']['last_pull']); + $output = hosting_format_interval($pull_data['last_pull']); } // Otherwise, we assume no commit notification recieved. else { From 3e5c8c737852af145368bea93b4611c39cc219db Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 16:02:05 -0400 Subject: [PATCH 0851/3476] Using new project settings to store last pull info. --- devshop_pull/devshop_pull.inc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 5f3137783..6e4806f1f 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -4,10 +4,12 @@ /** * URL callback that is invoked by git to create a code pull task. */ -function devshop_pull_callback($project, $hash) { +function devshop_pull_callback($project_hosting_context, $hash) { // Load the project node & list of allowed IPs - $project_node = hosting_context_load(str_replace('project_', '', $project)); + $project_node = hosting_context_load(str_replace('project_', '', $project_hosting_context)); + $pull_settings = $project_node->project->settings->pull; + $allowed_ips = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); array_filter(array_map('trim', $allowed_ips)); @@ -29,7 +31,7 @@ function devshop_pull_callback($project, $hash) { $status = DEVSHOP_PULL_STATUS_INVALID_CODE; } // Make sure the project has pull callback enabled - elseif (!$project_node->project_settings['pull']['pull_enabled']){ + elseif (!$pull_settings['pull_enabled']){ $message = "Project $project is NOT configured to use Pull Code URL callback!"; } // Make sure the client's IP address is on the list @@ -51,9 +53,9 @@ function devshop_pull_callback($project, $hash) { } // Save the project node with last pull info. - $project_node->project_settings['pull']['last_pull'] = time(); - $project_node->project_settings['pull']['last_pull_status'] = DEVSHOP_PULL_STATUS_OK; - $project_node->project_settings['pull']['last_pull_ip'] = ip_address(); + $pull_settings['last_pull'] = time(); + $pull_settings['last_pull_status'] = DEVSHOP_PULL_STATUS_OK; + $pull_settings['last_pull_ip'] = ip_address(); node_save($project_node); // Output a message, no matter what. From 0923d9b698ffc41a908eadadc2a83b01840692ee Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 16:04:44 -0400 Subject: [PATCH 0852/3476] Fixing environments to pull check. --- devshop_pull/devshop_pull.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 6e4806f1f..ec3786344 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -8,6 +8,7 @@ function devshop_pull_callback($project_hosting_context, $hash) { // Load the project node & list of allowed IPs $project_node = hosting_context_load(str_replace('project_', '', $project_hosting_context)); + $project = $project_node->project; $pull_settings = $project_node->project->settings->pull; $allowed_ips = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); @@ -15,9 +16,9 @@ function devshop_pull_callback($project_hosting_context, $hash) { // Check for environments set to pull $environments_to_pull = array(); - foreach ($project_node->settings as $env => $settings) { - if ($settings['pull_enabled']) { - $environments_to_pull[] = $env; + foreach ($project->environments as $environment) { + if (!$environment->settings->pull_disabled) { + $environments_to_pull[] = $environment->name; } } From 233b044458c25542c639f31996d766c8089d0c73 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 16:08:04 -0400 Subject: [PATCH 0853/3476] Save last pull status to project node settings. --- devshop_pull/devshop_pull.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index ec3786344..c892fbf21 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -57,6 +57,9 @@ function devshop_pull_callback($project_hosting_context, $hash) { $pull_settings['last_pull'] = time(); $pull_settings['last_pull_status'] = DEVSHOP_PULL_STATUS_OK; $pull_settings['last_pull_ip'] = ip_address(); + + $project_node->project->settings->pull = $pull_settings; + node_save($project_node); // Output a message, no matter what. From 1ff4e6344121d54197215afe4ac6cf83ffb197ed Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 16:18:19 -0400 Subject: [PATCH 0854/3476] Only show URL if pull on commit is enabled. --- devshop_pull/devshop_pull.module | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 50265fbbd..b91fb6f20 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -109,18 +109,27 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#type' => 'checkbox', '#description' => t('Run a "Pull Code" task when a commit notification is received. All environments set to a branch will have their code updated.'), '#default_value' => $node->project->settings->pull['pull_enabled'], + '#attributes' => array( + 'onclick' => 'if ($(this).attr("checked")) { $("#pull-url").show(); } else { $("#pull-url").hide(); }', + ), ); -// module_load_include('inc', 'devshop_pull'); -// $form['project_settings']['pull']['pull_url'] = array( -// '#type' => 'textfield', -// '#title' => t('Pull Trigger URL'), -// '#value' => _devshop_pull_callback_url($node), -// '#description' => t('Configure your repo to hit this URL when it receives a commit.'), -// '#attributes' => array( -// 'onclick' => 'this.select()', -// ), -// ); + if (!$node->project->settings->pull['pull_enabled']){ + $display_none = 'style="display:none;"'; + } + else { + $display_none = ''; + } + + module_load_include('inc', 'devshop_pull'); + $form['project']['settings']['pull']['pull_url'] = array( + '#type' => 'item', + '#title' => t('Pull Trigger URL'), + '#value' => _devshop_pull_callback_url($node), + '#description' => t('Configure your repo to hit this URL when it receives a commit.'), + '#prefix' => '
', + '#suffix' => '
', + ); $queues = hosting_get_queues(); $form['project']['settings']['pull']['queue_enabled'] = array( '#title' => 'Pull on Queue', From bb39a8fe3e69d88d98dfc00177587f341a7b04dc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 16:45:13 -0400 Subject: [PATCH 0855/3476] Saving "project" property as is into the alias. --- devshop_projects/devshop_projects.drush.inc | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index f33245502..8aeb43985 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -270,7 +270,7 @@ function devshop_projects_hosting_project_context_options(&$task) { node_save($task->ref); } - // Save project object to context. + // Save project object to drush alias (aegir context). if (isset($task->ref->project)) { $task->context_options['server'] = '@server_master'; $task->context_options['project_name'] = $task->ref->title; @@ -286,18 +286,8 @@ function devshop_projects_hosting_project_context_options(&$task) { function devshop_projects_drush_context_import($context, &$node) { if ($context->type == 'project') { $node->title = $context->project_name; - $node->type = $context->type; - - // @TODO: Fix up for $node->project - $node->code_path = $context->code_path; - $node->drupal_path = $context->drupal_path; - $node->base_url = $context->base_url; - $node->install_profile = $context->install_profile; - - $node->git_url = $context->git_url; - $branches = getBranchesAndTags($context->git_url); - $node->project->git['branches'] = $branches['branches']; - $node->project->git['tags'] = $branches['tags']; + $node->type = 'project'; + $node->project = $context->project; } } From b54ccad2bbb2049d694561b8153cebb8a901bf81 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:06:10 -0400 Subject: [PATCH 0856/3476] fixing live environment settings. --- devshop_projects/inc/forms.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 49626e837..78a658fa8 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -233,14 +233,17 @@ function devshop_projects_form(&$node) { ); $live_alias_options['other'] = 'Other...'; - if (empty($live_alias_options[$node->project->live['live_environment']])){ + if (empty($live_alias_options[$node->project->settings->live['live_environment']])){ // No live environment $live_environment_default_value = ''; } - elseif (!empty($node->project->live['live_environment_alias'])){ + elseif (!empty($node->project->settings->live['live_environment_alias'])){ // "other" live environment $live_environment_default_value = 'other'; } + else { + $live_environment_default_value = $node->project->settings->live['live_environment']; + } $form['project']['settings']['live']['live_environment'] = array( '#type' => 'select', From bed722a8b73d4ada24d74fdbf7fbbdb5681ca388 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:13:50 -0400 Subject: [PATCH 0857/3476] Commenting out live domain stuff for now. --- devshop_projects/inc/forms.inc | 66 ++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 78a658fa8..e7fd338de 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -232,37 +232,43 @@ function devshop_projects_form(&$node) { '#title' => t('Live Environment Settings'), ); - $live_alias_options['other'] = 'Other...'; - if (empty($live_alias_options[$node->project->settings->live['live_environment']])){ - // No live environment - $live_environment_default_value = ''; - } - elseif (!empty($node->project->settings->live['live_environment_alias'])){ - // "other" live environment - $live_environment_default_value = 'other'; - } - else { - $live_environment_default_value = $node->project->settings->live['live_environment']; - } +// @TODO: Not ready yet! Rethink this. +// 1. We should probably allow a "production mode" on environments, disabling sync data destination and allowing for other things. +// 2. We should break out domain management into it's own thing. The way this is setup is hard to understand. +// - $form['project']['settings']['live']['live_environment'] = array( - '#type' => 'select', - '#title' => t('Live Environment'), - '#options' => $live_alias_options, - '#required' => FALSE, - '#default_value' => $live_environment_default_value, - '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), - ); - $form['project']['settings']['live']['live_environment_alias'] = array( - '#title' => t('Live Site Drush Alias'), - '#type' => 'textfield', - '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), - '#field_prefix' => '@', - '#size' => 40, - '#default_value' => $node->project->live['live_environment_alias'], - '#size' => 50, - '#maxlength' => 50, - ); + +// $live_alias_options['other'] = 'Other...'; +// if (empty($live_alias_options[$node->project->settings->live['live_environment']])){ +// // No live environment +// $live_environment_default_value = ''; +// } +// elseif (!empty($node->project->settings->live['live_environment_alias'])){ +// // "other" live environment +// $live_environment_default_value = 'other'; +// } +// else { +// $live_environment_default_value = $node->project->settings->live['live_environment']; +// } +// +// $form['project']['settings']['live']['live_environment'] = array( +// '#type' => 'select', +// '#title' => t('Live Environment'), +// '#options' => $live_alias_options, +// '#required' => FALSE, +// '#default_value' => $live_environment_default_value, +// '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), +// ); +// $form['project']['settings']['live']['live_environment_alias'] = array( +// '#title' => t('Live Site Drush Alias'), +// '#type' => 'textfield', +// '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), +// '#field_prefix' => '@', +// '#size' => 40, +// '#default_value' => $node->project->live['live_environment_alias'], +// '#size' => 50, +// '#maxlength' => 50, +// ); // Live Domain $form['project']['settings']['live']['live_domain'] = array( From 4b17bdb0ddebe59db7f6c58e2861fbd2c0564294 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:16:02 -0400 Subject: [PATCH 0858/3476] Renaming forms to make more sense. --- devshop_projects/inc/forms.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e7fd338de..fdba6e7f6 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -229,7 +229,7 @@ function devshop_projects_form(&$node) { // Live Environment settings. $form['project']['settings']['live'] = array( '#type' => 'fieldset', - '#title' => t('Live Environment Settings'), + '#title' => t('Live Domain Settings'), ); // @TODO: Not ready yet! Rethink this. @@ -284,8 +284,8 @@ function devshop_projects_form(&$node) { if (module_exists('hosting_alias')){ $form['project']['settings']['live']['live_domain_aliases'] = array( '#type' => 'checkbox', - '#title' => t('Use environment aliases'), - '#description' => t('Create aliases for all environments as subdomains of the live domain.'), + '#title' => t('Environment Subdomains'), + '#description' => t('Create subdomains under the live domain for all environments..'), '#default_value' => $node->project->live['live_domain_aliases'], '#weight' => 6, ); From 4e9ec94d1b95186dc605ae63fd0df9f716d3db6b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:16:40 -0400 Subject: [PATCH 0859/3476] Moving live domain settings. --- devshop_projects/inc/forms.inc | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index fdba6e7f6..5e293f30b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -232,6 +232,27 @@ function devshop_projects_form(&$node) { '#title' => t('Live Domain Settings'), ); + // Live Domain + $form['project']['settings']['live']['live_domain'] = array( + '#type' => 'textfield', + '#title' => t('Live domain'), + '#description' => t('The live domain for this project. Do not include "www".'), + '#size' => 40, + '#default_value' => $node->project->live['live_domain'], + '#weight' => 5, + ); + + // Use aliases + if (module_exists('hosting_alias')){ + $form['project']['settings']['live']['live_domain_aliases'] = array( + '#type' => 'checkbox', + '#title' => t('Environment Subdomains'), + '#description' => t('Create subdomains under the live domain for all environments..'), + '#default_value' => $node->project->live['live_domain_aliases'], + '#weight' => 6, + ); + } + // @TODO: Not ready yet! Rethink this. // 1. We should probably allow a "production mode" on environments, disabling sync data destination and allowing for other things. // 2. We should break out domain management into it's own thing. The way this is setup is hard to understand. @@ -270,27 +291,6 @@ function devshop_projects_form(&$node) { // '#maxlength' => 50, // ); - // Live Domain - $form['project']['settings']['live']['live_domain'] = array( - '#type' => 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain for this project. Do not include "www".'), - '#size' => 40, - '#default_value' => $node->project->live['live_domain'], - '#weight' => 5, - ); - - // Use aliases - if (module_exists('hosting_alias')){ - $form['project']['settings']['live']['live_domain_aliases'] = array( - '#type' => 'checkbox', - '#title' => t('Environment Subdomains'), - '#description' => t('Create subdomains under the live domain for all environments..'), - '#default_value' => $node->project->live['live_domain_aliases'], - '#weight' => 6, - ); - } - return $form; } From 47f33b8ba109a7fa2ad4ef4f8b9c407769b5dfc5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:17:37 -0400 Subject: [PATCH 0860/3476] Fixing up domain name settings. --- devshop_projects/inc/forms.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 5e293f30b..5c160de39 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -229,7 +229,7 @@ function devshop_projects_form(&$node) { // Live Environment settings. $form['project']['settings']['live'] = array( '#type' => 'fieldset', - '#title' => t('Live Domain Settings'), + '#title' => t('Domain Name Settings'), ); // Live Domain @@ -238,7 +238,7 @@ function devshop_projects_form(&$node) { '#title' => t('Live domain'), '#description' => t('The live domain for this project. Do not include "www".'), '#size' => 40, - '#default_value' => $node->project->live['live_domain'], + '#default_value' => $node->project->settings->live['live_domain'], '#weight' => 5, ); @@ -248,7 +248,7 @@ function devshop_projects_form(&$node) { '#type' => 'checkbox', '#title' => t('Environment Subdomains'), '#description' => t('Create subdomains under the live domain for all environments..'), - '#default_value' => $node->project->live['live_domain_aliases'], + '#default_value' => $node->project->settings->live['live_domain_aliases'], '#weight' => 6, ); } From cde3737fe46444615d29fcad41a8552ea8f55b31 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:18:06 -0400 Subject: [PATCH 0861/3476] Removing old code! --- devshop_projects/inc/forms.inc | 45 +--------------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 5c160de39..599b68ca8 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -298,50 +298,7 @@ function devshop_projects_form(&$node) { * Submit function for save data of platforms. */ function devshop_projects_submit_settings($form, &$form_state) { -// -// // Go through and save our settings to site and platform nodes. -// $project_node = node_load($form_state['values']['nid']); -// $settings_info = module_invoke_all('devshop_project_settings', $project_node); -// $nodes = array(); -// -// // Save all items under project to data -// // $form_state['values']['project']['data'] = (object) $form_state['values']['project']; -// -// // Go through each environment -// // @TODO: Move to devshop_project_update(); -// foreach ($form_state['values']['project']['environments'] as $environment_name => $settings){ -// //$form_state['values']['project']['environments'][$environment_name]['settings'] = $form_state['values']['project']['environments'][$environment_name]; -// -// // Then through each setting of each environment -// foreach ($settings as $setting_name => $setting_value){ -// -// //Find the node type for this setting. -// $node_type = $settings_info[$setting_name]['#node_type']; -// -// // Load the site or platform node for this environment, then set the value and save. -// if (!empty($project_node->project->environments[$environment_name]->{$node_type})){ -// $nid = $project_node->project->environments[$environment_name]->{$node_type}; -// $nodes[$nid] = node_load($nid); -// $nodes[$nid]->no_verify = TRUE; -// -// //If changed database then execute migrate task. -// if ($setting_name == 'db_server' && $nodes[$nid]->{$setting_name} != $setting_value) { -// $args['target_platform'] = $nodes[$nid]->platform; -// $args['new_uri'] = $nodes[$nid]->title; -// $args['new_db_server'] = $setting_value; -// -// hosting_add_task($nid, 'migrate', $args); -// } -// $nodes[$nid]->{$setting_name} = $setting_value; -// } -// } -// } -// -// // Go save all nodes -// foreach ($nodes as $nid => $node){ -// node_save($node); -// } -// + } /** From 2abde3869c41acfec07392867b2ae3744f665869 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:18:23 -0400 Subject: [PATCH 0862/3476] Removing old code! --- devshop_projects/inc/forms.inc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 599b68ca8..e0e6f26c7 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -294,13 +294,6 @@ function devshop_projects_form(&$node) { return $form; } -/** - * Submit function for save data of platforms. - */ -function devshop_projects_submit_settings($form, &$form_state) { - -} - /** * Implementation of hook_validate(). */ From 49f49e119d2ee63f5b5afde76f89c1f2ffaa1bfd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:19:20 -0400 Subject: [PATCH 0863/3476] Removing bad live environment stuff. --- devshop_projects/inc/forms.inc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e0e6f26c7..2279c4230 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -343,11 +343,4 @@ function devshop_projects_validate($node, &$form) { form_set_error('code_path', t('Code path directory already exists.')); } - // If live environment is selected, do not save live environment alias - if ($node->live_environment == 'other'){ - $node->live_environment = $node->live_environment_alias; - } - else { - $node->live_environment_alias = ''; - } } From 2cbe9367af3bbc3217f25f7592bf3f4fff4f4a0e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:19:25 -0400 Subject: [PATCH 0864/3476] whitespace --- devshop_projects/inc/forms.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 2279c4230..74e7738d6 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -342,5 +342,4 @@ function devshop_projects_validate($node, &$form) { if (!$node->retry && $add && file_exists($cp)) { form_set_error('code_path', t('Code path directory already exists.')); } - } From ccd646bbdf25d57d583d89ee1c98fc4a6a99c7e4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:20:39 -0400 Subject: [PATCH 0865/3476] Removing old code! --- devshop_projects/devshop_projects.module | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index dae5b1694..25b8cecc6 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -149,20 +149,6 @@ function devshop_projects_devshop_project_settings($node = NULL){ $project = $node->project; $settings = array(); -// $settings['site'] = array( -// '#node_type' => 'site', -// '#type' => 'hidden', -// ); -// $settings['platform'] = array( -// '#node_type' => 'platform', -// '#type' => 'hidden', -// ); -// $settings['git_ref'] = array( -// '#title' => t('Git Branch/Tag'), -// '#node_type' => 'platform', -// '#type' => 'select', -// '#options' => devshop_projects_git_ref_options($project), -// ); $http_servers = hosting_get_servers('http'); if (count($http_servers)) { From 94d90c3e19216a68623f0db28e8dff98b22dc6fa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:27:19 -0400 Subject: [PATCH 0866/3476] Adding production mode setting to form. --- devshop_projects/devshop_projects.module | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 25b8cecc6..e3b17e55e 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -170,6 +170,12 @@ function devshop_projects_devshop_project_settings($node = NULL){ ); } + $settings['production_mode'] = array( + '#title' => t('Production Mode'), + '#type' => 'checkbox', + '#default_value' => 0, + '#description' => t('Prevent overwriting of data, disabling, and other interruptive actions.'), + ); return $settings; } From 8815f91587ad80aa1f8b84aa7515445021378e52 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:33:36 -0400 Subject: [PATCH 0867/3476] Protecting production mode sites from being targets for Sync. --- devshop_projects/tasks/sync.inc | 35 ++++++++++++--------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/devshop_projects/tasks/sync.inc b/devshop_projects/tasks/sync.inc index c4aa01a65..9765c7e7f 100644 --- a/devshop_projects/tasks/sync.inc +++ b/devshop_projects/tasks/sync.inc @@ -12,6 +12,7 @@ function hosting_task_devshop_sync_form($node) { // Get nid $nid = $node->nid; + $project = $node->project; $form = array(); devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); @@ -22,17 +23,17 @@ function hosting_task_devshop_sync_form($node) { $form['destination']['#prefix'] = '
'; $form['destination']['#suffix'] = '
'; - if (!empty($form['destination']['#options'][$node->live_environment])){ - // Remove live site from destination options. - unset($form['destination']['#options'][$node->live_environment]); + // Look for any production mode sites. + foreach ($project->environments as $environment) { + if ($environment->settings->production_mode){ + // Remove live site from destination options. + unset($form['destination']['#options'][$environment->name]); - $source_options = $form['source']['#options']; - unset($source_options[$node->live_environment]); - $form['source']['#options'] = array_merge(array($node->live_environment => $node->live_environment . " (LIVE ENVIRONMENT)"), $source_options); - $form['source']['#default_value'] = $node->live_environment; - } - elseif (!empty($node->live_environment_alias)){ - $form['source']['#options']["@" . $node->live_environment_alias] = "@" . $node->live_environment_alias . " (LIVE ENVIRONMENT)"; + $source_options = $form['source']['#options']; + unset($source_options[$environment->name]); + $form['source']['#options'] = array_merge(array($environment->name => $environment->name . " (Production Mode)"), $source_options); + $form['source']['#default_value'] = $environment->name; + } } $form['database'] = array( @@ -49,7 +50,7 @@ function hosting_task_devshop_sync_form($node) { '#suffix' => '', ); - + // We have to add the fieldset via prefix because of the way aegir handles this form. $form['pull'] = array( '#title' => t('Pull code on Destination'), '#type' => 'checkbox', @@ -65,18 +66,8 @@ function hosting_task_devshop_sync_form($node) { $form['revert'] = array( '#title' => t('Revert all features on Destination'), '#type' => 'checkbox', - '#default_value' => $has_features, - '#access' => $has_features, + '#default_value' => 1, ); - } else{ - // I think its better UI just to hide it? If they need features it will be enabled! - //$form['actions']['revert'] = array( - // '#title' => t('Revert all features on Destination after content sync?'), - // '#type' => 'checkbox', - // '#description' => t('This site doesn\'t have features.module enabled. Please enable and then "Verify" the site.'), - // '#default_value' => FALSE, - // '#disabled' => TRUE, - // ); } $form['cache'] = array( '#title' => t('Clear cache on Destination'), From 9d39686453cdfab80de08529a38bfb04cbec6816 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:34:32 -0400 Subject: [PATCH 0868/3476] Better description of sql sync --- devshop_projects/tasks/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index 593dabf25..64b739d69 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -37,7 +37,7 @@ function devshop_projects_hosting_tasks() { ); $tasks['project']['devshop-sync'] = array( 'title' => t('Sync Data'), - 'description' => t('Sync database and files from another site and (optionally) run update.php, clear cache, and revert features.'), + 'description' => t('Copy database and files from one environment to another.', 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); From 79182d89fe0dca62e2e0a04d31a22cedcbbebf7a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:34:43 -0400 Subject: [PATCH 0869/3476] bad code --- devshop_projects/tasks/tasks.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index 64b739d69..8135007f3 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -37,7 +37,7 @@ function devshop_projects_hosting_tasks() { ); $tasks['project']['devshop-sync'] = array( 'title' => t('Sync Data'), - 'description' => t('Copy database and files from one environment to another.', + 'description' => t('Copy database and files from one environment to another.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); From de35ac31aa9838c53a8594da5827950a0498be9c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:36:57 -0400 Subject: [PATCH 0870/3476] Fixing live site display. --- devshop_projects/inc/ui.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index ba996445b..2b9e2a309 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -121,8 +121,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // '#weight' => -8 //); - if (!empty($project->live_domain)) { - $url = 'http://' . $project->live_domain; + if (!empty($project->settings->live['live_domain'])) { + $url = 'http://' . $project->settings->live['live_domain']; $node->content['info']['live_domain'] = array( '#type' => 'item', From 2a9f806c8ed36e13e323bc9782a4e987074d6c08 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:41:53 -0400 Subject: [PATCH 0871/3476] Adding production mode tables! --- devshop_projects/inc/ui.inc | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 2b9e2a309..7014c0335 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -173,6 +173,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Environments $rows = array(); + $production_rows = array(); + foreach ($project->environments as $env => $environment) { @@ -298,12 +300,31 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } $row[] = theme('ctools_dropdown', t('Actions'), $actions); - $rows[] = $row; + + if ($environment->settings->production_mode){ + $production_rows[] = $row; + } + else { + $rows[] = $row; + } } $header = array(); $table = theme('table', $header, $rows, array('class' => 'environments-table')); - $node->content['sites'] = array( + $node->content['production_environments'] = array( + '#type' => 'fieldset', + '#title' => t('Production'), + '#weight' => 11, + '#attributes' => array( + 'class' => 'project-environments', + ), + ); + $node->content['production_environments']['table'] = array( + '#type' => 'item', + '#value' => theme('table', $header, $production_rows, array('class' => 'environments-table')), + ); + + $node->content['environments'] = array( '#type' => 'fieldset', '#title' => t('Environments'), '#weight' => 12, @@ -313,7 +334,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); $link = l(t('Create New Environment'), 'node/' . $node->nid . '/project_devshop-create', array('attributes' => array('class' => 'create-new-environment hosting-button-dialog'), 'query' => array('token' => drupal_get_token($user->uid)))); - $node->content['sites']['table'] = array( + $node->content['environments']['table'] = array( '#type' => 'item', '#value' => $table, '#suffix' => $link, From ce1f1fae0bbfaf30b2b3ed0e24f3ecf596bf1c08 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 17:45:05 -0400 Subject: [PATCH 0872/3476] Only show restore backups or disable task if site is not in production mode. --- devshop_projects/inc/ui.inc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 7014c0335..e9cf67ed6 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -256,7 +256,11 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $site_tasklist['restore']['title'] = t('Restore Backups'); // The actions we want - $site_actions = array('flush_cache', 'login_reset', 'backup', 'restore'); + $site_actions = array('flush_cache', 'login_reset', 'backup'); + + if (!$environment->settings->production_mode){ + $site_actions[] = 'restore'; + } // Add disable or delete task based on hosting variable. if (!variable_get('hosting_require_disable_before_delete', TRUE)){ @@ -265,7 +269,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($environment->site_status == HOSTING_SITE_DISABLED){ $site_actions[] = 'enable'; $site_actions[] = 'delete'; - } else { + } elseif (!$environment->settings->production_mode){ $site_actions[] = 'disable'; } } From 64c1877a1a0d302c37b7c7b19c8282a4ca4da7fc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 18:06:30 -0400 Subject: [PATCH 0873/3476] New name for environment aliases. --- devshop_projects/inc/forms.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 74e7738d6..137cbd797 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -244,11 +244,11 @@ function devshop_projects_form(&$node) { // Use aliases if (module_exists('hosting_alias')){ - $form['project']['settings']['live']['live_domain_aliases'] = array( + $form['project']['settings']['live']['environment_aliases'] = array( '#type' => 'checkbox', '#title' => t('Environment Subdomains'), '#description' => t('Create subdomains under the live domain for all environments..'), - '#default_value' => $node->project->settings->live['live_domain_aliases'], + '#default_value' => $node->project->settings->live['environment_aliases'], '#weight' => 6, ); } From 39540ec5b33f426e73a56298ccd39d5abfce0590 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 22:12:07 -0400 Subject: [PATCH 0874/3476] Remove commented out code. --- devshop_projects/devshop_projects.drush.inc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 8aeb43985..e01f210e9 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -239,15 +239,6 @@ function devshop_projects_post_hosting_verify_task($task, $data) { drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. '); } } - - // When a project is verified, queue a verification for all platforms - // @TODO: Figure out a better way to verify, only when needed. - if ($task->ref->type == 'project'){ - foreach ($task->ref->project->environments as $environment) { - drush_log('[DEVSHOP] Running verify on environment ' . $environment->name, 'ok'); -// hosting_add_task($environment->platform, 'verify'); - } - } } /** From 655f7343042bf85f2dd658498990563b094aa1ac Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 22:25:55 -0400 Subject: [PATCH 0875/3476] Attempting to refactor saving site nodes with aliases. --- devshop_projects/inc/nodes.inc | 61 +++++++++++++--------------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 84ce92355..3b372b945 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -220,47 +220,34 @@ function devshop_projects_update($node) { $objects['platform']->needs_save = TRUE; } - // Save site and platform if needed. - foreach ($objects as $type => $object){ - if ($object->needs_save) { - node_save($object); + // Save environment aliases + if (!empty($project->settings['live']['live_domain']) && $project->settings['live']['environment_aliases']) { + $domain = $name . '.' . $project->settings['live']['live_domain']; + if (array_search($domain, $objects['site']->aliases) === FALSE) { + $objects['site']->aliases[] = $domain; + $objects['site']->needs_save = TRUE; } } - } - } - } - - // If using environment aliases... save them. - devshop_project_save_domain_aliases($node); -} - -/** - * Helper to add a domain alias to a site node. Makes sure not to add the same - * alias twice. - */ -function devshop_project_save_domain_aliases($project_node) { - if (module_exists('hosting_alias')){ - if (!empty($project_node->live_domain) && $project_node->live_domain_aliases) { - foreach ($project_node->project->environments as $env => $details){ - $domain = "$env.$project_node->live_domain"; - $site_node = node_load($details['site_nid']); - if (array_search($domain, $site_node->aliases) === FALSE) { - $site_node->aliases[] = $domain; - node_save($site_node); + // If environment_aliases is NOT checked, remove them from the nodes. + elseif (!$project->settings['live']['environment_aliases'] && count($project->environments)){ + foreach ($project->environments as $env => $environment){ + $domain = $name . '.' . $project->settings['live']['live_domain']; + if (!empty($objects['site']->aliases)){ + $i = array_search($domain, $objects['site']->aliases); + if ($i !== FALSE) { + unset($objects['site']->aliases[$i]); + $objects['site']->needs_save = TRUE; + } + } + } } - } - } - // If live_domain_aliases is checked, remove them from the nodes. - elseif (!$project_node->live_domain_aliases && count($project_node->project->environments)){ - foreach ($project_node->project->environments as $env => $environment){ - $domain = "$env.$project_node->live_domain"; - $site_node = node_load($environment->site); - if (!empty($site_node->aliases)){ - $i = array_search($domain, $site_node->aliases); - if ($i !== FALSE) { - unset($site_node->aliases[$i]); - node_save($site_node); + + // Save site and platform if needed. + foreach ($objects as $type => $object){ + if ($object->needs_save) { + drupal_set_message("Saving $type " . $object->title); + node_save($object); } } } From a70b1149e55ef23a94e57d58a87c844f4f386a03 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 22:52:55 -0400 Subject: [PATCH 0876/3476] Only show production environments if there are some. --- devshop_projects/inc/ui.inc | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index e9cf67ed6..9d1f3ae2d 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -315,18 +315,21 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $header = array(); $table = theme('table', $header, $rows, array('class' => 'environments-table')); - $node->content['production_environments'] = array( - '#type' => 'fieldset', - '#title' => t('Production'), - '#weight' => 11, - '#attributes' => array( - 'class' => 'project-environments', - ), - ); - $node->content['production_environments']['table'] = array( - '#type' => 'item', - '#value' => theme('table', $header, $production_rows, array('class' => 'environments-table')), - ); + // Show production environments separately. + if (count($production_rows)){ + $node->content['production_environments'] = array( + '#type' => 'fieldset', + '#title' => t('Production'), + '#weight' => 11, + '#attributes' => array( + 'class' => 'project-environments', + ), + ); + $node->content['production_environments']['table'] = array( + '#type' => 'item', + '#value' => theme('table', $header, $production_rows, array('class' => 'environments-table')), + ); + } $node->content['environments'] = array( '#type' => 'fieldset', From 5af92bc09df5c3d514642ea276527e04ed245790 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 Aug 2014 23:08:46 -0400 Subject: [PATCH 0877/3476] Fixing saving of aliases --- devshop_projects/inc/nodes.inc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3b372b945..1c5dba57e 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -99,9 +99,6 @@ function devshop_projects_insert($node) { hosting_context_register($node->nid, ($node->hosting_name) ? $node->hosting_name : $node->title); } - // If using environment aliases... save them. - devshop_project_save_domain_aliases($node); - // @TODO: The wizard always creates the project before the environments. // Not sure if we need this, but we might to enable importing via drush. // Save Environment records. @@ -144,6 +141,7 @@ function devshop_projects_update($node) { } $project = (object) $node->project; + $project->settings = (object) $project->settings; $info = new stdClass(); $info->nid = $node->nid; @@ -221,8 +219,8 @@ function devshop_projects_update($node) { } // Save environment aliases - if (!empty($project->settings['live']['live_domain']) && $project->settings['live']['environment_aliases']) { - $domain = $name . '.' . $project->settings['live']['live_domain']; + if (!empty($project->settings->live['live_domain']) && $project->settings->live['environment_aliases']) { + $domain = $name . '.' . $project->settings->live['live_domain']; if (array_search($domain, $objects['site']->aliases) === FALSE) { $objects['site']->aliases[] = $domain; $objects['site']->needs_save = TRUE; @@ -230,9 +228,9 @@ function devshop_projects_update($node) { } // If environment_aliases is NOT checked, remove them from the nodes. - elseif (!$project->settings['live']['environment_aliases'] && count($project->environments)){ + elseif (!$project->settings->live['environment_aliases'] && count($project->environments)){ foreach ($project->environments as $env => $environment){ - $domain = $name . '.' . $project->settings['live']['live_domain']; + $domain = $name . '.' . $project->settings->live['live_domain']; if (!empty($objects['site']->aliases)){ $i = array_search($domain, $objects['site']->aliases); if ($i !== FALSE) { From c4ed801311dbda9749a37b337ef8ee5c6f5c807f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 2 Aug 2014 14:16:46 -0400 Subject: [PATCH 0878/3476] Adding link to github page, if this is a github repo. --- devshop_projects/inc/ui.inc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 9d1f3ae2d..032720d4c 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -134,11 +134,18 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->content['info']['git_url'] = array( '#type' => 'item', - '#title' => t('Git URL'), + '#title' => t('Git Repository'), '#value' => strtr("", array('!url' => $project->git_url)), '#weight' => -10 ); + // Detect GitHub + // @TODO: Detect other web URLs for other git hosts. + if (strpos($project->git_url, 'github.com') !== FALSE) { + $url = str_replace('git@github.com:', 'http://github.com/', $project->git_url); + $node->content['info']['git_url']['#value'] .= l(t('View on GitHub.com'), $url, array('target' => '_blank')); + } + // Branches display if (!empty($project->settings->git['branches'])){ $items = theme_item_list($project->settings->git['branches'], NULL, 'ul', array('class' => 'branches')); From 06a37098ea499c23dc0f9f35240cbbcccd858a2f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 2 Aug 2014 14:23:29 -0400 Subject: [PATCH 0879/3476] Don't mention every thing. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 1c5dba57e..0f9f82d5d 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -244,7 +244,7 @@ function devshop_projects_update($node) { // Save site and platform if needed. foreach ($objects as $type => $object){ if ($object->needs_save) { - drupal_set_message("Saving $type " . $object->title); + // drupal_set_message("Saving $type " . $object->title); node_save($object); } } From 28bb4ee5e968000fab942290d838942ed37dea7f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 2 Aug 2014 14:23:49 -0400 Subject: [PATCH 0880/3476] Add a link to view in github. --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 032720d4c..4905ed8e1 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -143,7 +143,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // @TODO: Detect other web URLs for other git hosts. if (strpos($project->git_url, 'github.com') !== FALSE) { $url = str_replace('git@github.com:', 'http://github.com/', $project->git_url); - $node->content['info']['git_url']['#value'] .= l(t('View on GitHub.com'), $url, array('target' => '_blank')); + $node->content['info']['git_url']['#value'] .= l(t('View on GitHub.com'), $url, array('attributes' => array('target' => '_blank'))); } // Branches display From 10e0205a8e61439fe6cd4d2beada692729a63fbc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 2 Aug 2014 14:26:47 -0400 Subject: [PATCH 0881/3476] Changing settings organization for github integraitons. Work on Issue #2314245 --- devshop_pull/devshop_pull.inc | 4 ++-- devshop_pull/devshop_pull.module | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index c892fbf21..02a4a11ed 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -131,7 +131,7 @@ function devshop_pull_github_webhook($project){ // @TODO: Add to project settings. $message = "Detected Pull Request for $branch \n"; - if ($project->project_settings['pull']['pull_request_environments']){ + if ($project->settings->github['pull_request_environments']){ if (isset($project->environments[$environment_name])){ $message .= "Environment $environment_name already exists."; @@ -145,7 +145,7 @@ function devshop_pull_github_webhook($project){ } elseif ($data->action == 'closed'){ $message .= "Pull Request Closed \n"; - if ($project->project_settings['pull']['pull_request_environments_delete']){ + if ($project->settings->github['pull_request_environments_delete']){ hosting_add_task($project->environments[$environment_name]['site'], 'delete'); $message .= "Site $environment_name scheduled for deletion."; } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index b91fb6f20..d884476d0 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -153,13 +153,27 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#value' => $node->project->settings->pull['last_pull_ip'], ); - $form['project']['settings']['pull']['pull_request_environments'] = array( + // @TODO: Make this more abstract and extensible. + $is_github = (strpos($node->project->git_url, 'github.com') !== FALSE); + + //All settings git pull in project page + $form['project']['settings']['github'] = array( + '#type' => 'fieldset', + '#title' => t('GitHub Integration'), + '#access' => $is_github, + ); + + // Pull Requests create environments? + $form['project']['settings']['github']['pull_request_environments'] = array( '#type' => 'checkbox', '#title' => t('Create Environments for Pull Requests'), '#default_value' => $node->project->settings->pull['pull_request_environments'], '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), + '#access' => $is_github, ); - $form['project']['settings']['pull_request_environments_delete'] = array( + + // Delete Pull Request environments? + $form['project']['settings']['github']['pull_request_environments_delete'] = array( '#type' => 'checkbox', '#title' => t('Delete Pull Request Environments'), '#default_value' => $node->project->settings->pull['pull_request_environments_delete'], From fc0079efac7fd6732fcbcdc6723e7965554330b3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 2 Aug 2014 14:51:49 -0400 Subject: [PATCH 0882/3476] Better text for Disable Pull --- devshop_pull/devshop_pull.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index d884476d0..f1cb74456 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -80,10 +80,10 @@ function devshop_pull_hosting_queues() { function devshop_pull_devshop_project_settings(){ return array( 'pull_disabled' => array( - '#title' => t('Disable Pull'), + '#title' => t('Disable Pull on Commit'), '#node_type' => 'platform', '#type' => 'checkbox', - '#description' => t('Disable pulling code on this environment, allowing "live development".'), + '#description' => t('Turn off automatic code updates for this environment.'), ), ); } From 83b91ae6d6a9e8a421b2b291fb1fdec2fc60392f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Aug 2014 18:04:34 -0400 Subject: [PATCH 0883/3476] Fixing the handling of live domain and environment aliases. --- devshop_projects/inc/forms.inc | 51 +++++++--------------------- devshop_projects/inc/nodes.inc | 62 ++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 60 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 137cbd797..2189318f5 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -242,6 +242,19 @@ function devshop_projects_form(&$node) { '#weight' => 5, ); + // Live Environment + $environments = array_keys($node->project->environments); + $environments_options = array_combine($environments, $environments); + + $form['project']['settings']['live']['live_environment'] = array( + '#type' => 'select', + '#title' => t('Live environment'), + '#description' => t('The environment for the live website.'), + '#options' => $environments_options, + '#default_value' => $node->project->settings->live['live_environment'], + '#weight' => 5, + ); + // Use aliases if (module_exists('hosting_alias')){ $form['project']['settings']['live']['environment_aliases'] = array( @@ -253,44 +266,6 @@ function devshop_projects_form(&$node) { ); } -// @TODO: Not ready yet! Rethink this. -// 1. We should probably allow a "production mode" on environments, disabling sync data destination and allowing for other things. -// 2. We should break out domain management into it's own thing. The way this is setup is hard to understand. -// - - -// $live_alias_options['other'] = 'Other...'; -// if (empty($live_alias_options[$node->project->settings->live['live_environment']])){ -// // No live environment -// $live_environment_default_value = ''; -// } -// elseif (!empty($node->project->settings->live['live_environment_alias'])){ -// // "other" live environment -// $live_environment_default_value = 'other'; -// } -// else { -// $live_environment_default_value = $node->project->settings->live['live_environment']; -// } -// -// $form['project']['settings']['live']['live_environment'] = array( -// '#type' => 'select', -// '#title' => t('Live Environment'), -// '#options' => $live_alias_options, -// '#required' => FALSE, -// '#default_value' => $live_environment_default_value, -// '#description' => t('Choose which environment is your live production environment. This will prevent users from Syncing data over the live site, and allow easy "Sync From Live" functionality.'), -// ); -// $form['project']['settings']['live']['live_environment_alias'] = array( -// '#title' => t('Live Site Drush Alias'), -// '#type' => 'textfield', -// '#description' => t("If your 'live' site is outside of this server, add a remote alias file to /var/aegir/.drush and enter the alias name here."), -// '#field_prefix' => '@', -// '#size' => 40, -// '#default_value' => $node->project->live['live_environment_alias'], -// '#size' => 50, -// '#maxlength' => 50, -// ); - return $form; } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 0f9f82d5d..3144bde68 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -166,6 +166,7 @@ function devshop_projects_update($node) { foreach ($project->environments as $name => $environment){ // Ensure correct data types $environment = (object) $environment; + $environment->name = $name; $environment->settings = (array) $environment->settings; $info = new stdClass(); @@ -187,9 +188,14 @@ function devshop_projects_update($node) { // Save aegir objects if (!empty($environment->site)){ + $site = node_load($environment->site); + $platform = node_load($environment->platform); + + // Save to array so we can apply values for settings. $objects = array(); - $objects['site'] = node_load($environment->site); - $objects['platform'] = node_load($environment->platform); + $objects['site'] = &$site; + $objects['platform'] = &$platform; + $settings_info = module_invoke_all('devshop_project_settings', $node); // Go through each setting, applying it to either the site or the platform. @@ -205,48 +211,60 @@ function devshop_projects_update($node) { // If the database server is changed, a "migrate" task is needed. if ($setting_name == 'db_server') { $args = array(); - $args['target_platform'] = $objects['site']->platform; - $args['new_uri'] = $objects['site']->title; + $args['target_platform'] = $site->platform; + $args['new_uri'] = $site->title; $args['new_db_server'] = $environment->settings['db_server']; - hosting_add_task($objects['site']->nid, 'migrate', $args); + hosting_add_task($site->nid, 'migrate', $args); } } } // If git_ref changed, flag needs_save for platform. if ($environment->orig->git_ref != $environment->git_ref){ - $objects['platform']->needs_save = TRUE; + $platform->needs_save = TRUE; } - // Save environment aliases - if (!empty($project->settings->live['live_domain']) && $project->settings->live['environment_aliases']) { - $domain = $name . '.' . $project->settings->live['live_domain']; - if (array_search($domain, $objects['site']->aliases) === FALSE) { - $objects['site']->aliases[] = $domain; - $objects['site']->needs_save = TRUE; + // If there's a live domain + if (!empty($project->settings->live['live_domain'])) { + + // Save the live environment + // @TODO: Remove the alias from the old "live" environment + if ($project->settings->live['live_environment'] == $environment->name) { + if (array_search($project->settings->live['live_domain'], $site->aliases) === FALSE) { + $site->aliases[] = $project->settings->live['live_domain']; + $site->needs_save = TRUE; + } + } + // Save other environment aliases if environment_aliases is checked. + elseif ($project->settings->live['environment_aliases']) { + $domain = $environment->name . '.' . $project->settings->live['live_domain']; + if (array_search($domain, $site->aliases) === FALSE) { + $site->aliases[] = $domain; + $site->needs_save = TRUE; + } } } // If environment_aliases is NOT checked, remove them from the nodes. elseif (!$project->settings->live['environment_aliases'] && count($project->environments)){ foreach ($project->environments as $env => $environment){ - $domain = $name . '.' . $project->settings->live['live_domain']; - if (!empty($objects['site']->aliases)){ - $i = array_search($domain, $objects['site']->aliases); + $domain = $environment->name . '.' . $project->settings->live['live_domain']; + if (!empty($site->aliases)){ + $i = array_search($domain, $site->aliases); if ($i !== FALSE) { - unset($objects['site']->aliases[$i]); - $objects['site']->needs_save = TRUE; + unset($site->aliases[$i]); + $site->needs_save = TRUE; } } } } // Save site and platform if needed. - foreach ($objects as $type => $object){ - if ($object->needs_save) { - // drupal_set_message("Saving $type " . $object->title); - node_save($object); - } + if ($platform->needs_save) { + node_save($platform); + } + if ($site->needs_save) { + node_save($site); } } } From e2e492ed39126c219fd13c9e2696447ea2da2f2f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Aug 2014 18:15:52 -0400 Subject: [PATCH 0884/3476] Using live alias settings for environments table. --- devshop_projects/inc/nodes.inc | 2 +- devshop_projects/inc/ui.inc | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 3144bde68..bf12e5462 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -135,7 +135,6 @@ function devshop_projects_insert($node) { * */ function devshop_projects_update($node) { - if (!$node->no_verify) { hosting_add_task($node->nid, 'verify'); } @@ -229,6 +228,7 @@ function devshop_projects_update($node) { // Save the live environment // @TODO: Remove the alias from the old "live" environment + if ($project->settings->live['live_environment'] == $environment->name) { if (array_search($project->settings->live['live_domain'], $site->aliases) === FALSE) { $site->aliases[] = $project->settings->live['live_domain']; diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 4905ed8e1..ff714ca86 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -433,7 +433,21 @@ function devshop_projects_nodeapi_platform_view(&$node, $a3, $a4) { */ function devshop_hosting_site_goto_link($node) { // $project = node_load($node->environment->project_nid); - $url = "{$node->environment->name}.{$node->project->base_url}"; + + // If this is the live environment, use the live domain. + if ($node->environment->name == $node->project->settings->live['live_environment']) { + $url = $node->project->settings->live['live_domain']; + } + // If environment aliases are enabled + elseif ($node->project->settings->live['environment_aliases']) { + $url = "{$node->environment->name}.{$node->project->settings->live['live_domain']}"; + } + // Otherwise use the base_url + else { + $url = "{$node->environment->name}.{$node->project->base_url}"; + } + + // Get the link from cache. $cache = cache_get("hosting:site:" . $node->nid . ":login_link"); if (!is_null($cache) && (time() < $cache->data['expire'])) { $title = t("Log in: !url", array('!url' => $url)); From d09dd8aaea9feb9075ae54a520c176d96ce6d629 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Aug 2014 18:18:16 -0400 Subject: [PATCH 0885/3476] Changing link directly to site and platform settings. --- devshop_projects/inc/ui.inc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index ff714ca86..2cf08b246 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -301,12 +301,14 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Grant access to Aegir pages if (user_access('administer hosting')){ $actions[] = array( - 'title' => t('Site Dashboard'), - 'href' => "node/$environment->site", + 'title' => t('Site Settings'), + 'href' => "node/$environment->site/edit", + 'query' => drupal_get_destination(), ); $actions[] = array( - 'title' => t('Platform Dashboard'), - 'href' => "node/$environment->platform", + 'title' => t('Platform Settings'), + 'href' => "node/$environment->platform/edit", + 'query' => drupal_get_destination(), ); } From 679606f4acad0059327b7c27fc71efe35ba2f73b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Aug 2014 18:22:33 -0400 Subject: [PATCH 0886/3476] Cleaner branch/tag UI --- devshop_projects/inc/ui.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 2cf08b246..c5e7ad7c9 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -217,8 +217,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } // Display current branch - $row[] = "{$environment->git_ref}"; + $row[] = "{$environment->git_ref}"; // Create branch/tag chooser // @TODO: Uncomment once we have a link to go to. From e7541f001d804595784bb733b886dae3320842ff Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 Aug 2014 18:49:32 -0400 Subject: [PATCH 0887/3476] Adding "www" option to projects. --- devshop_projects/inc/forms.inc | 25 ++++++++++++++----------- devshop_projects/inc/nodes.inc | 12 ++++++++++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 2189318f5..e02ee1654 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -239,7 +239,6 @@ function devshop_projects_form(&$node) { '#description' => t('The live domain for this project. Do not include "www".'), '#size' => 40, '#default_value' => $node->project->settings->live['live_domain'], - '#weight' => 5, ); // Live Environment @@ -252,19 +251,23 @@ function devshop_projects_form(&$node) { '#description' => t('The environment for the live website.'), '#options' => $environments_options, '#default_value' => $node->project->settings->live['live_environment'], - '#weight' => 5, + ); + + // Use www + $form['project']['settings']['live']['live_domain_www'] = array( + '#type' => 'checkbox', + '#title' => t('Add www as a ServerAlias for Live Environment'), + '#description' => t('Access the site at http://livedomain.com and http://www.livedomain.com'), + '#default_value' => $node->project->settings->live['live_domain_www'], ); // Use aliases - if (module_exists('hosting_alias')){ - $form['project']['settings']['live']['environment_aliases'] = array( - '#type' => 'checkbox', - '#title' => t('Environment Subdomains'), - '#description' => t('Create subdomains under the live domain for all environments..'), - '#default_value' => $node->project->settings->live['environment_aliases'], - '#weight' => 6, - ); - } + $form['project']['settings']['live']['environment_aliases'] = array( + '#type' => 'checkbox', + '#title' => t('Environment Subdomains'), + '#description' => t('Create subdomains under the live domain for all environments.'), + '#default_value' => $node->project->settings->live['environment_aliases'], + ); return $form; } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index bf12e5462..d8a7c59ba 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -226,14 +226,22 @@ function devshop_projects_update($node) { // If there's a live domain if (!empty($project->settings->live['live_domain'])) { - // Save the live environment + // Save the live domain // @TODO: Remove the alias from the old "live" environment - if ($project->settings->live['live_environment'] == $environment->name) { if (array_search($project->settings->live['live_domain'], $site->aliases) === FALSE) { $site->aliases[] = $project->settings->live['live_domain']; $site->needs_save = TRUE; } + + // Save www version + if ($project->settings->live['live_domain_www']) { + $site->aliases[] = 'www.' . $project->settings->live['live_domain']; + $site->needs_save = TRUE; + } + else { + $site->aliases = array_diff($site->aliases, array('www' . $project->settings->live['live_domain'])); + } } // Save other environment aliases if environment_aliases is checked. elseif ($project->settings->live['environment_aliases']) { From 703d3e5ba9212f83c773bcd0e8f0881f2185b4a7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Aug 2014 09:17:20 -0400 Subject: [PATCH 0888/3476] avoiding notice. --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index c5e7ad7c9..77910dd4a 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -103,7 +103,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Check to see if this project is still in the wizard ctools_include('object-cache'); $project_wizard_cache = ctools_object_cache_get('project', NULL); - if ($node->nid == $project_wizard_cache->project_nid){ + if (isset($project_wizard_cache->project_nid) && $node->nid == $project_wizard_cache->project_nid){ drupal_goto('hosting/projects/add/' . $project_wizard_cache->step); } From 2cd31665d29a14cb9b01e066d1251b02fc34e792 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Aug 2014 09:18:53 -0400 Subject: [PATCH 0889/3476] avoiding notice. --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 77910dd4a..ce2728bc7 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -291,7 +291,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'destination' => "node/$node->nid", ), 'attributes' => array( - 'class' => $site_tasklist[$task_name]['dialog']? 'hosting-button-dialog': '', + 'class' => isset($site_tasklist[$task_name]['dialog'])? 'hosting-button-dialog': '', ), ); } From e991850e1532082897ec861f85d739b9891fdce0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Aug 2014 09:31:01 -0400 Subject: [PATCH 0890/3476] Removing limit of task display. --- devshop_hosting.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 4f27f9ba2..31410c408 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -229,7 +229,7 @@ function devshop_hosting_task_queue_block() { drupal_add_js($settings, 'setting'); $tasks['processing'] = hosting_get_tasks('task_status', HOSTING_TASK_PROCESSING, 5); - $tasks['queued'] = hosting_get_tasks('task_status', HOSTING_TASK_QUEUE, 5); + $tasks['queued'] = hosting_get_tasks('task_status', HOSTING_TASK_QUEUE); $total_tasks = count($tasks['queued']) + count($tasks['processing']); $status = format_plural($total_tasks, '1 active task.', '@count active tasks.'); From 6f763c5b3060d1992d9719aa55a058196365c7b5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Aug 2014 09:31:59 -0400 Subject: [PATCH 0891/3476] Avoiding notices. --- devshop_hosting.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_hosting.module b/devshop_hosting.module index 31410c408..572109ae0 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -259,6 +259,8 @@ function devshop_hosting_task_queue_block() { // Build Strings $link = l('Tasks Logs', 'hosting/queues/tasks', array('attributes' => array('class' => 'task-logs-link'))); + $headers = array(); + if ($rows){ $table = theme('table', $headers, $rows, array('class' => 'hosting-table')); } From 72bd5e4bd2cc3f2eb0694e28cde7293097e0f23c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Aug 2014 11:39:49 -0400 Subject: [PATCH 0892/3476] Adding "clone environment" and fixing "fork environment". --- devshop_projects/devshop_projects.drush.inc | 44 ++++---- devshop_projects/inc/ui.inc | 11 +- devshop_projects/tasks/create.inc | 118 +++++++++++++------- 3 files changed, 112 insertions(+), 61 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index e01f210e9..54883e7ad 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -120,12 +120,15 @@ function drush_devshop_platform_verify(){ $command .= " --branch $git_branch"; } - // @TODO: Gotta be a better way... I don't want a clone_nid field in devshop_project_objects table. - // If is set site nid clone from old site and later create a new branch. -// if ($platform->clone_nid) { -// $site_source = node_load($platform->clone_nid); -// $git_branch = $site_source->git_branch; -// $create_branch = TRUE; + // If we are cloning or forking an environment +// if ($platform->site_to_clone) { +// $site_source = node_load($platform->site_to_clone); +// $git_branch = $platform->environment->git_ref; +// +// // If branch doesn't exist yet... create it. +// if (!in_array($git_branch, $platform->project->settings->git['branches'])){ +// $create_branch = TRUE; +// } // } } // If the platform has been verified and has a branch and git url @@ -224,19 +227,22 @@ function devshop_projects_post_hosting_verify_task($task, $data) { else { drush_log('[DEVSHOP] No site found for this environment. Creating...', 'notice'); - //Check if clone or create a new site. - // if ($platform->clone_nid) { - // $servers = hosting_get_servers('db'); - // $args['target_platform'] = $platform->nid; - // $args['new_uri'] = $platform->environment->name .'.'. $platform->project->base_url; - // $args['new_db_server'] = key($servers); - // drush_log('[DEVSHOP] Cloning site...'); - // hosting_add_task($platform->clone_nid, 'clone', $args); - // } - // else { - - devshop_projects_create_site($platform->project, $platform, $platform->environment->name); - drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. '); + // Check for site to clone + if ($platform->environment->site_to_clone) { + $servers = hosting_get_servers('db'); + + $args['target_platform'] = $platform->nid; + $args['new_uri'] = $platform->environment->name .'.'. $platform->project->base_url; + + // @TODO: Match settings of cloned environment. + $args['new_db_server'] = key($servers); + drush_log('[DEVSHOP] Cloning environment: ' . $platform->environment->name); + hosting_add_task($platform->environment->site_to_clone, 'clone', $args); + } + else { + devshop_projects_create_site($platform->project, $platform, $platform->environment->name); + drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. '); + } } } } diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index ce2728bc7..da820d448 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -249,13 +249,20 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Create actions dropdown. $actions = array(); $actions[] = array( + 'title' => t('Clone environment'), + 'href' => 'node/' . $node->nid . '/project_devshop-create/clone/' . $environment->site, array( 'query' => array('token' => drupal_get_token($user->uid))), + 'attributes' => array( + 'class' => 'hosting-button-dialog', + ), + ); + $actions[] = array( 'title' => t('Fork environment'), - 'href' => 'node/' . $node->nid . '/project_devshop-create/' . $environment->site, array( 'query' => array('token' => drupal_get_token($user->uid))), + 'href' => 'node/' . $node->nid . '/project_devshop-create/fork/' . $environment->site, array( 'query' => array('token' => drupal_get_token($user->uid))), 'attributes' => array( 'class' => 'hosting-button-dialog', ), ); - + // Aegir Tasks $site_tasklist = hosting_task_fetch_tasks($environment->site); diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 548f513ea..98fdf9eed 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -14,32 +14,59 @@ function hosting_task_devshop_create_form($node) { $project = $node->project; $branch_options = devshop_projects_git_ref_options($project); - // Look for Site to Fork - // @TODO: Do this with hook_menu? - $site_to_fork = arg(3); - if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { - if (!empty($project->environments[$site->environment->name])){ - - $form['branch_source'] = array( - '#type' => 'item', - '#title' => t('Fork from'), - '#value' => t('!env environment on branch !branch', array('!env' => "{$site->environment->name}", '!branch' => "{$site->environment->git_ref}")), - ); - $form['fork_source'] = array( - '#type' => 'hidden', - '#value' => $site->environment->name, - ); - $form['new_branch'] = array( - '#title' => t('New branch name'), - '#description' => t('Pick a name for your new git branch.'), - '#type' => 'textfield', - '#size' => 60, - '#maxlength' => 128, - '#required' => TRUE, - ); + // Look for Site to Fork or Clone + + if (arg(3) == 'fork' || arg(3) == 'clone'){ + + if (arg(3) == 'fork'){ + // @TODO: Do this with hook_menu? + $site_to_fork = arg(4); + if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { + if (!empty($project->environments[$site->environment->name])){ + + $form['branch_source'] = array( + '#type' => 'item', + '#title' => t('Fork from'), + '#value' => t('!env environment on branch !branch', array('!env' => "{$site->environment->name}", '!branch' => "{$site->environment->git_ref}")), + ); + $form['environment_source'] = array( + '#type' => 'hidden', + '#value' => $site->environment->name, + ); + $form['new_branch'] = array( + '#title' => t('New branch name'), + '#description' => t('Pick a name for your new git branch.'), + '#type' => 'textfield', + '#size' => 60, + '#maxlength' => 128, + '#required' => TRUE, + ); + } + } + } + elseif (arg(3) == 'clone') { + // @TODO: Do this with hook_menu? + $site_to_clone = arg(4); + if ($site_to_clone && is_numeric($site_to_clone) && $site = node_load($site_to_clone)) { + + if (!empty($project->environments[$site->environment->name])){ + $form['environment_source_display'] = array( + '#type' => 'item', + '#title' => t('Clone'), + '#value' => t('!env environment', array('!env' => "{$site->environment->name}")), + ); + $form['environment_source'] = array( + '#type' => 'hidden', + '#value' => $site->environment->name, + ); + $form['branch'] = array( + '#type' => 'hidden', + '#value' => $site->environment->git_ref, + ); + } + } } } - else { $form['branch'] = array( '#title' => t('Branch/Tag'), @@ -107,10 +134,10 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $project = node_load($form_state['values']['nid']); $environment_name = $form_state['values']['parameters']['environment_name']; - $branch = $form_state['values']['parameters']['new_branch']? $form_state['values']['parameters']['new_branch']: $form_state['values']['parameters']['branch']; - $fork_source = $form_state['values']['parameters']['fork_source']; + $branch = !empty($form_state['values']['parameters']['new_branch'])? $form_state['values']['parameters']['new_branch']: $form_state['values']['parameters']['branch']; + $environment_source = $form_state['values']['parameters']['environment_source']; - hosting_create_environment($project, $environment_name, $branch, $fork_source); + hosting_create_environment($project, $environment_name, $branch, $environment_source); // We are replacing hosting_confirm_form_submit here, so just do what it does, // minus the hosting task creation! @@ -161,28 +188,39 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ $platform->publish_path = $project->code_path . '/' . $environment_name; } - // Check if we are forking another site. - // @see ... - if (isset($project_node->project->environments[$fork_source])) { - $platform->clone_nid = $project_node->project->environments[$fork_source]->site; - $platform->git_branch = $branch; - $platform->old_branch = $project_node->project->environments[$fork_source]->git_ref; - } - // Save the platform node. if ($platform = node_submit($platform)) { node_save($platform); } - // Next, add the environment record. - $environment = new stdClass(); - $environment->git_ref = $branch; - $environment->platform = $platform->nid; + // If cloning or forking, check if source environment exists... + if (isset($project_node->project->environments[$fork_source])) { + $source_environment = $project_node->project->environments[$fork_source]; + + $environment = new stdClass(); + $environment->git_ref = $branch; + $environment->platform = $platform->nid; + + // Copy settings from source environment. + $environment->settings = $source_environment->settings; + + // Clone/fork specific data. + $environment->site_to_clone = $source_environment->site; + $environment->branch_to_fork = $source_environment->git_ref; + + } + else { + // Next, add the environment record. + $environment = new stdClass(); + $environment->git_ref = $branch; + $environment->platform = $platform->nid; + // @TODO: Implement default environment settings. + + } $project->environments[$environment_name] = $environment; // Save the project node. $project_node->project = $project; node_save($project_node); - } From 3b30fdbef8fad8d176d281e747e3a4e62e037bc8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 Aug 2014 18:05:45 -0400 Subject: [PATCH 0893/3476] Preventing notice. --- devshop_hosting.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 572109ae0..8d7df9a5e 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -261,7 +261,7 @@ function devshop_hosting_task_queue_block() { $headers = array(); - if ($rows){ + if (isset($rows)){ $table = theme('table', $headers, $rows, array('class' => 'hosting-table')); } From 256ce83baa612dba738796ff3c600afed7da8d1d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 6 Aug 2014 18:05:44 -0400 Subject: [PATCH 0894/3476] Fixing up clone/fork environment to use new settings. --- devshop_projects/devshop_projects.drush.inc | 46 ++++++++++----------- devshop_projects/tasks/create.inc | 2 +- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 54883e7ad..6dcc31afd 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -94,7 +94,7 @@ function drush_devshop_platform_verify(){ $platform = $task->ref; $root = $platform->publish_path; - $git_branch = $platform->environment->git_ref; + $git_ref = $platform->environment->git_ref; $git_remote = $platform->project->git_url; $drupal_path = $platform->project->drupal_path; @@ -108,51 +108,47 @@ function drush_devshop_platform_verify(){ $root = str_replace($drupal_path, '', $root); } + // If there is a branch to fork, set the branch_to_fork as git_ref + if (!empty($platform->environment->branch_to_fork)) { + $git_ref = $platform->environment->branch_to_fork; + $create_branch = TRUE; + } + // Check if the platform code exists. If it doesn't, clone it. if (!is_dir($root)) { drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $git_remote, '!root' => $root))); - // @TODO: If another environment exists, copy the repo locally, - // git clean, and checkout the proper branch. - // Build the command string + // Build the clone command string $command = "git clone --recursive $git_remote $root"; - if ($git_branch) { - $command .= " --branch $git_branch"; - } - // If we are cloning or forking an environment -// if ($platform->site_to_clone) { -// $site_source = node_load($platform->site_to_clone); -// $git_branch = $platform->environment->git_ref; -// -// // If branch doesn't exist yet... create it. -// if (!in_array($git_branch, $platform->project->settings->git['branches'])){ -// $create_branch = TRUE; -// } -// } + // If a branch is selected, add --branch to the command. + // @TODO: Won't the platform ALWAYS have a git_branch? + if ($git_ref) { + $command .= " --branch $git_ref"; + } } // If the platform has been verified and has a branch and git url else { - drush_log(dt("[DEVSHOP] Existing Repo found at !root. Checking out branch !branch", array('!branch' => $git_branch, '!root' => $root))); + drush_log(dt("[DEVSHOP] Existing Repo found at !root. Checking out branch !branch", array('!branch' => $git_ref, '!root' => $root))); // Run git fetch to ensure we have all branches and tags $output .= _devshop_projects_git_execute('git fetch', $root); // Build the command string - $command = "git checkout $git_branch"; - + $command = "git checkout $git_ref"; } - // Execute + // Execute clone or checkout. $output .= _devshop_projects_git_execute($command, $root); + // If branch needs to be created (for "fork environment") if ($create_branch) { - //Create branch. - $command = "git checkout -b $git_branch"; + // Checkout the new branch. + $command = "git checkout -b {$platform->environment->git_ref}"; $output .= _devshop_projects_git_execute($command, $root); - //Push the branch - $command = "git push -u origin $git_branch"; + // Push the branch + $command = "git push -u origin {$platform->environment->git_ref}"; $output .= _devshop_projects_git_execute($command, $root); } diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 98fdf9eed..4c5d16b96 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -204,7 +204,7 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ // Copy settings from source environment. $environment->settings = $source_environment->settings; - // Clone/fork specific data. + // Save Clone/fork specific data to the environment $environment->site_to_clone = $source_environment->site; $environment->branch_to_fork = $source_environment->git_ref; From 347699b3718895b260431594580533fbb3e00679 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 9 Aug 2014 08:50:20 -0400 Subject: [PATCH 0895/3476] Prevent verification when getting branches. --- devshop_projects/devshop_projects.drush.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 6dcc31afd..a3cfec95b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -260,6 +260,8 @@ function devshop_projects_hosting_project_context_options(&$task) { $task->ref->project->settings->git['tags'] = $branches['tags']; // Save the project node now that we have branches and tags. + // Don't verify again, this is the verification process. + $task->ref->no_verify = TRUE; node_save($task->ref); } From cbcf5afd5e54bad194e49e35beeb5e5bd021f913 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 9 Aug 2014 09:22:08 -0400 Subject: [PATCH 0896/3476] Getting started on the new "Deploy" task. --- devshop_projects/devshop_projects.drush.inc | 10 ++++ devshop_projects/devshop_projects.module | 1 + devshop_projects/tasks/deploy.inc | 59 +++++++++++++++++++++ devshop_projects/tasks/tasks.inc | 7 ++- 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 devshop_projects/tasks/deploy.inc diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index a3cfec95b..8daf4433c 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -38,6 +38,16 @@ function drush_devshop_projects_pre_hosting_task() { $task->options['force'] = FALSE; } + // Deploy + if ($task->ref->type == 'project' && $task->task_type == 'devshop-deploy') { + $task->args['git_ref'] = $task->task_args['git_ref']; + $task->args['environment'] = $task->task_args['environment']; + $task->options['update'] = $task->task_args['update']; + $task->options['revert'] = !empty($task->task_args['revert']); + $task->options['cache'] = $task->task_args['cache']; + $task->options['force'] = FALSE; + } + // Commit if ($task->ref->type == 'project' && $task->task_type == 'devshop-commit') { $task->args['environment'] = $task->task_args['environment']; diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index e3b17e55e..aab652d54 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -16,6 +16,7 @@ include_once('tasks/commit.inc'); include_once('tasks/create.inc'); include_once('tasks/pull.inc'); include_once('tasks/sync.inc'); +include_once('tasks/deploy.inc'); /** diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc new file mode 100644 index 000000000..604501235 --- /dev/null +++ b/devshop_projects/tasks/deploy.inc @@ -0,0 +1,59 @@ +project; + $branch_options = devshop_projects_git_ref_options($project); + + $form = array(); + + $form['git_ref'] = array( + '#title' => t('Git Branch or Tag'), + '#description' => t('Choose the branch or tag to deploy to this environment.'), + '#type' => 'select', + '#options' => $branch_options, + '#required' => TRUE, + ); + + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to deploy the chosen git branch or tag to.'), 'environment', 'Environment', 'select'); + + + $form['update'] = array( + '#title' => t('Run update.php after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + + if (_devshop_projects_project_has_module($node, 'features')){ + $form['revert'] = array( + '#title' => t('Revert all features after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + } + $form['cache'] = array( + '#title' => t('Clear cache after code pull?'), + '#type' => 'checkbox', + '#default_value' => 1, + ); + + return $form; +} + + +// @TODO: +// 1. Save environment with new current git ref. +// 2. Create a new platform with the new git_ref +// 3. When platform is complete, migrate the environment's site to it. \ No newline at end of file diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index 8135007f3..a4d704680 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -48,7 +48,12 @@ function devshop_projects_hosting_tasks() { 'dialog' => TRUE, 'hidden' => TRUE, ); - + $tasks['project']['devshop-deploy'] = array( + 'title' => t('Deploy'), + 'description' => t('Deploy a tag or branch to an environment, and (optionally) run update.php, clear cache, and revert features.'), + 'access callback' => 'devshop_hosting_task_menu_access', + 'dialog' => TRUE, + ); return $tasks; } From ca17fceaf7a052a362fc5b7cd6609f5b5cee1241 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 9 Aug 2014 11:02:12 -0400 Subject: [PATCH 0897/3476] Clone task now runs, but newly cloned site is orphaned from the project. --- devshop_projects/devshop_projects.drush.inc | 8 ++++---- devshop_projects/tasks/create.inc | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index a3cfec95b..247626266 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -109,8 +109,8 @@ function drush_devshop_platform_verify(){ } // If there is a branch to fork, set the branch_to_fork as git_ref - if (!empty($platform->environment->branch_to_fork)) { - $git_ref = $platform->environment->branch_to_fork; + if (!empty($platform->environment->settings->branch_to_fork)) { + $git_ref = $platform->environment->settings->branch_to_fork; $create_branch = TRUE; } @@ -224,7 +224,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { drush_log('[DEVSHOP] No site found for this environment. Creating...', 'notice'); // Check for site to clone - if ($platform->environment->site_to_clone) { + if ($platform->environment->settings->site_to_clone) { $servers = hosting_get_servers('db'); $args['target_platform'] = $platform->nid; @@ -233,7 +233,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // @TODO: Match settings of cloned environment. $args['new_db_server'] = key($servers); drush_log('[DEVSHOP] Cloning environment: ' . $platform->environment->name); - hosting_add_task($platform->environment->site_to_clone, 'clone', $args); + hosting_add_task($platform->environment->settings->site_to_clone, 'clone', $args); } else { devshop_projects_create_site($platform->project, $platform, $platform->environment->name); diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 4c5d16b96..82fd345f5 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -205,8 +205,8 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ $environment->settings = $source_environment->settings; // Save Clone/fork specific data to the environment - $environment->site_to_clone = $source_environment->site; - $environment->branch_to_fork = $source_environment->git_ref; + $environment->settings->site_to_clone = $source_environment->site; + $environment->settings->branch_to_fork = $source_environment->git_ref; } else { From 3bdf1d473572fff583ecb5c32123fd7f7c607a80 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 13 Aug 2014 19:15:03 -0400 Subject: [PATCH 0898/3476] Avoiding notices when looking up tasks for a site that isn't installed yet. --- devshop_projects/inc/ui.inc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index da820d448..fd9384113 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -183,7 +183,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $production_rows = array(); foreach ($project->environments as $env => $environment) { - + $site_installed = false; // Skip this if it is not enabled. if ($environment->site_status == HOSTING_SITE_DELETED && $environment->platform_status == HOSTING_PLATFORM_DELETED) { @@ -214,6 +214,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = '' . t('Installing...') . ''; } else { $row[] = devshop_hosting_site_goto_link($site); + $site_installed = TRUE; } // Display current branch @@ -265,8 +266,13 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Aegir Tasks - $site_tasklist = hosting_task_fetch_tasks($environment->site); - $site_tasklist['restore']['title'] = t('Restore Backups'); + if ($site_installed){ + $site_tasklist = hosting_task_fetch_tasks($environment->site); + $site_tasklist['restore']['title'] = t('Restore Backups'); + } + else { + $site_tasklist = array(); + } // The actions we want $site_actions = array('flush_cache', 'login_reset', 'backup'); @@ -289,7 +295,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Build links to tasks foreach ($site_actions as $task_name) { - if ($site_tasklist[$task_name]['task_permitted']){ + if (isset($site_tasklist[$task_name]) && $site_tasklist[$task_name]['task_permitted']){ $actions[] = array( 'title' => $site_tasklist[$task_name]['title'], 'href' => sprintf("node/%d/%s_%s", $environment->site, 'site', $task_name), From e7db0c4a4285e5df59533c1a290090038d668bc0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 13 Aug 2014 19:29:01 -0400 Subject: [PATCH 0899/3476] Only show link to site settings if we have a site. --- devshop_projects/inc/ui.inc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index fd9384113..dad66fb65 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -312,11 +312,13 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Grant access to Aegir pages if (user_access('administer hosting')){ - $actions[] = array( - 'title' => t('Site Settings'), - 'href' => "node/$environment->site/edit", - 'query' => drupal_get_destination(), - ); + if ($environment->site){ + $actions[] = array( + 'title' => t('Site Settings'), + 'href' => "node/$environment->site/edit", + 'query' => drupal_get_destination(), + ); + } $actions[] = array( 'title' => t('Platform Settings'), 'href' => "node/$environment->platform/edit", From 715e5c0c9c759973f12080f0fb58e041b743e0d1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 13 Aug 2014 19:30:16 -0400 Subject: [PATCH 0900/3476] Ensures that cloned sites that are on project platforms inherit the project. --- devshop_projects/devshop_projects.drush.inc | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 247626266..205c237ed 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -233,6 +233,9 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // @TODO: Match settings of cloned environment. $args['new_db_server'] = key($servers); drush_log('[DEVSHOP] Cloning environment: ' . $platform->environment->name); + + // Queue the Cloning of the site. + // @see devshop_projects_post_hosting_import_task(). hosting_add_task($platform->environment->settings->site_to_clone, 'clone', $args); } else { @@ -243,6 +246,28 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } } +/** + * Implementation of hook_post_hosting_TASK_TYPE_task() + * + * Ensures that cloned sites that are on project platforms inherit the project. + */ +function devshop_projects_post_hosting_import_task($task, $data) { + + // If this is a new platform created by a project, we will create a site here. + if ($task->ref->type == 'site') { + drush_log('[DEVSHOP] Existing site has been imported!'); + + $site = $task->ref; + $platform = node_load($site->platform); + + if ($platform->environment->name) { + drush_log('[DEVSHOP] Project & Environment found:' . $platform->project->name . '.' . $platform->environment->name); + $return = db_query('UPDATE {hosting_devshop_project_environment} SET site = %d WHERE project_nid = %d AND name = "%s"', $site->nid, $platform->project->nid, $platform->environment->name); + drush_log('[DEVSHOP] Site saved to environment.'); + } + } +} + /** * Implements hook_hosting_project_context_options() * From c9be3684286287cedd1612e3e44bd1a921c5229e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 13 Aug 2014 19:30:38 -0400 Subject: [PATCH 0901/3476] Changing data column to settings column in update hook. --- devshop_projects/devshop_projects.install | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index e3ccac0c9..6d0120a7a 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -261,4 +261,17 @@ function devshop_projects_update_13(){ $ret = array(); db_drop_table($ret, 'hosting_devshop_project_object'); return $ret; +} + +/** + * Change "data" column to "settings" column. + */ +function devshop_projects_update_14(){ + $ret = array(); + db_change_column($ret, 'hosting_devshop_project', 'data', 'settings', 'text', array( + 'not null' => FALSE, + 'size' => 'big', + 'description' => 'A serialized array of settings for this project.', + )); + return $ret; } \ No newline at end of file From d2483b549ea724433e8704af071e018630dbc780 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 13 Aug 2014 20:02:14 -0400 Subject: [PATCH 0902/3476] In site environments settings form, skip deleted platforms/sites. --- devshop_projects/inc/forms.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e02ee1654..21f895ccd 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -174,6 +174,12 @@ function devshop_projects_form(&$node) { $live_alias_options = array(''); $settings = module_invoke_all('devshop_project_settings', $node); foreach ($node->project->environments as $environment_name => $environment) { + + // Skip this if it is not enabled. + if ($environment->site_status == HOSTING_SITE_DELETED && $environment->platform_status == HOSTING_PLATFORM_DELETED) { + continue; + } + $form['project']['environments'][$environment_name] = array( '#tree' => TRUE, '#title' => $environment_name, From 0711339802d56b178f6645ebc0decf2a5ffc873a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 13 Aug 2014 20:02:27 -0400 Subject: [PATCH 0903/3476] Don't hide tasks for disabled sites. --- devshop_projects/inc/ui.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index dad66fb65..69e1e188e 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -197,6 +197,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = "$env"; if ($environment->site_status == HOSTING_SITE_DISABLED){ $output = '' . t('Disabled') . ''; + $site_installed = TRUE; // A "disabled" site might be a failed install. Load and display. $task = hosting_get_most_recent_task($environment->site, 'install'); From 6cc75fa33029041f9826f533f527e975657c2260 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 13 Aug 2014 20:05:14 -0400 Subject: [PATCH 0904/3476] Fixes #1979618: Skip loading of environment at all if hosting site and platform are deleted. --- devshop_projects/inc/nodes.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index d8a7c59ba..e8bcbe308 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -50,6 +50,11 @@ function devshop_projects_load($node) { $environments = array(); while ($environment = db_fetch_object($query)) { + // Skip this if it is not enabled. + if ($environment->site_status == HOSTING_SITE_DELETED && $environment->platform_status == HOSTING_PLATFORM_DELETED) { + continue; + } + // Save to environments array $environment->settings = (object) unserialize($environment->settings); $environments[$environment->name] = $environment; From 2302ba8c608071f62cd371c52538ef62ed17e172 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 14 Aug 2014 21:56:38 -0400 Subject: [PATCH 0905/3476] Verifying that environments and git refs exist for provision-devshop-deploy. Passing git_ref and environment as drush options instead of arguments. Improving comments --- devshop_projects/devshop_projects.drush.inc | 17 +++++++++++------ devshop_projects/inc/forms.inc | 2 ++ devshop_projects/tasks/deploy.inc | 14 ++++++++++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 4d54e08ed..ccbfbb91c 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -40,8 +40,8 @@ function drush_devshop_projects_pre_hosting_task() { // Deploy if ($task->ref->type == 'project' && $task->task_type == 'devshop-deploy') { - $task->args['git_ref'] = $task->task_args['git_ref']; - $task->args['environment'] = $task->task_args['environment']; + $task->options['git_ref'] = $task->task_args['git_ref']; + $task->options['environment'] = $task->task_args['environment']; $task->options['update'] = $task->task_args['update']; $task->options['revert'] = !empty($task->task_args['revert']); $task->options['cache'] = $task->task_args['cache']; @@ -293,6 +293,7 @@ function devshop_projects_hosting_project_context_options(&$task) { if (!empty($branches['branches'])){ $task->ref->project->settings->git['branches'] = $branches['branches']; $task->ref->project->settings->git['tags'] = $branches['tags']; + $task->ref->project->settings->git['refs'] = $branches['refs']; // Save the project node now that we have branches and tags. // Don't verify again, this is the verification process. @@ -356,6 +357,7 @@ function getBranchesAndTags($git_url = NULL){ // Build tag and branch list $branches = array(); $tags = array(); + $refs = array(); foreach ($exec_output AS $line_string){ @@ -364,16 +366,19 @@ function getBranchesAndTags($git_url = NULL){ // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 $line = trim(substr($line_string, 40)); $ref = explode("/", $line); - $branch = array_pop($ref); + $git_ref = array_pop($ref); if ($ref[1] == 'heads') { - $branches[] = $branch; + $branches[] = $git_ref; + $refs[$git_ref] = 'branch'; } else if ($ref[1] == 'tags') { - $tags[] = $branch; + $tags[] = $git_ref; + $refs[$git_ref] = 'tag'; } + } drush_log(dt('[DEVSHOP] Found !b branches and !t tags.', array('!b' => count($branches), '!t' => count($tags), )), 'ok'); - return array('branches' => $branches, 'tags' => $tags); + return array('branches' => $branches, 'tags' => $tags, 'refs' => $refs); } /** diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 21f895ccd..1c4bf316d 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -67,6 +67,8 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Save values that need to be saved. if ($form_id == 'platform_node_form' || $form_id == 'site_node_form'){ $node = $form['#node']; + + // @TODO: I don't think any of these are needed anymore $form['project'] = array( '#type' => 'value', '#value' => $node->project, diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 604501235..f5befc56f 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -52,8 +52,14 @@ function hosting_task_devshop_deploy_form($node) { return $form; } +/** + * @TODO: + * 1. Save environment with new current git ref to hostmaster using post hosting task. + * + * To go the route of creating new platforms and migrating... + * we will need to: + * 2. Create a new platform with the new git_ref. + * 3. When platform is complete, migrate the environment's site to it. + */ + -// @TODO: -// 1. Save environment with new current git ref. -// 2. Create a new platform with the new git_ref -// 3. When platform is complete, migrate the environment's site to it. \ No newline at end of file From 30847161190583b3ecfe03f1acd8a9d0bc04eccc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 15 Aug 2014 21:13:34 -0400 Subject: [PATCH 0906/3476] removing comment --- devshop_projects/tasks/deploy.inc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index f5befc56f..3fbf94a27 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -51,15 +51,3 @@ function hosting_task_devshop_deploy_form($node) { return $form; } - -/** - * @TODO: - * 1. Save environment with new current git ref to hostmaster using post hosting task. - * - * To go the route of creating new platforms and migrating... - * we will need to: - * 2. Create a new platform with the new git_ref. - * 3. When platform is complete, migrate the environment's site to it. - */ - - From 0e9f236d058ccffbc550eb9fbc52ea659791e2e1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 16:00:25 -0400 Subject: [PATCH 0907/3476] Fixing project settings form for github . --- devshop_pull/devshop_pull.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index f1cb74456..b6ab20fa8 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -167,7 +167,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { $form['project']['settings']['github']['pull_request_environments'] = array( '#type' => 'checkbox', '#title' => t('Create Environments for Pull Requests'), - '#default_value' => $node->project->settings->pull['pull_request_environments'], + '#default_value' => $node->project->settings->github['pull_request_environments'], '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), '#access' => $is_github, ); @@ -176,7 +176,7 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { $form['project']['settings']['github']['pull_request_environments_delete'] = array( '#type' => 'checkbox', '#title' => t('Delete Pull Request Environments'), - '#default_value' => $node->project->settings->pull['pull_request_environments_delete'], + '#default_value' => $node->project->settings->github['pull_request_environments_delete'], '#description' => t('When Pull Requests are closed, delete the environment.'), ); From dcfe4ba76ab1fed42c9c3bb1d2ad80617230d72c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 16:14:11 -0400 Subject: [PATCH 0908/3476] Fixing up devshop pull to use new project/environments data layout. --- devshop_pull/devshop_pull.inc | 56 +++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 02a4a11ed..9ed413b57 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -94,8 +94,9 @@ function devshop_pull_default_webhook($project, $environments_to_pull){ * GitHub action to take on webhook init * @TODO: Once it is more pluggable, put this in it's own include, or maybe it's own module. */ -function devshop_pull_github_webhook($project){ +function devshop_pull_github_webhook($project_node){ $headers = getallheaders(); + $project = $project_node->project; // @TODO: Handle form content as well. if ($headers['content-type'] == 'application/json'){ @@ -112,42 +113,45 @@ function devshop_pull_github_webhook($project){ // Add a task to pull then project's environments // Check for environments set to pull $environments_to_pull = array(); - foreach ($project->project_settings['environments'] as $env => $settings) { + foreach ($project->environments as $environment_name => $environment) { // Only pull if pull disabled or is tracking a tag. - if (!$settings['pull_disabled'] && !in_array($settings['git_branch'], $project->project_settings['git_tags'])) { - $environments_to_pull[] = $env; + if (!$environment->settings['pull_disabled'] && !in_array($environment->git_ref, $project->settings['git']['tags'])) { + $environments_to_pull[] = $environment_name; } } $args['environments'] = implode(' ', $environments_to_pull); - hosting_add_task($project->nid, 'devshop-pull', $args); + hosting_add_task($project_node->nid, 'devshop-pull', $args); $message = 'Push Received.'; break; + case 'pull_request': - $message = 'Pull Request Received.'; - $branch = $data->pull_request->head->ref; - $environment_name = "pr" . $data->pull_request->number; + // If pull request environments is enabled... + if ($project->settings->github['pull_request_environments']){ + $message = 'Pull Request Received.'; - if ($data->action == 'opened'){ - // @TODO: Add to project settings. + // @TODO: Handle forks? + $branch = $data->pull_request->head->ref; - $message = "Detected Pull Request for $branch \n"; - if ($project->settings->github['pull_request_environments']){ + // Determine environment branch. + // @TODO: Make Configurable, allow branch names to be env name + $environment_name = "pr" . $data->pull_request->number; - if (isset($project->environments[$environment_name])){ - $message .= "Environment $environment_name already exists."; - } - else { - hosting_create_environment($project, $environment_name, $branch); - $message .= "Environment $environment_name created for $project->title \n"; - } + // When PR is opened... create new environment. + if ($data->action == 'opened'){ + $message = "Detected Pull Request creation for $branch \n"; + + // @TODO: Allow cloning from "live" (or other) environment. + hosting_create_environment($project_node, $environment_name, $branch); + $message .= "Environment $environment_name created for $project_node->title \n"; } - // @TODO: synchronize action - } - elseif ($data->action == 'closed'){ - $message .= "Pull Request Closed \n"; - if ($project->settings->github['pull_request_environments_delete']){ - hosting_add_task($project->environments[$environment_name]['site'], 'delete'); - $message .= "Site $environment_name scheduled for deletion."; + + // When PR is cloded, delete environment. + elseif ($data->action == 'closed'){ + $message .= "Pull Request Closed \n"; + if ($project->settings->github['pull_request_environments_delete']){ + hosting_add_task($project->environments[$environment_name]->site, 'delete'); + $message .= "Site $environment_name scheduled for deletion."; + } } } break; From 140a57fcfc56f5dbc622c38911254e09d10fa8d3 Mon Sep 17 00:00:00 2001 From: Aegir user Date: Mon, 18 Aug 2014 21:06:07 +0000 Subject: [PATCH 0909/3476] Fixing pushes not triggering properly. --- devshop_pull/devshop_pull.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 9ed413b57..cb9a04214 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -98,6 +98,7 @@ function devshop_pull_github_webhook($project_node){ $headers = getallheaders(); $project = $project_node->project; + // @TODO: Handle form content as well. if ($headers['content-type'] == 'application/json'){ $data = json_decode($GLOBALS['HTTP_RAW_POST_DATA']); @@ -115,13 +116,14 @@ function devshop_pull_github_webhook($project_node){ $environments_to_pull = array(); foreach ($project->environments as $environment_name => $environment) { // Only pull if pull disabled or is tracking a tag. - if (!$environment->settings['pull_disabled'] && !in_array($environment->git_ref, $project->settings['git']['tags'])) { + if (!$environment->settings->pull_disabled && !in_array($environment->git_ref, $project->settings->git['tags'])) { $environments_to_pull[] = $environment_name; } } + $args['environments'] = implode(' ', $environments_to_pull); hosting_add_task($project_node->nid, 'devshop-pull', $args); - $message = 'Push Received.'; + $message = 'Push Received. Deploying code to environments: ' . $args['environments']; break; case 'pull_request': From a71c3f835fbf42f9973d751e886ce88f9cd03dc1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 17:11:53 -0400 Subject: [PATCH 0910/3476] Adding better comments. --- devshop_pull/devshop_pull.inc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 9ed413b57..801a23478 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -98,7 +98,10 @@ function devshop_pull_github_webhook($project_node){ $headers = getallheaders(); $project = $project_node->project; - // @TODO: Handle form content as well. + // @TODO: This only works with GitHub now!!! IMMEDIATELY complete issue https://www.drupal.org/node/2322593 + // so we don't leave all other git hosts in the dark :( + + // @TODO: Handle form content from github as well. if ($headers['content-type'] == 'application/json'){ $data = json_decode($GLOBALS['HTTP_RAW_POST_DATA']); @@ -110,7 +113,9 @@ function devshop_pull_github_webhook($project_node){ $message = 'Pong!'; break; case 'push': - // Add a task to pull then project's environments + + // @TODO: Limit "Pull" tasks to only run for the branches we have new code for.. + // Check for environments set to pull $environments_to_pull = array(); foreach ($project->environments as $environment_name => $environment) { @@ -119,6 +124,8 @@ function devshop_pull_github_webhook($project_node){ $environments_to_pull[] = $environment_name; } } + + // Set arguments and create "Pull" task. $args['environments'] = implode(' ', $environments_to_pull); hosting_add_task($project_node->nid, 'devshop-pull', $args); $message = 'Push Received.'; From a2e1af775dfed04bb7f1c77f53b9a854bcf86153 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 17:30:34 -0400 Subject: [PATCH 0911/3476] Fixing up settings form. Makes much more sense now! --- devshop_projects/inc/admin.inc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/admin.inc b/devshop_projects/inc/admin.inc index f09fd8e88..cf7b898fe 100644 --- a/devshop_projects/inc/admin.inc +++ b/devshop_projects/inc/admin.inc @@ -9,26 +9,27 @@ function devshop_projects_settings_form() { $form = array(); $form['devshop_project_default_path'] = array( - '#title' => t('Default Project Path'), + '#title' => t('Projects Base Path'), '#type' => 'textfield', - '#description' => t('Used in Code Path'), + '#description' => t('The folder that all projects will be created in. Each project gets their own folder, inside that is a folder for each environment.'), '#default_value' => variable_get('devshop_project_default_path', '/var/aegir/projects'), ); $form['devshop_project_default_drupal_path'] = array( - '#title' => t('Default Project Drupal Path'), + '#title' => t('Default path to Drupal'), '#type' => 'textfield', - '#description' => t('Used in Drupal Code Path'), + '#description' => t("If index.php isn't in the root of the git repo, you can edit the 'Path to Drupal' setting on each project. Set a default 'Path to Drupal' here. (For example, an Acquia hosted repo uses 'docroot'.)"), '#default_value' => variable_get('devshop_project_default_drupal_path', ''), ); + // @TODO: Add @environment as a replacement pattern. $form['devshop_project_default_base_url_pattern'] = array( - '#title' => t('Default Primary Domain Pattern'), + '#title' => t('Domain Name Pattern'), '#type' => 'textfield', - '#description' => t('Used in Primary Domain. You can use @project for project name and @hostname for server name.'), + '#description' => t('Each environment (aegir site) gets an initial URL. You can use @project for project name and @hostname for %host. Environments are put underneath this URL as subdomains.', array('%host' => $_SERVER['hostname'])), '#default_value' => variable_get('devshop_project_default_base_url_pattern', '@project.@hostname'), ); $form['devshop_projects_skip_settings'] = array( - '#title' => t('Skip Settings Page'), - '#description' => t('when starting a new project'), + '#title' => t('Block Code Path and Path to Drupal settings.'), + '#description' => t('When starting a new project, you can skip the "Code Path" and "Path to Drupal" settings, using the defaults set on this page. Check this box to block these settings from being changed.'), '#type' => 'checkbox', '#default_value' => variable_get('devshop_projects_skip_settings', TRUE), ); From 38235b9decd503d8171807f6c60b874a670f14ae Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 17:31:55 -0400 Subject: [PATCH 0912/3476] Slight rewording. --- devshop_projects/inc/admin.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/admin.inc b/devshop_projects/inc/admin.inc index cf7b898fe..dd0b96f31 100644 --- a/devshop_projects/inc/admin.inc +++ b/devshop_projects/inc/admin.inc @@ -28,7 +28,7 @@ function devshop_projects_settings_form() { '#default_value' => variable_get('devshop_project_default_base_url_pattern', '@project.@hostname'), ); $form['devshop_projects_skip_settings'] = array( - '#title' => t('Block Code Path and Path to Drupal settings.'), + '#title' => t('Block users from editing the "Code Path" and "Path to Drupal" settings.'), '#description' => t('When starting a new project, you can skip the "Code Path" and "Path to Drupal" settings, using the defaults set on this page. Check this box to block these settings from being changed.'), '#type' => 'checkbox', '#default_value' => variable_get('devshop_projects_skip_settings', TRUE), From 07095ade51a6d2227fe8c4316b232beaab775fe4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 17:34:15 -0400 Subject: [PATCH 0913/3476] better language. --- devshop_projects/inc/admin.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/admin.inc b/devshop_projects/inc/admin.inc index dd0b96f31..89b0088c4 100644 --- a/devshop_projects/inc/admin.inc +++ b/devshop_projects/inc/admin.inc @@ -28,8 +28,8 @@ function devshop_projects_settings_form() { '#default_value' => variable_get('devshop_project_default_base_url_pattern', '@project.@hostname'), ); $form['devshop_projects_skip_settings'] = array( - '#title' => t('Block users from editing the "Code Path" and "Path to Drupal" settings.'), - '#description' => t('When starting a new project, you can skip the "Code Path" and "Path to Drupal" settings, using the defaults set on this page. Check this box to block these settings from being changed.'), + '#title' => t('Block users from editing the "Code Path", and "Path to Drupal", and "Base URL" settings.'), + '#description' => t('When starting a new project, you can prevent users from altering these settings, forcing projects to use the defaults set on this page. Check this box to block these settings from being changed.'), '#type' => 'checkbox', '#default_value' => variable_get('devshop_projects_skip_settings', TRUE), ); From f3ea4e18020a533e9d290ce6643f21ec5f08eec3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 18:41:56 -0400 Subject: [PATCH 0914/3476] Deploy tag or branch links! --- devshop_projects/inc/ui.inc | 45 ++++++++++++++++++------------- devshop_projects/tasks/deploy.inc | 3 +++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 69e1e188e..7923c00ef 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -218,25 +218,34 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $site_installed = TRUE; } - // Display current branch - $row[] = "{$environment->git_ref}"; - // Create branch/tag chooser - // @TODO: Uncomment once we have a link to go to. -// $actions = array(); -// foreach ($node->project->git['branches'] as $branch){ -// $actions[] = array( -// 'title' => $branch, -// 'href' => "node/" . $platform_nid . "/deploy", -// ); -// } -// foreach ($node->project->git['tags'] as $tag){ -// $actions[] = array( -// 'title' => "tag/" . $tag, -// 'href' => "node/" . $platform_nid . "/deploy", -// ); -// } -// $row[] = theme('ctools_dropdown', $site->git_branch, $actions); + $actions = array(); + foreach ($project->settings->git['branches'] as $branch){ + $actions[] = array( + 'title' => $branch, + 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $branch, + 'query' => array( + 'token' => drupal_get_token($user->uid), + 'environment' => $environment->name, + ), + 'attributes' => array( + 'class' => 'hosting-button-dialog git-branch', + 'title' => t('Deploy branch %ref to %environment environment', array('%ref' => $branch, '%environment' => $environment->name)), + ), + ); + } + foreach ($project->settings->git['tags'] as $tag){ + $actions[] = array( + 'title' => $tag, + 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $tag, + 'query' => array('token' => drupal_get_token($user->uid)), + 'attributes' => array( + 'class' => 'hosting-button-dialog git-tag', + 'title' => t('Deploy tag %ref to %environment environment', array('ref' => $tag)), + ), + ); + } + $row[] = theme('ctools_dropdown', $environment->git_ref, $actions); if (module_exists('devshop_log')) { $row[] =l(t('Commits'), "node/$environment->site/logs/commits"); diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 3fbf94a27..eaa4bc541 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -25,10 +25,13 @@ function hosting_task_devshop_deploy_form($node) { '#type' => 'select', '#options' => $branch_options, '#required' => TRUE, + '#default_value' => arg(3) == 'ref'? arg(4): '', ); devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to deploy the chosen git branch or tag to.'), 'environment', 'Environment', 'select'); + $form['environment']['#default_value'] = isset($_GET['environment'])? $_GET['environment']: ''; + $form['update'] = array( '#title' => t('Run update.php after code pull?'), From 1c1974fc9aafae980f11dfc4a1794a4e01a1c84f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 18:43:53 -0400 Subject: [PATCH 0915/3476] Changing environments to radios. --- devshop_projects/tasks/deploy.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index eaa4bc541..66b6136cc 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -28,7 +28,7 @@ function hosting_task_devshop_deploy_form($node) { '#default_value' => arg(3) == 'ref'? arg(4): '', ); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to deploy the chosen git branch or tag to.'), 'environment', 'Environment', 'select'); + devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to deploy the chosen git branch or tag to.'), 'environment', 'Environment', 'radios'); $form['environment']['#default_value'] = isset($_GET['environment'])? $_GET['environment']: ''; From 6aaecb9c994ee7133fecba7bed4b83232d30ff8b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 18:54:36 -0400 Subject: [PATCH 0916/3476] Saving environment record post deployment. --- devshop_projects/devshop_projects.drush.inc | 22 +++++++++++++++++++++ devshop_projects/inc/ui.inc | 5 ++++- devshop_projects/tasks/deploy.inc | 1 - 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index ccbfbb91c..6137b9380 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -202,6 +202,28 @@ function devshop_projects_post_hosting_delete_task($task, $data) { } } + +/** + * Implementation of hook_post_hosting_TASK_TYPE_task() for devshop deploy tasks. + */ +function devshop_projects_post_hosting_devshop_deploy_task($task, $data) { + + // Save the deployed git ref to the environment record. + drush_log('[DEVSHOP] Environment Deployed. Saving record.'); + + $environment = $task->task_args['environment']; + $git_ref = $task->task_args['git_ref']; + + $return = db_query('UPDATE {hosting_devshop_project_environment} SET git_ref = "%s" WHERE project_nid = %d AND name = "%s"', $git_ref, $task->ref->nid, $environment); + + if ($return) { + drush_log('[DEVSHOP] New git ref saved to environment..'); + } + else { + return drush_set_error('[DEVSHOP] Environment update failed!'); + } +} + /** * Implementation of hook_post_hosting_TASK_TYPE_task */ diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 7923c00ef..593c59117 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -238,7 +238,10 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $actions[] = array( 'title' => $tag, 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $tag, - 'query' => array('token' => drupal_get_token($user->uid)), + 'query' => array( + 'token' => drupal_get_token($user->uid), + 'environment' => $environment->name, + ), 'attributes' => array( 'class' => 'hosting-button-dialog git-tag', 'title' => t('Deploy tag %ref to %environment environment', array('ref' => $tag)), diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 66b6136cc..753fb9047 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -32,7 +32,6 @@ function hosting_task_devshop_deploy_form($node) { $form['environment']['#default_value'] = isset($_GET['environment'])? $_GET['environment']: ''; - $form['update'] = array( '#title' => t('Run update.php after code pull?'), '#type' => 'checkbox', From b5479ab7fd0b3e59c5a2068da6c175b45f10bf89 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 19:04:00 -0400 Subject: [PATCH 0917/3476] Adding FontAwesome icons! --- devshop_hosting.module | 4 ++++ devshop_projects/inc/ui.inc | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 8d7df9a5e..5af96104b 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -14,6 +14,10 @@ function devshop_hosting_init(){ drupal_add_css(drupal_get_path('module', 'devshop_hosting') . '/devshop.css'); $path = drupal_get_path('module', 'devshop_hosting') . '/icon.png'; drupal_set_html_head(""); + + drupal_set_html_head(''); + + } /** diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 593c59117..9f2f31cc7 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -222,33 +222,45 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $actions = array(); foreach ($project->settings->git['branches'] as $branch){ $actions[] = array( - 'title' => $branch, + 'title' => ' ' . $branch, 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $branch, 'query' => array( 'token' => drupal_get_token($user->uid), 'environment' => $environment->name, ), + 'html' => TRUE, 'attributes' => array( 'class' => 'hosting-button-dialog git-branch', 'title' => t('Deploy branch %ref to %environment environment', array('%ref' => $branch, '%environment' => $environment->name)), ), ); + + // Detect current ref + if ($environment->git_ref == $branch){ + $current_ref = 'Branch'; + } } foreach ($project->settings->git['tags'] as $tag){ $actions[] = array( - 'title' => $tag, + 'title' => ' ' . $tag, 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $tag, 'query' => array( 'token' => drupal_get_token($user->uid), 'environment' => $environment->name, ), + 'html' => TRUE, 'attributes' => array( 'class' => 'hosting-button-dialog git-tag', 'title' => t('Deploy tag %ref to %environment environment', array('ref' => $tag)), ), ); + + // Detect current ref + if ($environment->git_ref == $tag){ + $current_ref = 'Tag'; + } } - $row[] = theme('ctools_dropdown', $environment->git_ref, $actions); + $row[] = theme('ctools_dropdown', $current_ref . ': ' . $environment->git_ref, $actions); if (module_exists('devshop_log')) { $row[] =l(t('Commits'), "node/$environment->site/logs/commits"); From a0dba01c8e27202cedb4d115676ab68fd44f77e0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 19:22:15 -0400 Subject: [PATCH 0918/3476] I was right, these are not needed anymore. --- devshop_projects/inc/forms.inc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 1c4bf316d..3ed943d95 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -67,20 +67,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Save values that need to be saved. if ($form_id == 'platform_node_form' || $form_id == 'site_node_form'){ $node = $form['#node']; - - // @TODO: I don't think any of these are needed anymore - $form['project'] = array( - '#type' => 'value', - '#value' => $node->project, - ); - $form['environment'] = array( - '#type' => 'value', - '#value' => $node->environment, - ); - $form['git_branch'] = array( - '#type' => 'value', - '#value' => $node->git_branch, - ); } } From ae00807ffb828a931c24ad6f040b6355704fbc13 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 19:42:55 -0400 Subject: [PATCH 0919/3476] Adding bootstrap for our new dropdowns. --- devshop.css | 9 +++++++++ devshop_hosting.module | 8 ++++++++ devshop_projects/inc/ui.inc | 17 ++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/devshop.css b/devshop.css index 7c7becc6d..139420513 100644 --- a/devshop.css +++ b/devshop.css @@ -148,3 +148,12 @@ body.node-type-project a.hosting-goto-site-link { font-size: 10pt; text-transform: uppercase; } + +div.node div.content ul, div#console div.messages ul { + padding-left: 0 !important; +} + +ul.dropdown-menu > li, +div.node div.content ul li { + list-style: none !important; +} diff --git a/devshop_hosting.module b/devshop_hosting.module index 5af96104b..9407ab992 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -18,6 +18,14 @@ function devshop_hosting_init(){ drupal_set_html_head(''); + drupal_set_html_head(''); + +} + +function devshop_hosting_footer(){ + return ' + + '; } /** diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 9f2f31cc7..6c634afc5 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -237,7 +237,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Detect current ref if ($environment->git_ref == $branch){ - $current_ref = 'Branch'; + $icon = 'code-fork'; } } foreach ($project->settings->git['tags'] as $tag){ @@ -257,10 +257,21 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Detect current ref if ($environment->git_ref == $tag){ - $current_ref = 'Tag'; + $icon = 'tag'; } } - $row[] = theme('ctools_dropdown', $current_ref . ': ' . $environment->git_ref, $actions); + + // Render as a Bootstrap dropdown. + $links = theme('links', $actions, array('class' => 'dropdown-menu', 'role' => 'menu')); + $html = << + + $links + +HTML; + $row[] = $html; if (module_exists('devshop_log')) { $row[] =l(t('Commits'), "node/$environment->site/logs/commits"); From c29a635abdbcb3bdf7c3bf259a0bc6b5f1f5bce3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 18 Aug 2014 19:22:15 -0400 Subject: [PATCH 0920/3476] Revert "Adding bootstrap for our new dropdowns." This reverts commit ae00807ffb828a931c24ad6f040b6355704fbc13. --- devshop.css | 9 --------- devshop_hosting.module | 8 -------- devshop_projects/inc/ui.inc | 17 +++-------------- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/devshop.css b/devshop.css index 139420513..7c7becc6d 100644 --- a/devshop.css +++ b/devshop.css @@ -148,12 +148,3 @@ body.node-type-project a.hosting-goto-site-link { font-size: 10pt; text-transform: uppercase; } - -div.node div.content ul, div#console div.messages ul { - padding-left: 0 !important; -} - -ul.dropdown-menu > li, -div.node div.content ul li { - list-style: none !important; -} diff --git a/devshop_hosting.module b/devshop_hosting.module index 9407ab992..5af96104b 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -18,14 +18,6 @@ function devshop_hosting_init(){ drupal_set_html_head(''); - drupal_set_html_head(''); - -} - -function devshop_hosting_footer(){ - return ' - - '; } /** diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 6c634afc5..9f2f31cc7 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -237,7 +237,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Detect current ref if ($environment->git_ref == $branch){ - $icon = 'code-fork'; + $current_ref = 'Branch'; } } foreach ($project->settings->git['tags'] as $tag){ @@ -257,21 +257,10 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Detect current ref if ($environment->git_ref == $tag){ - $icon = 'tag'; + $current_ref = 'Tag'; } } - - // Render as a Bootstrap dropdown. - $links = theme('links', $actions, array('class' => 'dropdown-menu', 'role' => 'menu')); - $html = << - - $links - -HTML; - $row[] = $html; + $row[] = theme('ctools_dropdown', $current_ref . ': ' . $environment->git_ref, $actions); if (module_exists('devshop_log')) { $row[] =l(t('Commits'), "node/$environment->site/logs/commits"); From 27592591abe38bc3d761e6060bbf0009000624e0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 19 Aug 2014 00:11:15 -0400 Subject: [PATCH 0921/3476] fixing environment url --- devshop_projects/inc/nodes.inc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index e8bcbe308..0468defa0 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -36,12 +36,14 @@ function devshop_projects_node_info() { function devshop_projects_load($node) { // Load the "hosting context". The unique name in the aegir system. - $additions['hosting_name'] = 'project_' . db_result(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); + $name = db_result(db_query("SELECT name AS hosting_name FROM {hosting_context} WHERE nid = %d", $node->nid)); + $additions['hosting_name'] = 'project_' . $name; // Load project data $project = db_fetch_object(db_query('SELECT * FROM {hosting_devshop_project} WHERE nid = %d', $node->nid)); // Load up all project settings. + $project->name = $name; $project->settings = (object) unserialize($project->settings); // Load Environments @@ -57,6 +59,10 @@ function devshop_projects_load($node) { // Save to environments array $environment->settings = (object) unserialize($environment->settings); + + // Environments URL: + // @TODO: Use aegir generated link + $environment->url = 'http://' . $environment->name . '.' . $project->name . '.' . $_SERVER['HTTP_HOST']; $environments[$environment->name] = $environment; } $project->environments = $environments; From 39367874417bf4972f3283390263d0bcf5b67e9c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 19 Aug 2014 00:19:18 -0400 Subject: [PATCH 0922/3476] adding git ref type as an environment property --- devshop_projects/inc/nodes.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 0468defa0..689586ea8 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -63,6 +63,8 @@ function devshop_projects_load($node) { // Environments URL: // @TODO: Use aegir generated link $environment->url = 'http://' . $environment->name . '.' . $project->name . '.' . $_SERVER['HTTP_HOST']; + $environment->git_ref_type = $project->settings->git['refs'][$environment->git_ref]; + $environments[$environment->name] = $environment; } $project->environments = $environments; From 96fb06b8b43217068d13f4d43fc7e1f73d2acb3d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 19 Aug 2014 13:35:24 -0400 Subject: [PATCH 0923/3476] locking in sync source and destinations when linking from a dashboard. --- devshop_projects/tasks/sync.inc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/devshop_projects/tasks/sync.inc b/devshop_projects/tasks/sync.inc index 9765c7e7f..6efb719f5 100644 --- a/devshop_projects/tasks/sync.inc +++ b/devshop_projects/tasks/sync.inc @@ -36,6 +36,32 @@ function hosting_task_devshop_sync_form($node) { } } + // Detect pre-selected environments + if ($_GET['source']) { + $form['source']['#default_value'] = $_GET['source']; + $form['destination']['#default_value'] = $_GET['destination']; + + $form['source']['#type'] = 'value'; + $form['destination']['#type'] = 'value'; + + $form['source_label'] = array( + '#type' => 'item', + '#title' => t('Source Environment'), + '#value' => $_GET['source'], + '#description' => t('The environment to copy data from.'), + '#prefix' => '
', + '#suffix' => '
', + ); + $form['destination_label'] = array( + '#type' => 'item', + '#title' => t('Destination Environment'), + '#value' => $_GET['destination'], + '#description' => t("This environment's data will be DESTROYED, replaced with data from the Source."), + '#prefix' => '
', + '#suffix' => '
', + ); + } + $form['database'] = array( '#title' => t('Copy database from source to destination.'), '#type' => 'checkbox', From 6c7f5f0148cef1be4283d09c1bcf76a53e05cb89 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 Aug 2014 10:29:30 -0400 Subject: [PATCH 0924/3476] Hmm, must handle saving site nid during clone AND import task. --- devshop_projects/devshop_projects.drush.inc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 6137b9380..461acc0ea 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -278,6 +278,15 @@ function devshop_projects_post_hosting_verify_task($task, $data) { } } +/** + * Implementation of hook_post_hosting_TASK_TYPE_task() for `clone` task. + * + * Ensures that cloned sites that are on project platforms inherit the project. + */ +function devshop_projects_post_hosting_clone_task($task, $data) { + devshop_projects_post_hosting_import_task($task, $data); +} + /** * Implementation of hook_post_hosting_TASK_TYPE_task() * From 30c8917baf8ea3638e9d968206063bad9b665b08 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 Aug 2014 10:44:56 -0400 Subject: [PATCH 0925/3476] Adding a hook post platform deletion to remove an environment record. --- devshop_projects/devshop_projects.drush.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 461acc0ea..9473f0fa9 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -191,6 +191,11 @@ function devshop_projects_post_hosting_delete_task($task, $data) { // We trigger platform deletion here. hosting_add_task($task->ref->platform, 'delete'); } + + // When a platform is deleted (if it is in a project), delete the environment record. + if ($task->ref->type == 'platform' && !empty($task->ref->project)) { + db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d AND platform = %d', $task->ref->project->nid, $task->ref->platform); + } // When a platform is deleted, if it is the last in the project, // and the project has been unpublished, delete the directory. From 65ffb04e6892aa2a52329ee8db840d83cb2cd8a1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 Aug 2014 11:37:27 -0400 Subject: [PATCH 0926/3476] Adding environment actions to new UI --- devshop_projects/inc/ui.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 9f2f31cc7..58b8ec131 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -350,6 +350,8 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { 'query' => drupal_get_destination(), ); } + + $node->environment_actions[$environment->name] = $actions; $row[] = theme('ctools_dropdown', t('Actions'), $actions); From 8c5a07456e0c149ff6b9f94e4c4d52d9d6ed160f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 Aug 2014 11:48:17 -0400 Subject: [PATCH 0927/3476] Allow no live environment. --- devshop_projects/inc/forms.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 3ed943d95..435790874 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -238,6 +238,7 @@ function devshop_projects_form(&$node) { // Live Environment $environments = array_keys($node->project->environments); $environments_options = array_combine($environments, $environments); + $environments_options[''] = t('No live environment'); $form['project']['settings']['live']['live_environment'] = array( '#type' => 'select', From e6e53110f96c512d9170581fe568be98fd353e2a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 Aug 2014 11:53:54 -0400 Subject: [PATCH 0928/3476] Hiding branch selector form on project settings. --- devshop_projects/inc/forms.inc | 6 ++---- devshop_projects/inc/theme.inc | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 435790874..de976d5ee 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -190,10 +190,8 @@ function devshop_projects_form(&$node) { '#value' => $environment->platform, ); $form['project']['environments'][$environment_name]['git_ref'] = array( - '#title' => t('Git Branch/Tag'), - '#type' => 'select', - '#options' => devshop_projects_git_ref_options($project), - '#default_value' => $environment->git_ref, + '#type' => 'value', + '#value' => $environment->git_ref, ); $form['project']['environments'][$environment_name]['settings'] = array(); diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index b8a03b4c6..6600f4941 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -27,9 +27,7 @@ function theme_devshop_projects_settings_form($form) { $header[] = t('Environment'); foreach (element_children($form) as $env_name) { $row = array(); - $row[] = $env_name; - $row[] = drupal_render($form[$env_name]['git_ref']); - $header['git_ref'] = t('Branch/Tag'); + $row[] = $env_name . drupal_render($form[$env_name]['git_ref']); foreach(element_children($form[$env_name]['settings']) as $setting){ if (!isset($header[$setting])){ $header[$setting] = $form[$env_name]['settings'][$setting]['#title']; From 2d44d6f8e8e915f25c41ce44f61f9168301945f5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 Aug 2014 12:00:14 -0400 Subject: [PATCH 0929/3476] Fixing deploy tags link, setting default values if there are any. --- devshop_projects/tasks/deploy.inc | 26 ++++++++++++++++++++++++++ devshop_projects/tasks/sync.inc | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 753fb9047..d09b7287c 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -32,6 +32,32 @@ function hosting_task_devshop_deploy_form($node) { $form['environment']['#default_value'] = isset($_GET['environment'])? $_GET['environment']: ''; + // Detect pre-selected environments + if ($_GET['environment']) { + $form['environment']['#type'] = 'value'; + $form['environment']['#value'] = $_GET['environment']; + + $form['git_ref']['#type'] = 'value'; + $form['git_ref']['#value'] = $_GET['git_ref']; + + $form['environment_label'] = array( + '#type' => 'item', + '#title' => t('Environment'), + '#value' => $_GET['environment'], + '#description' => t('The environment to deploy code to.'), + '#prefix' => '
', + '#suffix' => '
', + ); + $form['git_ref_label'] = array( + '#type' => 'item', + '#title' => t('Git Reference'), + '#value' => $_GET['git_ref'], + '#description' => t("The Git branch or tag to deploy."), + '#prefix' => '
', + '#suffix' => '
', + ); + } + $form['update'] = array( '#title' => t('Run update.php after code pull?'), '#type' => 'checkbox', diff --git a/devshop_projects/tasks/sync.inc b/devshop_projects/tasks/sync.inc index 6efb719f5..5dff83281 100644 --- a/devshop_projects/tasks/sync.inc +++ b/devshop_projects/tasks/sync.inc @@ -58,7 +58,7 @@ function hosting_task_devshop_sync_form($node) { '#value' => $_GET['destination'], '#description' => t("This environment's data will be DESTROYED, replaced with data from the Source."), '#prefix' => '
', - '#suffix' => '
', + '#suffix' => '
', ); } From d708fbd811846667f4b10807976ab39d81e54c1f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 21 Aug 2014 10:07:04 -0400 Subject: [PATCH 0930/3476] Adding version to environment display. --- devshop_projects/inc/nodes.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 689586ea8..faf85fed6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -65,6 +65,11 @@ function devshop_projects_load($node) { $environment->url = 'http://' . $environment->name . '.' . $project->name . '.' . $_SERVER['HTTP_HOST']; $environment->git_ref_type = $project->settings->git['refs'][$environment->git_ref]; + // Environment version + $iid = db_result(db_query("SELECT iid FROM {hosting_package_instance} i left join {hosting_package} p on p.nid=i.package_id WHERE p.package_type='platform' AND i.rid=%d", $environment->platform)); + $release = hosting_package_instance_load($iid); + $environment->version = $release->version; + $environments[$environment->name] = $environment; } $project->environments = $environments; From 0e4d96e95f57c5e2472dc1a08538a676c38f01da Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 21 Aug 2014 14:51:10 -0400 Subject: [PATCH 0931/3476] changing destination to dest to not conflict with drupal --- devshop_projects/tasks/sync.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/tasks/sync.inc b/devshop_projects/tasks/sync.inc index 5dff83281..3e6827157 100644 --- a/devshop_projects/tasks/sync.inc +++ b/devshop_projects/tasks/sync.inc @@ -39,7 +39,7 @@ function hosting_task_devshop_sync_form($node) { // Detect pre-selected environments if ($_GET['source']) { $form['source']['#default_value'] = $_GET['source']; - $form['destination']['#default_value'] = $_GET['destination']; + $form['destination']['#default_value'] = $_GET['dest']; $form['source']['#type'] = 'value'; $form['destination']['#type'] = 'value'; @@ -55,7 +55,7 @@ function hosting_task_devshop_sync_form($node) { $form['destination_label'] = array( '#type' => 'item', '#title' => t('Destination Environment'), - '#value' => $_GET['destination'], + '#value' => $_GET['dest'], '#description' => t("This environment's data will be DESTROYED, replaced with data from the Source."), '#prefix' => '
', '#suffix' => '
', From 314cd794f681554ecd96527eb5b85295d8b673b0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 21 Aug 2014 19:31:58 -0400 Subject: [PATCH 0932/3476] Loading HTTP and DB servers into environments. --- devshop_projects/inc/nodes.inc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index faf85fed6..551bdf36c 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -70,6 +70,19 @@ function devshop_projects_load($node) { $release = hosting_package_instance_load($iid); $environment->version = $release->version; + // Get servers + $environment->servers['http'] = db_result(db_query("SELECT n.title FROM {hosting_platform} p + LEFT JOIN {node} n on n.nid = p.web_server + WHERE p.nid = %d + ", $environment->platform)); + + // HTTP Server + $environment->servers['db'] = db_result(db_query("SELECT n.title FROM {hosting_site} p + LEFT JOIN {node} n on n.nid = p.db_server + WHERE p.nid = %d + ", $environment->site)); + + // Save to project environments collection. $environments[$environment->name] = $environment; } $project->environments = $environments; From 94789efb24c0448430619f02c319f752bf7a705b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Aug 2014 20:17:04 -0400 Subject: [PATCH 0933/3476] Adding server switchers to the environments ui. --- devshop_projects/inc/forms.inc | 75 +++++++++++++++++++++++----------- devshop_projects/inc/nodes.inc | 33 ++++++++++----- 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index de976d5ee..f72a809d2 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -43,31 +43,60 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } } - // On Hosting Task: Create Project form, do our own submission. - if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-create'){ - drupal_set_title(t('Create new environment')); - $form['#submit'] = array('hosting_task_devshop_create_form_submit'); - } - - // On Hosting Task: Delete Project form, do our own submission. - if ($form_id == 'hosting_task_confirm_form' && $form['task']['#value'] == 'devshop-delete') { - $node = node_load($form['nid']['#value']); - - if ($node->type == 'project') { - $form['#submit'] = array('hosting_task_devshop_delete_form_submit'); + // Hosting Task Forms + if ($form_id == 'hosting_task_confirm_form') { + switch($form['task']['#value']) { + + // Create Environment form. + case 'devshop-create': + drupal_set_title(t('Create new environment')); + $form['#submit'] = array('hosting_task_devshop_create_form_submit'); + break; + + // Migrate Form: used for changing database server. + case 'migrate': + + // To change the database server, we use the migrate form. + if ($_GET['db_server']) { + drupal_set_title(t('Migrate Database')); + $site_node = node_load($form['nid']['#value']); + $environment = $site_node->environment; + $form['help']['#value'] = t("Are you sure you want to change this site's database server?"); + + // Set DB Server values + $form['parameters']['new_db_server']['#type'] = 'value'; + $db_server = $_GET['db_server']; + $db_server_nid = db_result(db_query('SELECT nid FROM {node} WHERE type = "server" && title = "%s"', $db_server)); + $form['parameters']['new_db_server']['#default_value'] = $db_server_nid; + + // Set URI value + $form['parameters']['new_uri']['#type'] = 'value'; + $form['parameters']['new_uri']['#value'] = $form['parameters']['new_uri']['#default_value']; +dsm($form); + // Display something helpful + $form['old'] = array( + '#type' => 'item', + '#title' => t('Current Database Server'), + '#value' => l($environment->servers['db']['name'], 'node/' . $environment->servers['db']['name']), + '#weight' => '-1', + ); + + // Display something helpful + $form['new'] = array( + '#type' => 'item', + '#title' => t('New Database Server'), + '#value' => l($db_server, "node/$db_server_nid"), + '#weight' => '0', + ); + + // @TODO: not sure how to hide the radios. We'll just use CSS for now. + $form['parameters']['#prefix'] = ''; + + } + break; } } - - - // If not a part of a project, bail out. - if (empty($form['#node']->nid)){ - return; - } - - // Save values that need to be saved. - if ($form_id == 'platform_node_form' || $form_id == 'site_node_form'){ - $node = $form['#node']; - } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 551bdf36c..47711c39b 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -48,7 +48,20 @@ function devshop_projects_load($node) { // Load Environments // @TODO: Remove environments where the site has been deleted. - $query = db_query("SELECT e.*, s.status as site_status, p.status as platform_status FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid LEFT JOIN {hosting_platform} p ON e.platform = p.nid WHERE project_nid = %d ORDER BY name", $node->nid); + $query = db_query(" + SELECT + e.*, + s.status as site_status, + p.status as platform_status, + http.title as web_server, + db.title as db_server + FROM {hosting_devshop_project_environment} e + LEFT JOIN {hosting_site} s ON e.site = s.nid + LEFT JOIN {hosting_platform} p ON e.platform = p.nid + LEFT JOIN {node} http ON p.web_server = http.nid + LEFT JOIN {node} db ON s.db_server = db.nid + WHERE project_nid = %d + ORDER BY name; ", $node->nid); $environments = array(); while ($environment = db_fetch_object($query)) { @@ -70,17 +83,17 @@ function devshop_projects_load($node) { $release = hosting_package_instance_load($iid); $environment->version = $release->version; - // Get servers - $environment->servers['http'] = db_result(db_query("SELECT n.title FROM {hosting_platform} p - LEFT JOIN {node} n on n.nid = p.web_server - WHERE p.nid = %d - ", $environment->platform)); + // Get extra server info + $environment->servers['http'] = array( + 'nid' => $environment->settings->web_server, + 'name' => $environment->web_server + ); // HTTP Server - $environment->servers['db'] = db_result(db_query("SELECT n.title FROM {hosting_site} p - LEFT JOIN {node} n on n.nid = p.db_server - WHERE p.nid = %d - ", $environment->site)); + $environment->servers['db'] = array( + 'nid' => $environment->settings->db_server, + 'name' => $environment->db_server + ); // Save to project environments collection. $environments[$environment->name] = $environment; From 5a294c9f47aa5f61dc0ad05357b16876fba38715 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Aug 2014 20:22:06 -0400 Subject: [PATCH 0934/3476] Nid, not title. --- devshop_projects/inc/forms.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index f72a809d2..a34868cb7 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -77,7 +77,7 @@ dsm($form); $form['old'] = array( '#type' => 'item', '#title' => t('Current Database Server'), - '#value' => l($environment->servers['db']['name'], 'node/' . $environment->servers['db']['name']), + '#value' => l($environment->servers['db']['name'], 'node/' . $environment->servers['db']['nid']), '#weight' => '-1', ); From 30c2da8197ed5223ded85be84811cede5866b24b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 22 Aug 2014 20:22:22 -0400 Subject: [PATCH 0935/3476] stray dsm --- devshop_projects/inc/forms.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index a34868cb7..240ab892b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -72,7 +72,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Set URI value $form['parameters']['new_uri']['#type'] = 'value'; $form['parameters']['new_uri']['#value'] = $form['parameters']['new_uri']['#default_value']; -dsm($form); // Display something helpful $form['old'] = array( '#type' => 'item', From ccb7ba2d05a8b310c0db16275b5bd4cae0ef9c2e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 23 Aug 2014 10:59:31 -0400 Subject: [PATCH 0936/3476] Environment-specific server change widgets! --- devshop_projects/inc/forms.inc | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 240ab892b..a03ec8626 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -96,6 +96,56 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ break; } } + + // Platform "Edit" page. + if ($form_id == 'platform_node_form') { + dsm($form); + + $platform_node = node_load($form['nid']['#value']); + $environment = $platform_node->environment; + $form['help'] = array( + '#value' => t("Are you sure you want to change this site's web server? NOTE: You will have to change DNS as well!"), + '#weight' => -10, + ); + + // Set to values + $form['title']['#type'] = 'value'; + $form['makefile']['#type'] = 'value'; + unset($form['info']); + + if ($_GET['web_server']){ + $web_server = $_GET['web_server']; + $web_server_nid = db_result(db_query('SELECT nid FROM {node} WHERE type = "server" && title = "%s"', $web_server)); + + // Set values + $form['web_server']['#default_value'] = $web_server_nid; + $form['web_server']['#type'] = 'hidden'; + + // Load servers to display IP addresses. + $old_server = node_load($environment->servers['http']['nid']); + $new_server = node_load($web_server_nid); + + // Display something helpful + $form['old'] = array( + '#type' => 'item', + '#title' => t('Current Web Server'), + '#value' => l($environment->servers['http']['name'], 'node/' . $environment->servers['http']['nid']) . '
' . implode($old_server->ip_addresses, '
'), + '#weight' => '-1', + ); + + // Display something helpful + $form['new'] = array( + '#type' => 'item', + '#title' => t('New Web Server'), + '#value' => l($web_server, "node/$web_server_nid") . '
' . implode($new_server->ip_addresses, '
'), + '#weight' => '0', + ); + + + } + + + } } From 567270f4cbbe9163465c8037f466a05768cd4ddb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 23 Aug 2014 11:00:46 -0400 Subject: [PATCH 0937/3476] removing dsm --- devshop_projects/inc/forms.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index a03ec8626..cdf2b79f1 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -99,7 +99,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Platform "Edit" page. if ($form_id == 'platform_node_form') { - dsm($form); $platform_node = node_load($form['nid']['#value']); $environment = $platform_node->environment; From 721a16b7d659fcb38f0ee99525394460d0b044a8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 28 Aug 2014 21:06:25 -0400 Subject: [PATCH 0938/3476] Creating an integrated "environment settings" form. --- devshop_projects/devshop_projects.module | 27 ++++++++++++++++++++++++ devshop_projects/inc/theme.inc | 17 +++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index aab652d54..5ea0fe1a4 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -139,10 +139,37 @@ function devshop_projects_menu() { ); $items[$path] = array_merge($items[$path], $info); } + + // Environment settings pages + $items['node/%node/edit/%'] = array( + 'title' => 'Edit', + 'page callback' => 'devshop_project_environment_settings_page', + 'page arguments' => array(1, 3), + 'access callback' => 'node_access', + 'access arguments' => array('update', 1), + 'weight' => 1, + 'file' => 'node.pages.inc', + 'file path' => drupal_get_path('module', 'node'), + 'type' => MENU_LOCAL_TASK, + ); return $items; } +/** + * Environment settings page menu callback. + * + * Pulls in the node edit form for the site of the project. + */ +function devshop_project_environment_settings_page($project_node, $environment){ + + $site_node = node_load($project_node->project->environments[$environment]->site); + + drupal_set_title(t('Environment Settings:')); + return drupal_get_form($site_node->type . '_node_form', $site_node); +} + + /** * Implements hook_devshop_project_settings */ diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 6600f4941..9f71e11ef 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -69,3 +69,20 @@ function theme_devshop_projects_create_settings_form($form) { return $output; } + +/** + * Preprocess page + */ +function devshop_projects_preprocess_page(&$vars){ + + // On project node edit page + if (isset($vars['node']) && $vars['node']->type == 'project' && arg(2) == 'edit'){ + + $links = array(); + $node = node_load($vars['node']->nid); + foreach ($node->project->environments as $environment){ + $links[] = l($environment->name, 'node/' . $node->nid . '/edit/' . $environment->name); + } + $vars['tabs2'] = theme('item_list', $links, '', 'ul', array('class' => 'tabs secondary')); + } +} \ No newline at end of file From e2dbee79a401d30b349916a4285b2f503c80b2d0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 28 Aug 2014 21:11:29 -0400 Subject: [PATCH 0939/3476] Ttitle should be project name. --- devshop_projects/devshop_projects.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 5ea0fe1a4..d7657bbf5 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -165,7 +165,7 @@ function devshop_project_environment_settings_page($project_node, $environment){ $site_node = node_load($project_node->project->environments[$environment]->site); - drupal_set_title(t('Environment Settings:')); + drupal_set_title($project_node); return drupal_get_form($site_node->type . '_node_form', $site_node); } From 7cb5fec4c57f0186733a3acc922464b871ea1d1b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 28 Aug 2014 21:14:45 -0400 Subject: [PATCH 0940/3476] Only affect form if web server selected --- devshop_projects/inc/forms.inc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index cdf2b79f1..f1480e222 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -100,19 +100,21 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Platform "Edit" page. if ($form_id == 'platform_node_form') { - $platform_node = node_load($form['nid']['#value']); - $environment = $platform_node->environment; - $form['help'] = array( - '#value' => t("Are you sure you want to change this site's web server? NOTE: You will have to change DNS as well!"), - '#weight' => -10, - ); - // Set to values $form['title']['#type'] = 'value'; $form['makefile']['#type'] = 'value'; unset($form['info']); + // If switching webservers... if ($_GET['web_server']){ + + $platform_node = node_load($form['nid']['#value']); + $environment = $platform_node->environment; + $form['help'] = array( + '#value' => t("Are you sure you want to change this site's web server? NOTE: You will have to change DNS as well!"), + '#weight' => -10, + ); + $web_server = $_GET['web_server']; $web_server_nid = db_result(db_query('SELECT nid FROM {node} WHERE type = "server" && title = "%s"', $web_server)); From 9f06cf5467ceded1375fdcfebcd6e5076daec5b1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 28 Aug 2014 21:42:52 -0400 Subject: [PATCH 0941/3476] Adding environment settings pages. --- devshop_projects/devshop_projects.module | 2 +- devshop_projects/inc/forms.inc | 43 +++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index d7657bbf5..db688002f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -165,7 +165,7 @@ function devshop_project_environment_settings_page($project_node, $environment){ $site_node = node_load($project_node->project->environments[$environment]->site); - drupal_set_title($project_node); + drupal_set_title($project_node->title); return drupal_get_form($site_node->type . '_node_form', $site_node); } diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index f1480e222..c8a558464 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -141,14 +141,55 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#value' => l($web_server, "node/$web_server_nid") . '
' . implode($new_server->ip_addresses, '
'), '#weight' => '0', ); + } + } + // Site "Edit" page. This is our environment settings page.. + if ($form_id == 'site_node_form') { - } + $node = $form['#node']; + $environment = $node->environment; + + unset($form['info']); + + $form['settings'] = array( + '#type' => 'fieldset', + '#title' => t('Environment Settings'), + '#weight' => -10, + '#tree' => true, + ); + + $form['settings']['note'] = array( + '#value' => t('These settings apply only to the environment !site', array('!site' => l($environment->name, $environment->url))), + ); + + $form['settings']['production_mode'] = array( + '#type' => 'checkbox', + '#title' => t('Production Mode'), + '#default_value' => $environment->settings->production_mode, + '#description' => t('Prevent devshop users from overwriting the database.'), + ); + $form['settings']['pull_disabled'] = array( + '#type' => 'checkbox', + '#title' => t('Disable Pull on Commit'), + '#default_value' => $environment->settings->pull_disabled, + '#description' => t('Do not pull code to the server on commit & push.'), + ); + // Add our own submit handler + $form['buttons']['submit']['#submit'][] = 'devshop_projects_environment_settings_submit'; } } +/** + * Submit handler for site/environment settings page. + */ +function devshop_projects_environment_settings_submit($form, &$form_state){ + // @TODO: OSave the environment's data array. +// dsm($form_state); +} + /** * Implementation of hook_form(). From 25f7a9b7d36e7dc19bee7546b482df41278d0d5e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 28 Aug 2014 21:53:55 -0400 Subject: [PATCH 0942/3476] Removing environment settings from project form. --- devshop_projects/inc/forms.inc | 56 ---------------------------------- 1 file changed, 56 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c8a558464..19f1ef219 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -271,62 +271,6 @@ function devshop_projects_form(&$node) { } } - // Environment settings - $form['project']['environments'] = array( - '#type' => 'fieldset', - '#title' => t('Environment Settings'), - '#theme' => 'devshop_projects_settings_form', - '#tree' => TRUE, - ); - - $live_alias_options = array(''); - $settings = module_invoke_all('devshop_project_settings', $node); - foreach ($node->project->environments as $environment_name => $environment) { - - // Skip this if it is not enabled. - if ($environment->site_status == HOSTING_SITE_DELETED && $environment->platform_status == HOSTING_PLATFORM_DELETED) { - continue; - } - - $form['project']['environments'][$environment_name] = array( - '#tree' => TRUE, - '#title' => $environment_name, - '#theme' => 'devshop_projects_settings_table', - ); - - // Save current environment settings to detect differences later. - $form['project']['environments'][$environment_name]['orig'] = array( - '#type' => 'value', - '#value' => $environment, - ); - - // Environment properties - $form['project']['environments'][$environment_name]['site'] = array( - '#type' => 'value', - '#value' => $environment->site, - ); - $form['project']['environments'][$environment_name]['platform'] = array( - '#type' => 'value', - '#value' => $environment->platform, - ); - $form['project']['environments'][$environment_name]['git_ref'] = array( - '#type' => 'value', - '#value' => $environment->git_ref, - ); - - $form['project']['environments'][$environment_name]['settings'] = array(); - - // Environment settings - foreach ($settings as $setting_id => $setting){ - $form['project']['environments'][$environment_name]['settings'][$setting_id] = $setting; - $form['project']['environments'][$environment_name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; - $form['project']['environments'][$environment_name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; - } - - $site = node_load($environment->site); - $live_alias_options[$environment_name] = $site->hosting_name; - } - // Project Settings // Save git branches and tags $form['project']['settings']['git']['branches'] = array( From 22de932cc96b8a1655a4c879b1d86febf06aa4d9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 29 Aug 2014 09:17:58 -0400 Subject: [PATCH 0943/3476] Removing old validate code. --- devshop_projects/inc/forms.inc | 46 +++------------------------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 19f1ef219..6d7fafbc4 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -331,50 +331,10 @@ function devshop_projects_form(&$node) { /** * Implementation of hook_validate(). + * + * This function is no longer used since we have a ctools wizard for + * project creation. */ function devshop_projects_validate($node, &$form) { - // It may look find of strange that we are doing a node load when the - // exact node we want is passed in here. But if this is an edit of an - // existing node, not all of the node fields (like project_status) have - // been initialized yet. - - $node = node_load($node->nid); - - // No validation if op == Delete - if ($node->op == t('Delete')) { - return; - } - - // Full validation on when a creating a new node - $add = (arg(1) == 'add' ? TRUE : FALSE); - - // Title (project code) must not have any spaces - if(strpos($node->title, ' ') != FALSE) { - form_set_error('title', t('Project code name must not contain any white spaces.')); - } - // The project code name must not be in the hosting_context table - if (!$node->retry){ - $result = db_fetch_object(db_query("SELECT name, nid FROM {hosting_context} WHERE name = '%s'", $node->title)); - if ($node->nid != $result->nid){ - - form_set_error('title', t('Project code name is unavailable. Please choose another.')); - } - } - - // The project code name must be unique - if (!$node->retry && ($result = db_fetch_object(db_query("SELECT title FROM {node} WHERE title = '%s' AND type = 'devshop_project' AND nid <> %d", $node->title, $node->nid)))) { - form_set_error('title', t('Project code name is already is use by another project')); - } - - // Make sure the path is unique. - $cp = hosting_path_normalize($node->code_path); - if (!$node->retry && $add && $result = db_fetch_object(db_query("SELECT code_path FROM {hosting_devshop_project} WHERE code_path = '%s' AND nid <> %d", $cp, $node->nid))) { - form_set_error('code_path', t('Code path is already in use by another project')); - } - - // Directory must not exist - if (!$node->retry && $add && file_exists($cp)) { - form_set_error('code_path', t('Code path directory already exists.')); - } } From daf2df6ee13bed5e1fdff9c4c3e3ddbd376b0c43 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 29 Aug 2014 10:07:05 -0400 Subject: [PATCH 0944/3476] Passing the old project through as a value. --- devshop_projects/inc/forms.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 6d7fafbc4..4e92ed137 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -198,9 +198,14 @@ function devshop_projects_form(&$node) { $project = $node->project; + // Save last project data + $form['old'] = array( + '#value' => $node, + '#type' => 'value', + ); + // Project Settings // Every value under $form['project'] gets serialized and saved into a project's "data" column. - // @TODO: Add ['data'] so only items under 'data' get serialized. $form['project'] = array( '#tree' => TRUE, ); From e7e698995bc1388791a392b152c1816f5b82e337 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 29 Aug 2014 10:08:51 -0400 Subject: [PATCH 0945/3476] add todo --- devshop_projects/inc/forms.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 4e92ed137..91ddd9541 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -342,4 +342,6 @@ function devshop_projects_form(&$node) { */ function devshop_projects_validate($node, &$form) { + // @TODO: if there is no "live domain url" then unset the values for environment aliases + } From 853733db6c574f33a1963764413d02a0a4da36f7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 29 Aug 2014 15:25:49 -0400 Subject: [PATCH 0946/3476] Environment settings pages now working. --- devshop_projects/inc/forms.inc | 81 +++++++-- devshop_projects/inc/nodes.inc | 294 ++++++++++++++++++++------------- 2 files changed, 248 insertions(+), 127 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 91ddd9541..8727fa98e 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -150,27 +150,59 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ $node = $form['#node']; $environment = $node->environment; + // Remove aegir's helpful info panel. unset($form['info']); - $form['settings'] = array( - '#type' => 'fieldset', - '#title' => t('Environment Settings'), - '#weight' => -10, + // Help Text + $form['note'] = array( + '#value' => t('These settings apply only to the environment !site', array('!site' => l($environment->name, $environment->url))), '#weight' => -10, + ); + + $form['environment'] = array( + '#weight' => -9, '#tree' => true, ); - $form['settings']['note'] = array( - '#value' => t('These settings apply only to the environment !site', array('!site' => l($environment->name, $environment->url))), + // Values + $form['environment']['project_nid'] = array( + '#value' => $environment->project_nid, + '#type' => 'value', + ); + $form['environment']['project_name'] = array( + '#value' => $environment->project_name, + '#type' => 'value', + ); + $form['environment']['name'] = array( + '#value' => $environment->name, + '#type' => 'value', + ); + $form['environment']['git_ref'] = array( + '#value' => $environment->git_ref, + '#type' => 'value', + ); + $form['environment']['site'] = array( + '#value' => $environment->site, + '#type' => 'value', + ); + $form['environment']['platform'] = array( + '#value' => $environment->platform, + '#type' => 'value', ); - $form['settings']['production_mode'] = array( + // User-configurable Settings + $form['environment']['settings'] = array( + '#type' => 'fieldset', + '#title' => t('Environment Settings'), + '#tree' => true, + ); + $form['environment']['settings']['production_mode'] = array( '#type' => 'checkbox', '#title' => t('Production Mode'), '#default_value' => $environment->settings->production_mode, '#description' => t('Prevent devshop users from overwriting the database.'), ); - $form['settings']['pull_disabled'] = array( + $form['environment']['settings']['pull_disabled'] = array( '#type' => 'checkbox', '#title' => t('Disable Pull on Commit'), '#default_value' => $environment->settings->pull_disabled, @@ -186,8 +218,37 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ * Submit handler for site/environment settings page. */ function devshop_projects_environment_settings_submit($form, &$form_state){ - // @TODO: OSave the environment's data array. -// dsm($form_state); + + // Save the environment record. + // @TODO: Replace with an $environment->save(); + + // Ensure correct data types + $environment = (object) $form_state['values']['environment']; + $environment->settings = (array) $environment->settings; + + // Prepare record for saving + $info = new stdClass(); + $info->project_nid = $environment->project_nid; + $info->name = $environment->name; + $info->git_ref = $environment->git_ref; + $info->site = $environment->site; + $info->platform = $environment->platform; + $info->settings = serialize($environment->settings); + + // Save environment record. + if (drupal_write_record('hosting_devshop_project_environment', $info, array('project_nid', 'name'))){ + + // Remove the "Site dev.drupal.devshop.local has been updated. + drupal_get_messages('status'); + + // Send our own message + drupal_set_message(t('Environment settings saved for %env in project %project', array('%env' => $environment->name, '%project' => $environment->project_name))); + } + else { + watchdog('error', 'Environment record not saved: ' . print_r($info, 1)); + } + + $form_state['redirect'] = 'node/' . $environment->project_nid; } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 47711c39b..a048e805a 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -54,12 +54,14 @@ function devshop_projects_load($node) { s.status as site_status, p.status as platform_status, http.title as web_server, - db.title as db_server + db.title as db_server, + n.title as project_name FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid LEFT JOIN {hosting_platform} p ON e.platform = p.nid LEFT JOIN {node} http ON p.web_server = http.nid LEFT JOIN {node} db ON s.db_server = db.nid + LEFT JOIN {node} n ON e.project_nid = n.nid WHERE project_nid = %d ORDER BY name; ", $node->nid); $environments = array(); @@ -200,129 +202,187 @@ function devshop_projects_update($node) { // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); - // Save Environment records. - if (!empty($project->environments )) { - // Delete existing environment records - db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); + dsm($node,'project node on update'); - // Save each environment - foreach ($project->environments as $name => $environment){ - // Ensure correct data types - $environment = (object) $environment; - $environment->name = $name; - $environment->settings = (array) $environment->settings; + // If the live domain was removed... + // @TODO: Get "old node" values to detect removal + if (empty($project->settings->live['live_domain'])) { - $info = new stdClass(); - $info->project_nid = $node->nid; - $info->name = $name; - $info->git_ref = $environment->git_ref; - $info->site = $environment->site; - $info->platform = $environment->platform; + } - // Remove primary settings from settings array before saving. - // @TODO: These values shouldn't show up anymore. Once tested, remove. - unset($environment->settings['git_ref']); - unset($environment->settings['site']); - unset($environment->settings['platform']); - $info->settings = serialize($environment->settings); - // Save environment record. - drupal_write_record('hosting_devshop_project_environment', $info); +// +// // Save the live domain to live environment +// +// // @TODO: Remove the alias from the old "live" environment +// if ($project->settings->live['live_environment'] == $environment->name) { +// if (array_search($project->settings->live['live_domain'], $site->aliases) === FALSE) { +// $site->aliases[] = $project->settings->live['live_domain']; +// $site->needs_save = TRUE; +// } +// +// // Save www version +// if ($project->settings->live['live_domain_www']) { +// $site->aliases[] = 'www.' . $project->settings->live['live_domain']; +// $site->needs_save = TRUE; +// } +// else { +// $site->aliases = array_diff($site->aliases, array('www' . $project->settings->live['live_domain'])); +// } +// } +// // Save other environment aliases if environment_aliases is checked. +// elseif ($project->settings->live['environment_aliases']) { +// $domain = $environment->name . '.' . $project->settings->live['live_domain']; +// if (array_search($domain, $site->aliases) === FALSE) { +// $site->aliases[] = $domain; +// $site->needs_save = TRUE; +// } +// } +// } +// +// // If environment_aliases is NOT checked, remove them from the nodes. +// elseif (!$project->settings->live['environment_aliases'] && count($project->environments)){ +// foreach ($project->environments as $env => $environment){ +// $domain = $environment->name . '.' . $project->settings->live['live_domain']; +// if (!empty($site->aliases)){ +// $i = array_search($domain, $site->aliases); +// if ($i !== FALSE) { +// unset($site->aliases[$i]); +// $site->needs_save = TRUE; +// } +// } +// } +// } - // Save aegir objects - if (!empty($environment->site)){ - $site = node_load($environment->site); - $platform = node_load($environment->platform); - - // Save to array so we can apply values for settings. - $objects = array(); - $objects['site'] = &$site; - $objects['platform'] = &$platform; - - $settings_info = module_invoke_all('devshop_project_settings', $node); - - // Go through each setting, applying it to either the site or the platform. - foreach ($settings_info as $setting_name => $setting_element) { - $node_type = $settings_info[$setting_name]['#node_type']; - - // Set new value only if it has changed. - $old_value = $environment->orig->settings->{$setting_name}; - if (isset($old_value) && $objects[$node_type]->{$setting_name} != $old_value){ - $objects[$node_type]->{$setting_name} = $environment->settings[$setting_name]; - $objects[$node_type]->needs_save = TRUE; - - // If the database server is changed, a "migrate" task is needed. - if ($setting_name == 'db_server') { - $args = array(); - $args['target_platform'] = $site->platform; - $args['new_uri'] = $site->title; - $args['new_db_server'] = $environment->settings['db_server']; - hosting_add_task($site->nid, 'migrate', $args); - } - } - } - - // If git_ref changed, flag needs_save for platform. - if ($environment->orig->git_ref != $environment->git_ref){ - $platform->needs_save = TRUE; - } - - // If there's a live domain - if (!empty($project->settings->live['live_domain'])) { - - // Save the live domain - // @TODO: Remove the alias from the old "live" environment - if ($project->settings->live['live_environment'] == $environment->name) { - if (array_search($project->settings->live['live_domain'], $site->aliases) === FALSE) { - $site->aliases[] = $project->settings->live['live_domain']; - $site->needs_save = TRUE; - } - - // Save www version - if ($project->settings->live['live_domain_www']) { - $site->aliases[] = 'www.' . $project->settings->live['live_domain']; - $site->needs_save = TRUE; - } - else { - $site->aliases = array_diff($site->aliases, array('www' . $project->settings->live['live_domain'])); - } - } - // Save other environment aliases if environment_aliases is checked. - elseif ($project->settings->live['environment_aliases']) { - $domain = $environment->name . '.' . $project->settings->live['live_domain']; - if (array_search($domain, $site->aliases) === FALSE) { - $site->aliases[] = $domain; - $site->needs_save = TRUE; - } - } - } - - // If environment_aliases is NOT checked, remove them from the nodes. - elseif (!$project->settings->live['environment_aliases'] && count($project->environments)){ - foreach ($project->environments as $env => $environment){ - $domain = $environment->name . '.' . $project->settings->live['live_domain']; - if (!empty($site->aliases)){ - $i = array_search($domain, $site->aliases); - if ($i !== FALSE) { - unset($site->aliases[$i]); - $site->needs_save = TRUE; - } - } - } - } - - // Save site and platform if needed. - if ($platform->needs_save) { - node_save($platform); - } - if ($site->needs_save) { - node_save($site); - } - } - } - } } + +// +// // Save Environment records. +// // @TODO: REMOVE from project save. +// if (!empty($project->environments )) { +// // Delete existing environment records +// db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); +// +// // Save each environment +// foreach ($project->environments as $name => $environment){ +// // Ensure correct data types +// $environment = (object) $environment; +// $environment->name = $name; +// $environment->settings = (array) $environment->settings; +// +// $info = new stdClass(); +// $info->project_nid = $node->nid; +// $info->name = $name; +// $info->git_ref = $environment->git_ref; +// $info->site = $environment->site; +// $info->platform = $environment->platform; +// +// // Remove primary settings from settings array before saving. +// // @TODO: These values shouldn't show up anymore. Once tested, remove. +// unset($environment->settings['git_ref']); +// unset($environment->settings['site']); +// unset($environment->settings['platform']); +// $info->settings = serialize($environment->settings); +// +// // Save environment record. +// drupal_write_record('hosting_devshop_project_environment', $info); +// +//// @TODO: Remove 'hook_devshop_project_settings' completely. +//// // Save aegir objects +//// if (!empty($environment->site)){ +//// $site = node_load($environment->site); +//// $platform = node_load($environment->platform); +//// +//// // Save to array so we can apply values for settings. +//// $objects = array(); +//// $objects['site'] = &$site; +//// $objects['platform'] = &$platform; +//// +//// $settings_info = module_invoke_all('devshop_project_settings', $node); +//// +//// // Go through each setting, applying it to either the site or the platform. +//// foreach ($settings_info as $setting_name => $setting_element) { +//// $node_type = $settings_info[$setting_name]['#node_type']; +//// +//// // Set new value only if it has changed. +//// $old_value = $environment->orig->settings->{$setting_name}; +//// if (isset($old_value) && $objects[$node_type]->{$setting_name} != $old_value){ +//// $objects[$node_type]->{$setting_name} = $environment->settings[$setting_name]; +//// $objects[$node_type]->needs_save = TRUE; +//// +//// // If the database server is changed, a "migrate" task is needed. +//// if ($setting_name == 'db_server') { +//// $args = array(); +//// $args['target_platform'] = $site->platform; +//// $args['new_uri'] = $site->title; +//// $args['new_db_server'] = $environment->settings['db_server']; +//// hosting_add_task($site->nid, 'migrate', $args); +//// } +//// } +//// } +//// +//// // If git_ref changed, flag needs_save for platform. +//// if ($environment->orig->git_ref != $environment->git_ref){ +//// $platform->needs_save = TRUE; +//// } +// +// // If there's a live domain +// if (!empty($project->settings->live['live_domain'])) { +// +// // Save the live domain +// // @TODO: Remove the alias from the old "live" environment +// if ($project->settings->live['live_environment'] == $environment->name) { +// if (array_search($project->settings->live['live_domain'], $site->aliases) === FALSE) { +// $site->aliases[] = $project->settings->live['live_domain']; +// $site->needs_save = TRUE; +// } +// +// // Save www version +// if ($project->settings->live['live_domain_www']) { +// $site->aliases[] = 'www.' . $project->settings->live['live_domain']; +// $site->needs_save = TRUE; +// } +// else { +// $site->aliases = array_diff($site->aliases, array('www' . $project->settings->live['live_domain'])); +// } +// } +// // Save other environment aliases if environment_aliases is checked. +// elseif ($project->settings->live['environment_aliases']) { +// $domain = $environment->name . '.' . $project->settings->live['live_domain']; +// if (array_search($domain, $site->aliases) === FALSE) { +// $site->aliases[] = $domain; +// $site->needs_save = TRUE; +// } +// } +// } +// +// // If environment_aliases is NOT checked, remove them from the nodes. +// elseif (!$project->settings->live['environment_aliases'] && count($project->environments)){ +// foreach ($project->environments as $env => $environment){ +// $domain = $environment->name . '.' . $project->settings->live['live_domain']; +// if (!empty($site->aliases)){ +// $i = array_search($domain, $site->aliases); +// if ($i !== FALSE) { +// unset($site->aliases[$i]); +// $site->needs_save = TRUE; +// } +// } +// } +// } +// +// // Save site and platform if needed. +// if ($platform->needs_save) { +// node_save($platform); +// } +// if ($site->needs_save) { +// node_save($site); +// } +// } +//// } +//// } +//} + /** * Implementation of hook_delete(). */ From e7e8ba9f6e0541b37f9d9a001ae5d5d01fe9004e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 29 Aug 2014 16:58:53 -0400 Subject: [PATCH 0947/3476] Skeleton logic for live domains. --- devshop_projects/inc/nodes.inc | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index a048e805a..a9d936f5c 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -201,15 +201,35 @@ function devshop_projects_update($node) { // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); + dsm($node, 'node'); - dsm($node,'project node on update'); + $project_old = $node->old->project; + + // If the live domain was added... + if (!empty($project->settings->live['live_domain']) && empty($node->old->project->settings->live['live_domain'])) { + + // If there is a live environment, save the live domain as an alias to the live site. + + // If "environment aliases" is checked, save aliases to all environments +// drupal_set_message(t('You have added a live domain. Domain aliases have been saved to all environments.')); - // If the live domain was removed... - // @TODO: Get "old node" values to detect removal - if (empty($project->settings->live['live_domain'])) { } + // If the live domain was removed. + elseif (empty($project->settings->live['live_domain']) && !empty($node->old->project->settings->live['live_domain'])) { + + // Remove the old alias from all sites. + + drupal_set_message(t('You have removed the live domain. Aliases have been removed.')); + + + + } + elseif ($project->settings->live['live_domain'] != $node->old->project->settings->live['live_domain']) { + drupal_set_message(t('You have changed the live domain.')); + + } // // // Save the live domain to live environment From c8721c9d687d83a28b1ce7d8915514d1c34ac6e9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 29 Aug 2014 22:10:02 -0400 Subject: [PATCH 0948/3476] removing dsm --- devshop_projects/inc/nodes.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index a9d936f5c..dca21b9cb 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -201,7 +201,6 @@ function devshop_projects_update($node) { // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); - dsm($node, 'node'); $project_old = $node->old->project; From 00e41ad1024264420c74ec78c325bb3d1ae75e6c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 31 Aug 2014 09:00:27 -0400 Subject: [PATCH 0949/3476] Adding verify task info to project. --- devshop_projects/inc/nodes.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index dca21b9cb..ab3a10853 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -109,6 +109,10 @@ function devshop_projects_load($node) { // Save project object be available at $node->project. $additions['project'] = $project; + // Load verification status + $task = db_fetch_object(db_query('SELECT * FROM {hosting_task} WHERE rid = %d ORDER BY delta DESC', $node->nid)); + $additions['verify'] = $task; + return $additions; } From 6fe5015126b2d1e0a63837f4b525cd0bacbcf7bc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 31 Aug 2014 10:03:38 -0400 Subject: [PATCH 0950/3476] Fixing up branches dropdown if there is no data --- devshop_projects/inc/nodes.inc | 3 +- devshop_projects/inc/ui.inc | 73 ++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index ab3a10853..6d94a048d 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -110,7 +110,8 @@ function devshop_projects_load($node) { $additions['project'] = $project; // Load verification status - $task = db_fetch_object(db_query('SELECT * FROM {hosting_task} WHERE rid = %d ORDER BY delta DESC', $node->nid)); + $task = db_fetch_object(db_query('SELECT * FROM {hosting_task} WHERE rid = %d ORDER BY vid DESC', $node->nid)); + $task->task_status = (int) $task->task_status; $additions['verify'] = $task; return $additions; diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 58b8ec131..a60313eeb 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -220,46 +220,61 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Create branch/tag chooser $actions = array(); - foreach ($project->settings->git['branches'] as $branch){ - $actions[] = array( - 'title' => ' ' . $branch, - 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $branch, - 'query' => array( - 'token' => drupal_get_token($user->uid), - 'environment' => $environment->name, - ), - 'html' => TRUE, - 'attributes' => array( - 'class' => 'hosting-button-dialog git-branch', - 'title' => t('Deploy branch %ref to %environment environment', array('%ref' => $branch, '%environment' => $environment->name)), - ), - ); + if (!empty($project->settings->git['branches'])) { + foreach ($project->settings->git['branches'] as $branch){ + $actions[] = array( + 'title' => ' ' . $branch, + 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $branch, + 'query' => array( + 'token' => drupal_get_token($user->uid), + 'environment' => $environment->name, + ), + 'html' => TRUE, + 'attributes' => array( + 'class' => 'hosting-button-dialog git-branch', + 'title' => t('Deploy branch %ref to %environment environment', array('%ref' => $branch, '%environment' => $environment->name)), + ), + ); - // Detect current ref - if ($environment->git_ref == $branch){ - $current_ref = 'Branch'; + // Detect current ref + if ($environment->git_ref == $branch){ + $current_ref = 'Branch'; + } } } - foreach ($project->settings->git['tags'] as $tag){ + else { $actions[] = array( - 'title' => ' ' . $tag, - 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $tag, + 'title' => t('No branches found. Click to Refresh.'), + 'href' => 'node/' . $node->nid . '/project_verify', 'query' => array( 'token' => drupal_get_token($user->uid), - 'environment' => $environment->name, - ), - 'html' => TRUE, - 'attributes' => array( - 'class' => 'hosting-button-dialog git-tag', - 'title' => t('Deploy tag %ref to %environment environment', array('ref' => $tag)), ), ); + } + + if (!empty($project->settings->git['tags'])) { + foreach ($project->settings->git['tags'] as $tag){ + $actions[] = array( + 'title' => ' ' . $tag, + 'href' => 'node/' . $node->nid . '/project_devshop-deploy/ref/' . $tag, + 'query' => array( + 'token' => drupal_get_token($user->uid), + 'environment' => $environment->name, + ), + 'html' => TRUE, + 'attributes' => array( + 'class' => 'hosting-button-dialog git-tag', + 'title' => t('Deploy tag %ref to %environment environment', array('ref' => $tag)), + ), + ); - // Detect current ref - if ($environment->git_ref == $tag){ - $current_ref = 'Tag'; + // Detect current ref + if ($environment->git_ref == $tag){ + $current_ref = 'Tag'; + } } } + $row[] = theme('ctools_dropdown', $current_ref . ': ' . $environment->git_ref, $actions); if (module_exists('devshop_log')) { From b1381785db6ac6bdaec061469a83c378643367bc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 31 Aug 2014 10:30:10 -0400 Subject: [PATCH 0951/3476] adding TODOs to the deploy form. --- devshop_projects/tasks/deploy.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index d09b7287c..467808f03 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -34,6 +34,11 @@ function hosting_task_devshop_deploy_form($node) { // Detect pre-selected environments if ($_GET['environment']) { + + // @TODO: Check that Git ref exists! + // @TODO: Display current branch! + // @TODO: Display timestamps for the last commit in the two Git Refs! + $form['environment']['#type'] = 'value'; $form['environment']['#value'] = $_GET['environment']; From a842258c0593bcaceaf82afefa57aa72d34e9b64 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 31 Aug 2014 10:56:54 -0400 Subject: [PATCH 0952/3476] Removing links to platform and site settings. --- devshop_projects/inc/ui.inc | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index a60313eeb..23fa03a1c 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -349,22 +349,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { ); } } - - // Grant access to Aegir pages - if (user_access('administer hosting')){ - if ($environment->site){ - $actions[] = array( - 'title' => t('Site Settings'), - 'href' => "node/$environment->site/edit", - 'query' => drupal_get_destination(), - ); - } - $actions[] = array( - 'title' => t('Platform Settings'), - 'href' => "node/$environment->platform/edit", - 'query' => drupal_get_destination(), - ); - } $node->environment_actions[$environment->name] = $actions; From ae61b6851b7a2d2778fd2df46a3617d4a4cdc3bb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 31 Aug 2014 11:44:01 -0400 Subject: [PATCH 0953/3476] Fixing devshop pull status saving. --- devshop_pull/devshop_pull.inc | 4 +++- devshop_pull/devshop_pull.module | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index b530310a0..c8c455d9a 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -42,6 +42,8 @@ function devshop_pull_callback($project_hosting_context, $hash) { } // All checks pass! Server is allowed to trigger tasks! else { + $status = DEVSHOP_PULL_STATUS_OK; + // @TODO: Make this pluggable. // Check headers for GitHub Integration $headers = getallheaders(); @@ -55,7 +57,7 @@ function devshop_pull_callback($project_hosting_context, $hash) { // Save the project node with last pull info. $pull_settings['last_pull'] = time(); - $pull_settings['last_pull_status'] = DEVSHOP_PULL_STATUS_OK; + $pull_settings['last_pull_status'] = $status; $pull_settings['last_pull_ip'] = ip_address(); $project_node->project->settings->pull = $pull_settings; diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index b6ab20fa8..0d16abd13 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -203,16 +203,15 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { $output = ''; $status = (int) $pull_data['last_pull_status']; + $node->pull_status = $status; // If access denied, provide link to settings page if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ - $output = '' . t('Access Denied') . '
'; - $output .= '' . hosting_format_interval($pull_data['last_pull']) . '
'; - $output .= t('Commit notification recieved from %ip, but the IP is not allowed to trigger tasks. See !link.', array( - '!link' => l(t('DevShop Pull Settings'), 'admin/hosting/devshop_pull'), + $output .= t('Commit notification received %ago from %ip, but that IP is not allowed to trigger tasks. See !link.', array( + '!link' => l(t('DevShop Pull Settings'), '/admin/hosting/devshop/pull'), '%ip' => $pull_data['last_pull_ip'], + '%ago' => hosting_format_interval($pull_data['last_pull']) )); - } // If OK, show how much time has passed. elseif ($status == DEVSHOP_PULL_STATUS_OK) { @@ -220,8 +219,8 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { } // Otherwise, we assume no commit notification recieved. else { - $output .= t('No commit notifications received. Setup your Git host to ping'); - $output .= strtr(" ", array('!url' => $url)); + $output .= t('No commit notifications received. Setup a webhook on your Git host to ping: '); + $output .= strtr(" ", array('!url' => $url)); } $node->content['info']['last_pull'] = array( '#type' => 'item', @@ -229,6 +228,7 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { '#weight' => 32, '#value' => $output, ); + $node->pull_message = $output; } } } From 9af2ab8d3066e319b61f18b94ee6ecc194716cbb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 31 Aug 2014 15:16:50 -0400 Subject: [PATCH 0954/3476] put live environment on top. --- devshop_projects/inc/nodes.inc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 6d94a048d..6a4b1b1b5 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -100,7 +100,18 @@ function devshop_projects_load($node) { // Save to project environments collection. $environments[$environment->name] = $environment; } - $project->environments = $environments; + + // Put live environment at the top. + if ($project->settings->live['live_environment']) { + $live_env = $project->settings->live['live_environment']; + $project->environments = array(); + $project->environments[$live_env] = $environments[$live_env]; + unset($environments[$live_env]); + $project->environments += $environments; + } + else { + $project->environments = $environments; + } // Make project name and status available. $project->name = $node->title; From ff4cc2fbac5a8f3436dbd267b2b4eeb078e38ed6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 4 Sep 2014 18:08:51 -0400 Subject: [PATCH 0955/3476] Moving create wizard. --- devshop_projects/devshop_projects.module | 4 +- devshop_projects/inc/create-wizard.inc | 954 ----------------------- devshop_projects/inc/create/create.inc | 954 +++++++++++++++++++++++ devshop_projects/inc/create/step-1.inc | 0 4 files changed, 956 insertions(+), 956 deletions(-) create mode 100644 devshop_projects/inc/create/create.inc create mode 100644 devshop_projects/inc/create/step-1.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index aab652d54..c54dee8d7 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -112,8 +112,8 @@ function devshop_projects_menu() { 'page arguments' => array(3), 'access arguments' => array('create project'), 'description' => 'Start a new Drupal website project.', - 'file' => 'create-wizard.inc', - 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', + 'file' => 'create.inc', + 'file path' => drupal_get_path('module', 'devshop_projects') . '/create', ); // Ajax endpoint for reloads diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index e11948995..e69de29bb 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -1,954 +0,0 @@ - NULL, - ); - - // Skip step 2 if needed - $skip = variable_get('devshop_projects_skip_settings', TRUE); - if ($skip) { - $form_info['order'] = array( - 'git_url' => t('Step 1: Source'), - 'environments' => t('Step 2: Environments'), - 'sites' => t('Step 3: Install Profile'), - ); - } - - // Setup project - $project = ctools_object_cache_get('project', NULL); - - // Setup Step. - if ($step == NULL){ - drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); - } - - // Create default project object - if (empty($project)){ - // set form to first step -- we have no data - $step = current(array_keys($form_info['order'])); - $project = new stdClass(); - $project->step = $step; - $project->git_url = ''; - $project->project_nid = NULL; - $project->title = ''; - $project->environments = array('NEW' => array()); - - // ** set the storage object so its ready for whatever comes next - ctools_object_cache_set('project', $form_state['cache name'], $project); - } else { - // Quickly save the current step - $project->step = $step; - ctools_object_cache_set('project', $form_state['cache name'], $project); - } - - // Check verification status - // @TODO: We should be able to get the error messages... - $project_node = node_load($project->project_nid); - if (!empty($project_node->nid)){ - $tasks = hosting_task_fetch_tasks($project_node->nid); - } - if (isset($tasks['verify']['nid'])){ - - } - - // Get "verify" task status for the project - $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; - $project->verify_task_nid = $tasks['verify']['nid']; - - // If project verification failed, we need to ask for a new git url. - if ($project->verify_task_status == HOSTING_TASK_ERROR && !empty($project_node->nid)){ - $project->verify_error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, 'error')); - // If not on the first step, go to it. - if ($step != current(array_keys($form_info['order']))){ - drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); - } - } else { - $project->verify_error = NULL; - } - - // All forms can access $form_state['project']; - $form_state['project'] = $project; - - // Saving the last visited step for redirects from node - $_SESSION['last_step'] = $step; - - // Generate our ctools form and output - $output = ctools_wizard_multistep_form($form_info, $step, $form_state); - return $output; -} - -/** - * The form_info for the ctools wizard - */ -function devshop_projects_create_wizard_info(){ - return array( - 'id' => 'devshop_project_create', - 'path' => "hosting/projects/add/%step", - 'show trail' => TRUE, - 'show back' => TRUE, - 'show cancel' => TRUE, - 'show return' => FALSE, - 'next text' => 'Next', - 'next callback' => 'devshop_projects_create_wizard_next', - 'finish callback' => 'devshop_projects_create_wizard_finish', - 'cancel callback' => 'devshop_projects_create_wizard_cancel', - 'order' => array( - 'git_url' => t('Step 1: Source'), - 'settings' => t('Step 2: Settings'), - 'environments' => t('Step 3: Environments'), - 'sites' => t('Step 4: Install Profile'), - ), - 'forms' => array( - 'git_url' => array( - 'form id' => 'devshop_project_create_step_git' - ), - 'settings' => array( - 'form id' => 'devshop_project_create_step_settings' - ), - 'environments' => array( - 'form id' => 'devshop_project_create_step_environments' - ), - 'sites' => array( - 'form id' => 'devshop_project_create_step_sites' - ), - ), - ); -} - -/** - * WIZARD TOOLS - */ - - -/** - * NEXT callback - * Saves anything in $form_state['project'] to ctools cache. - * - * The form submit callbacks are responsible for putting data into - * $form_state['project']. - */ -function devshop_projects_create_wizard_next(&$form_state) { - $project = &$form_state['project']; - $cache = ctools_object_cache_set('project', $form_state['cache name'], $project); -} - - - - -/** - * CANCEL callback - * Callback generated when the 'cancel' button is clicked. - * Remove the project data cache and send back to projects page. - */ -function devshop_projects_create_wizard_cancel(&$form_state) { - // Update the cache with changes. - $project = &$form_state['project']; - ctools_object_cache_clear('project', $form_state['cache name']); - - // Redirect to projects list - $form_state['redirect'] = 'hosting/projects'; - - // If we have a project node, create a "delete" hosting task - if (!empty($project->project_nid)){ - hosting_add_task($project->project_nid, 'delete'); - } - - // Removing last step session variable. - unset($_SESSION['last_step']); - -} - -/** - * WIZARD STEPS - */ - - -/********** - * STEP 1 - * Git URL - **********/ - - -/** - * STEP 1: Form - * - * Just get the Git URL. If we can't access the code, all else should fail. - */ -function devshop_project_create_step_git(&$form, &$form_state) { - $project = &$form_state['project']; - $project_node = node_load($project->project_nid); - - if ($project->verify_error){ - $form['note'] = array( - '#value' => '
' . $project->verify_error . '
', - '#type' => 'markup', - ); - - // Display some help for certain errors - if ($project->verify_error == '[DEVSHOP] Error retrieving remote information: Host key verification failed. fatal: The remote end hung up unexpectedly'){ - $form['note']['#value'] .= '
' . t('You might have to authorize the host your are trying to connect to first. Add "StrictHostKeyChecking no" to your ~/.ssh/config file to avoid this for all domains.') . '
'; - } - } - - $form['git_url'] = array( - '#type' => 'textfield', - '#required' => 1, - '#title' => t('Git URL'), - '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), - '#default_value' => $project_node->project->git_url, - ); - - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository. NOTE: You cannot change this, but you can delete and start over.Choose wisely.'), - '#size' => 40, - '#default_value' => $project_node->title, - '#maxlength' => 255, - ); - - // If there is already a title, make this an item. - if (!empty($project->title)) { - // Force title - $form['title']['#value'] = $form['title']['#default_value']; - $form['title']['#type'] = 'value'; - $form['title']['#description'] = t('You cannot change the name of your project once it has been created. If you must, click the "Cancel" button to delete this project, then create a new one.'); - - // Be nice and show it. - $form['title_display'] = $form['title']; - $form['title_display']['#type'] = 'item'; - } - - // Display helpful tips for connecting. - $pubkey = variable_get('devshop_public_key', ''); - - // If we don't yet have the server's public key saved as a variable... - if (empty($pubkey)){ - $output = t("For convenience, the server's SSH public key will be displayed here, once you run the following command on your server:"); - $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; - $output .= "
"; - } else { - // @TODO: Make this Translatable - $output = <<If you haven't granted this server access to your Git repository, you should do so now using it's public SSH key. - -HTML; - } - - // Add info about connecting to Repo - $form['connect'] = array( - '#type' => 'item', - '#title' => t('Repository Access'), - '#description' => $output, - ); -} - -/** - * STEP 1: Validate - * - * This is where we try and connect to the remote branches. - * - * As long as the SSH for www-data is setup right, this will work for private repos. - * @TODO: Figure out any security implications of giving www-data an ssh private key and telling users that - * (if they want web-based validation) they have to add www-data's SSH key to the repo as well. - * - * This is also where we get all the tags and branches. - */ -function devshop_project_create_step_git_validate(&$form, &$form_state) { - $project = &$form_state['project']; - - // No spaces or special characters allowed. - $project_name = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive - if (!_hosting_valid_fqdn($project_name)) { - form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); - } - if (!ctype_alnum($project_name)) { - form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); - } - - form_set_value($form['title'], $project_name, $form_state); - - // Check for duplicate project name here. - $node = hosting_context_load($project_name); - if ($node->nid != $project->project_nid){ - form_set_error('title', t('That name is in use. Please try again.')); - } -} - -/** - * STEP 1: Submit - */ -function devshop_project_create_step_git_submit(&$from, &$form_state) { - global $user; - - $project = &$form_state['project']; - - // If the project already exists, this means the git url has changed... - if ($project->project_nid) { - // Change the git url and save the node. Verification SHOULD run again. - $node = node_load($project->project_nid); - $node->project->git_url = $form_state['values']['git_url']; - node_save($node); - } - // Create new project node - elseif (empty($project->project_nid)){ - // Create the project node now. We will update it with the chosen path. - // This is so we can check for branches and save the hosting_context as soon - // as possible. - $node = new stdClass; - $node->title = $form_state['values']['title']; - - - $node->type = 'project'; - $node->status = 0; - $node->uid = $user->uid; - $node->name = $user->name; - - $node->project = new stdClass(); - $node->project->git_url = $form_state['values']['git_url']; - $node->project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $form_state['values']['title']; - - // Setup project url. - $base_url = $_SERVER['SERVER_NAME']; - $server = variable_get('devshop_project_master_url', $base_url); - $node->project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => $form_state['values']['title'], '@hostname' => $server)); - $node->project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); - - // Save project node. - if ($node = node_submit($node)) { - node_save($node); - } - - // Save NID to ctools object cache. - if ($node->nid){ - $project->project_nid = $node->nid; - } - } - - //Verify if is need to skip. - $skip = variable_get('devshop_projects_skip_settings', TRUE); - if ($skip) { - $project->step = 'environments'; - } -} - -/********** - * STEP 2 - * Project Settings - *********/ - -/** - * STEP 2: Form - */ -function devshop_project_create_step_settings(&$form, &$form_state) { - $project = &$form_state['project']; - - - $form['code_path'] = array( - '#type' => 'textfield', - '#title' => t('Code path'), - '#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $project->code_path, - '#maxlength' => 255, - ); - $form['drupal_path'] = array( - '#type' => 'textfield', - '#title' => t('Path to Drupal'), - '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), - '#size' => 40, - '#default_Value' => $project->drupal_path, - '#maxlength' => 255, - ); - $form['base_url'] = array( - '#type' => 'textfield', - '#title' => t('Primary Domain'), - '#description' => t('All sites will be under a subdomain of this domain.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $project->base_url, - '#maxlength' => 255, - ); -} - -/** - * STEP 2: Validate - */ -function devshop_project_create_step_settings_validate(&$from, &$form_state) { - $project = &$form_state['project']; - - // Code path and domain must be unique - if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND code_path = "%s";', $form_state['values']['code_path']))){ - form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); - } - if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND base_url = "%s";', $form_state['values']['base_url']))){ - form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); - } - -} - -/** - * STEP 2: Submit - */ -function devshop_project_create_step_settings_submit(&$from, &$form_state) { - $project = &$form_state['project']; - - // Save code path and base url. - $project_node = node_load($project->project_nid); - $project_node->project->code_path = $form_state['values']['code_path']; - $project_node->project->drupal_path = $form_state['values']['drupal_path']; - $project_node->project->base_url = $form_state['values']['base_url']; - - $project_node->no_verify; - node_save($project_node); -} - - -/********** - * STEP 3 - * Project Environments - *********/ - -/** - * STEP 3: Form - */ -function devshop_project_create_step_environments(&$form, &$form_state) { - $project_cache = &$form_state['project']; - $project_node = node_load($project_cache->project_nid); - $project = $project_node->project; - - if ($project_cache->verify_task_status == HOSTING_TASK_QUEUED || $project_cache->verify_task_status == HOSTING_TASK_PROCESSING) { - $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; - $project_cache->no_next = TRUE; - - $form['note'] = array( - '#type' => 'markup', - '#value' => $note, - ); - $form['not_ready'] = array( - '#type' => 'value', - '#value' => TRUE, - ); - // Add code to reload the page when complete. - devshop_form_reloader($form, 'project'); - return; - } - - // this JS handles the form element hiding/showing - $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; - drupal_add_js($path); - - $settings = module_invoke_all('devshop_project_settings', $project_node); - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - $form['project'] = array( - '#tree' => TRUE, - ); - $form['project']['environments'] = array( - '#theme' => 'devshop_projects_create_settings_form', - '#tree' => TRUE, - '#prefix' => '
', - '#suffix' => '
', - ); - - // Ensure a blank row exists (happens when using 'Back' button) - if (!is_array($project->environments['NEW'])){ - $project->environments['NEW'] = array(); - } - foreach ($project->environments as $name => $environment) { - // No platforms exist yet - if ($name == 'NEW') { - $env_title = ''; - } else { - $env_title = $name; - } - - $form['project']['environments'][$name] = array( - '#tree' => TRUE, - '#type' => 'fieldset', - '#theme' => 'devshop_projects_settings_table', - ); - - // Environment properties - $form['project']['environments'][$name]['title'] = array( - '#type' => 'textfield', - '#title' => t('Environment Name'), - '#default_value' => $env_title, - '#size' => 6, - '#maxlength' => 64, - '#attributes' => array( - 'placeholder' => t('name'), - 'autofocus' => 'autofocus', - ), - '#field_prefix' => 'http://', - '#field_suffix' => "." . $project->base_url, - ); - $form['project']['environments'][$name]['site'] = array( - '#type' => 'value', - '#value' => $environment->site, - ); - $form['project']['environments'][$name]['platform'] = array( - '#type' => 'value', - '#value' => $environment->platform, - ); - $form['project']['environments'][$name]['git_ref'] = array( - '#title' => t('Git Branch/Tag'), - '#type' => 'select', - '#options' => devshop_projects_git_ref_options($project), - '#default_value' => $environment->git_ref, - ); - - // Environment settings - // Add environment settings form elements - foreach ($settings as $setting_id => $setting){ - $form['project']['environments'][$name]['settings'][$setting_id] = $setting; - $form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; - $form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; - $form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; - } - - //Now add button. - $form['add_environment'] = array( - '#type' => 'submit', - '#value' => t('Add environment'), - '#name' => 'add_environment', - '#submit' => array('devshop_projects_create_wizard_add_new_environment'), - '#prefix' => '
', - '#suffix' => '
', - ); - } -} - - -/** - * STEP 3: Validate - */ -function devshop_project_create_step_environments_validate(&$form, &$form_state) { - $project = &$form_state['project']; - - $values = &$form_state['values']; - - // Changes NEW environment data to $title - if ($values['project']['environments']['NEW']['title']){ - $new_env = $values['project']['environments']['NEW']['title']; - $new_env_settings = $values['project']['environments']['NEW']; - $values['project']['environments'][$new_env] = $new_env_settings; - - // Create the next NEW environment. Unset makes sure its always last. - unset($values['project']['environments']['NEW']); - - // If "add environment" button clicked, add another row. - if ($form_state['clicked_button']['#name'] == 'add_environment'){ - $values['project']['environments']['NEW'] = array(); - } - } else { - unset($values['project']['environments']['NEW']); - } - - // Check environment titles - foreach ($values['project']['environments'] as $env => $env_settings) { - // Check for illegal chars - if ($env != 'NEW' && !empty($env_settings['title'])){ - if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { - $form_item = 'environments][' . $env . '][title'; - form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); - } - } - } - - // Reject if empty - if (count($values['project']['environments']) < 1){ - if ($form_state['clicked_button']['#name'] == 'add_environment'){ - form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); - } else { - form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); - } - } -} - -/** - * STEP 3: SUBMIT - */ -function devshop_project_create_step_environments_submit(&$form, &$form_state) { - // Get project and reset properties.. - $project_node = node_load($form_state['values']['nid']); - $settings = module_invoke_all('devshop_project_settings', $project_node); - - // Create these platforms, if they don't exist yet - foreach ($form_state['values']['project']['environments'] as $name => $environment) { - - // If platform exists, it's because user went back in the wizard. - $platform_nid = $project_node->project->environments[$name]->platform; - - // If the platform already exists, update settings and verify it again. - if ($platform_nid) { - // @TODO: Apply settings and re-save platform. - hosting_add_task($platform_nid, 'verify'); - } - // If platform hasn't been created yet, do so now! - else { - - $platform = new stdClass; - $platform->title = $project_node->title . '_' . $name; - - // Platform publish_path - if (!empty($project_node->project->drupal_path)) { - $platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; - } - else { - $platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; - } - - // Other attributes - $platform->web_server = $environment['web_server']; - - foreach ($settings as $setting_name => $element){ - if ($element['#node_type'] == 'platform'){ - $platform->{$setting_name} = $environment['settings'][$setting_name]; - } - } - $platform_node = _devshop_projects_node_create('platform', $platform); - } - - $environment['platform'] = $platform_node->nid; - $environment['git_ref'] = $environment['git_ref']; - $project_node->project->environments[$name] = (object) $environment; - } - - // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); - - foreach ($removed_environments as $environment_name => $settings) { - // @TODO: Determine what to do here based on task status... - // if verify task hasn't even run yet (and has never run) we can just delete - // the platform node. - - // Create 'delete' task for the removed platform - $platform_nid = $settings['platform']; - if ($platform_nid){ - hosting_add_task($platform_nid, 'delete'); - } - } - - // Somehow, NEW environment still appears! - unset($project_node->project->environments['NEW']); - $project_node->no_verify = TRUE; - node_save($project_node); -} - -/********** - * STEP 4 - * Project Settings - *********/ - -/** - * STEP 4: Form - */ -function devshop_project_create_step_sites(&$form, &$form_state) { - $project = &$form_state['project']; - $project_node = node_load($project->project_nid); - - $profiles = array(); - $available_profiles = array(); - $completed = TRUE; - - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - - // Display the platforms - $rows = array(); - $header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); - $all_tasks_queued = TRUE; - $all_tasks_succeeded = TRUE; - - foreach ($project_node->project->environments as $name => $environment){ - - // Get platform and latest verify task. - $platform_nid = $environment->platform; - $platform = node_load($platform_nid); - $task = hosting_get_most_recent_task($platform_nid, 'verify'); - - // Build a table. - $row = array(); - $row['name'] = $name; - $row['branch'] = $environment->git_ref; - $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); - - // If platform verified successfully: - if ($task->task_status == HOSTING_TASK_SUCCESS) { - - // It's not really ready until we get a version. - if (empty($platform->release->version)){ - $completed = FALSE; - $row['version'] = '...'; - $row['profiles'] = '...'; - } - - // Collect install profiles - $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); - if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { - $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); - $row['profiles'] = implode(', ', $profiles[$name]); - } else { - $profiles[$name] = array(); - } - - // If no tasks have failed, save available profiles - if ($all_tasks_succeeded){ - if (empty($available_profiles)){ - $available_profiles = $profiles[$name]; - } else { - $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); - } - } - } - // If platform verification failed: - elseif ($task->task_status == HOSTING_TASK_ERROR) { - $completed = TRUE; - $all_tasks_succeeded = FALSE; - $available_profiles = array(); - - $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); - - $row['version'] = array( - 'data' => t('Platform verification failed: %error', array('%error' => $error)), - 'colspan' => 2, - ); - - // @TODO: Get task log error message - - } - // If platform is still processing: - elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { - $completed = FALSE; - $row['version'] = '...'; - $row['profiles'] = '...'; - } - - // If a single task is not queued, $all_tasks_queued == FALSE - if ($task->task_status != HOSTING_TASK_QUEUED){ - $all_tasks_queued = FALSE; - } - - // Add hosting task status. - $row['status'] = _hosting_parse_error_code($task->task_status); - - // Store rows for display - $rows[] = $row; - } // end foreach platform - - // Output our table. - $form['platforms'] = array( - '#type' => 'markup', - '#value' => theme('table', $header, $rows), - ); - - // Not completed means show all tasks are not completed (or errored) - if (!$completed){ - $project->no_finish = TRUE; - $note = '

' . t('Please wait while we clone your repo and verify your drupal code.') . '

'; - - $form['help'] = array( - '#type' => 'markup', - '#value' => $note, - ); - - devshop_form_reloader($form, 'platform'); - return $form; - } - // If no available profiles: - elseif (count($available_profiles) == 0) { - $project->no_finish = TRUE; - $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; - - $form['error'] = array( - '#type' => 'markup', - '#value' => $note, - ); - return $form; - } else { - $project->no_finish = FALSE; - - // Install Profile - // Sensible default? - // Lets go with standard for now... we can update later. - if (isset($available_profiles['standard'])) { - $default_profile = 'standard'; - } - // If 'drupal' profile exists, it is likely drupal6! - elseif (isset($available_profiles['drupal'])) { - $default_profile = 'drupal'; - } - - $form['install_profile'] = array( - '#type' => 'radios', - '#options' => $available_profiles, - '#title' => t('Project Install Profile'), - '#required' => 1, - '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), - '#default_value' => $default_profile, - ); - } -} - -/** - * STEP 4: Validate - */ -function devshop_project_create_step_sites_validate(&$from, &$form_state) { - $project = &$form_state['project']; - - if (empty($form_state['values']['install_profile'])){ - form_set_error('install_profile', t('You must choose an install profile')); - } -} - -/** - * STEP 4: Submit - * - * Save install profile to the project object (project and site node creation - * happens on wizard finish.) - */ -function devshop_project_create_step_sites_submit(&$from, &$form_state) { - $project = &$form_state['project']; - $project->install_profile = $form_state['values']['install_profile']; -} - - -/** - * FINISH callback - * Callback generated when the add page process is finished. - */ -function devshop_projects_create_wizard_finish(&$form_state) { - - $project = &$form_state['project']; - $project_node = node_load($project->project_nid); - - // Save the extra options to the project node. - $project_node->project->install_profile = $project->install_profile; - - // Create the site nodes, saving to the environment. - // @TODO: Can we speed things up here by only running install for the first, - // then "Cloning" to create the rest? - foreach ($project_node->project->environments as $environment_name => &$environment) { - // @TODO: Does this set the http_server as well?? Doesn't look like it. - $db_server = $environment->db_server; - $site_node = devshop_projects_create_site($project_node->project, node_load($environment->platform), $environment_name, $db_server); - $environment->site = $site_node->nid; - } - - // Set to not verify and to publish. - $project_node->no_verify = TRUE; - $project_node->status = 1; - node_save($project_node); - - ctools_object_cache_clear('project', $form_state['cache name']); - $form_state['redirect'] = 'node/' . $project_node->nid; - - // Removing last step session variable. - unset($_SESSION['last_step']); - - // Friendly message - drupal_set_message(t('Your project has been created. Your sites are being installed.')); -} - -/** - * Returns JSON showing the state of the project - */ -function devshop_projects_add_status($type = 'platform'){ - $return = array(); - - // Get Project Cache - ctools_include('wizard'); - ctools_include('object-cache'); - $project = ctools_object_cache_get('project', NULL); - - - $project_node = node_load($project->project_nid); - - $all_tasks_completed = TRUE; - $nids = array(); - - // When checking project... - if ($type == 'project') { - $nids = array($project_node->nid); - } - - // When checking platforms... - if ($type == 'platform') { - foreach ($project_node->project->environments as $name => $environment){ - $nids[] = $environment->platform; - } - } - - // Check verification task for all nids - foreach ($nids as $nid){ - $task = hosting_get_most_recent_task($nid, 'verify'); - $return['tasks'][$nid] = _hosting_parse_error_code($task->task_status); - - // If task is not completed, mark all tasks not complete. - if ($task->task_status == HOSTING_TASK_SUCCESS || $task->task_status == HOSTING_TASK_ERROR) { - continue; - } else { - $all_tasks_completed = FALSE; - } - - } - $return['tasks_complete'] = $all_tasks_completed; - drupal_json($return); - exit; -} - -/** - * Helper for adding reloading feature to form - */ -function devshop_form_reloader(&$form, $type = 'platform'){ - // Add JS that reloads the page when tasks finish. - $form['item'] = array( - '#type' => 'item', - '#value' => '', - '#weight' => 10 - ); - $settings['devshopReload'] = array( - 'type' => $type, - 'delay' => 1000, - ); - - drupal_add_js($settings, 'setting'); - drupal_add_js(drupal_get_path('module','devshop_projects') . '/inc/reload.js'); -} - -/** - * Functionality for add a new environment. - */ -function devshop_projects_create_wizard_add_new_environment($form, &$form_state) { - - // Load project node - $project_node = node_load($form_state['values']['nid']); - $environments = $form_state['values']['project']['environments']; - unset($environments['NEW']); - - // Set environments and no_verify - $project_node->project->environments = $environments; - $project_node->no_verify = TRUE; - node_save($project_node); - - // Go back to the same page. - drupal_goto('hosting/projects/add/environments'); -} diff --git a/devshop_projects/inc/create/create.inc b/devshop_projects/inc/create/create.inc new file mode 100644 index 000000000..8ad3ef068 --- /dev/null +++ b/devshop_projects/inc/create/create.inc @@ -0,0 +1,954 @@ + NULL, + ); + + // Skip step 2 if needed + $skip = variable_get('devshop_projects_skip_settings', TRUE); + if ($skip) { + $form_info['order'] = array( + 'git_url' => t('Step 1: Source'), + 'environments' => t('Step 2: Environments'), + 'sites' => t('Step 3: Install Profile'), + ); + } + + // Setup project + $project = ctools_object_cache_get('project', NULL); + + // Setup Step. + if ($step == NULL){ + drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); + } + + // Create default project object + if (empty($project)){ + // set form to first step -- we have no data + $step = current(array_keys($form_info['order'])); + $project = new stdClass(); + $project->step = $step; + $project->git_url = ''; + $project->project_nid = NULL; + $project->title = ''; + $project->environments = array('NEW' => array()); + + // ** set the storage object so its ready for whatever comes next + ctools_object_cache_set('project', $form_state['cache name'], $project); + } else { + // Quickly save the current step + $project->step = $step; + ctools_object_cache_set('project', $form_state['cache name'], $project); + } + + // Check verification status + // @TODO: We should be able to get the error messages... + $project_node = node_load($project->project_nid); + if (!empty($project_node->nid)){ + $tasks = hosting_task_fetch_tasks($project_node->nid); + } + if (isset($tasks['verify']['nid'])){ + + } + + // Get "verify" task status for the project + $project->verify_task_status = isset($tasks['verify']['task_status'])? $tasks['verify']['task_status']: HOSTING_TASK_ERROR; + $project->verify_task_nid = $tasks['verify']['nid']; + + // If project verification failed, we need to ask for a new git url. + if ($project->verify_task_status == HOSTING_TASK_ERROR && !empty($project_node->nid)){ + $project->verify_error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $project->verify_task_nid, 'error')); + // If not on the first step, go to it. + if ($step != current(array_keys($form_info['order']))){ + drupal_goto('hosting/projects/add/' . current(array_keys($form_info['order']))); + } + } else { + $project->verify_error = NULL; + } + + // All forms can access $form_state['project']; + $form_state['project'] = $project; + + // Saving the last visited step for redirects from node + $_SESSION['last_step'] = $step; + + // Generate our ctools form and output + $output = ctools_wizard_multistep_form($form_info, $step, $form_state); + return $output; +} + +/** + * The form_info for the ctools wizard + */ +function devshop_projects_create_wizard_info(){ + return array( + 'id' => 'devshop_project_create', + 'path' => "hosting/projects/add/%step", + 'show trail' => TRUE, + 'show back' => TRUE, + 'show cancel' => TRUE, + 'show return' => FALSE, + 'next text' => 'Next', + 'next callback' => 'devshop_projects_create_wizard_next', + 'finish callback' => 'devshop_projects_create_wizard_finish', + 'cancel callback' => 'devshop_projects_create_wizard_cancel', + 'order' => array( + 'git_url' => t('Step 1: Source'), + 'settings' => t('Step 2: Settings'), + 'environments' => t('Step 3: Environments'), + 'sites' => t('Step 4: Install Profile'), + ), + 'forms' => array( + 'git_url' => array( + 'form id' => 'devshop_project_create_step_git' + ), + 'settings' => array( + 'form id' => 'devshop_project_create_step_settings' + ), + 'environments' => array( + 'form id' => 'devshop_project_create_step_environments' + ), + 'sites' => array( + 'form id' => 'devshop_project_create_step_sites' + ), + ), + ); +} + +/** + * WIZARD TOOLS + */ + + +/** + * NEXT callback + * Saves anything in $form_state['project'] to ctools cache. + * + * The form submit callbacks are responsible for putting data into + * $form_state['project']. + */ +function devshop_projects_create_wizard_next(&$form_state) { + $project = &$form_state['project']; + $cache = ctools_object_cache_set('project', $form_state['cache name'], $project); +} + + + + +/** + * CANCEL callback + * Callback generated when the 'cancel' button is clicked. + * Remove the project data cache and send back to projects page. + */ +function devshop_projects_create_wizard_cancel(&$form_state) { + // Update the cache with changes. + $project = &$form_state['project']; + ctools_object_cache_clear('project', $form_state['cache name']); + + // Redirect to projects list + $form_state['redirect'] = 'hosting/projects'; + + // If we have a project node, create a "delete" hosting task + if (!empty($project->project_nid)){ + hosting_add_task($project->project_nid, 'delete'); + } + + // Removing last step session variable. + unset($_SESSION['last_step']); + +} + +/** + * WIZARD STEPS + */ + + +/********** + * STEP 1 + * Git URL + **********/ + + +/** + * STEP 1: Form + * + * Just get the Git URL. If we can't access the code, all else should fail. + */ +function devshop_project_create_step_git(&$form, &$form_state) { + $project = &$form_state['project']; + $project_node = node_load($project->project_nid); + + if ($project->verify_error){ + $form['note'] = array( + '#value' => '
' . $project->verify_error . '
', + '#type' => 'markup', + ); + + // Display some help for certain errors + if ($project->verify_error == '[DEVSHOP] Error retrieving remote information: Host key verification failed. fatal: The remote end hung up unexpectedly'){ + $form['note']['#value'] .= '
' . t('You might have to authorize the host your are trying to connect to first. Add "StrictHostKeyChecking no" to your ~/.ssh/config file to avoid this for all domains.') . '
'; + } + } + + $form['git_url'] = array( + '#type' => 'textfield', + '#required' => 1, + '#title' => t('Git URL'), + '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), + '#default_value' => $project_node->project->git_url, + ); + + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository. NOTE: You cannot change this, but you can delete and start over.Choose wisely.'), + '#size' => 40, + '#default_value' => $project_node->title, + '#maxlength' => 255, + ); + + // If there is already a title, make this an item. + if (!empty($project->title)) { + // Force title + $form['title']['#value'] = $form['title']['#default_value']; + $form['title']['#type'] = 'value'; + $form['title']['#description'] = t('You cannot change the name of your project once it has been created. If you must, click the "Cancel" button to delete this project, then create a new one.'); + + // Be nice and show it. + $form['title_display'] = $form['title']; + $form['title_display']['#type'] = 'item'; + } + + // Display helpful tips for connecting. + $pubkey = variable_get('devshop_public_key', ''); + + // If we don't yet have the server's public key saved as a variable... + if (empty($pubkey)){ + $output = t("For convenience, the server's SSH public key will be displayed here, once you run the following command on your server:"); + $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; + $output .= "
"; + } else { + // @TODO: Make this Translatable + $output = <<If you haven't granted this server access to your Git repository, you should do so now using it's public SSH key. + +HTML; + } + + // Add info about connecting to Repo + $form['connect'] = array( + '#type' => 'item', + '#title' => t('Repository Access'), + '#description' => $output, + ); +} + +/** + * STEP 1: Validate + * + * This is where we try and connect to the remote branches. + * + * As long as the SSH for www-data is setup right, this will work for private repos. + * @TODO: Figure out any security implications of giving www-data an ssh private key and telling users that + * (if they want web-based validation) they have to add www-data's SSH key to the repo as well. + * + * This is also where we get all the tags and branches. + */ +function devshop_project_create_step_git_validate(&$form, &$form_state) { + $project = &$form_state['project']; + + // No spaces or special characters allowed. + $project_name = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive + if (!_hosting_valid_fqdn($project_name)) { + form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); + } + if (!ctype_alnum($project_name)) { + form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); + } + + form_set_value($form['title'], $project_name, $form_state); + + // Check for duplicate project name here. + $node = hosting_context_load($project_name); + if ($node->nid != $project->project_nid){ + form_set_error('title', t('That name is in use. Please try again.')); + } +} + +/** + * STEP 1: Submit + */ +function devshop_project_create_step_git_submit(&$from, &$form_state) { + global $user; + + $project = &$form_state['project']; + + // If the project already exists, this means the git url has changed... + if ($project->project_nid) { + // Change the git url and save the node. Verification SHOULD run again. + $node = node_load($project->project_nid); + $node->project->git_url = $form_state['values']['git_url']; + node_save($node); + } + // Create new project node + elseif (empty($project->project_nid)){ + // Create the project node now. We will update it with the chosen path. + // This is so we can check for branches and save the hosting_context as soon + // as possible. + $node = new stdClass; + $node->title = $form_state['values']['title']; + + + $node->type = 'project'; + $node->status = 0; + $node->uid = $user->uid; + $node->name = $user->name; + + $node->project = new stdClass(); + $node->project->git_url = $form_state['values']['git_url']; + $node->project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $form_state['values']['title']; + + // Setup project url. + $base_url = $_SERVER['SERVER_NAME']; + $server = variable_get('devshop_project_master_url', $base_url); + $node->project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => $form_state['values']['title'], '@hostname' => $server)); + $node->project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); + + // Save project node. + if ($node = node_submit($node)) { + node_save($node); + } + + // Save NID to ctools object cache. + if ($node->nid){ + $project->project_nid = $node->nid; + } + } + + //Verify if is need to skip. + $skip = variable_get('devshop_projects_skip_settings', TRUE); + if ($skip) { + $project->step = 'environments'; + } +} + +/********** + * STEP 2 + * Project Settings + *********/ + +/** + * STEP 2: Form + */ +function devshop_project_create_step_settings(&$form, &$form_state) { + $project = &$form_state['project']; + + + $form['code_path'] = array( + '#type' => 'textfield', + '#title' => t('Code path'), + '#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $project->code_path, + '#maxlength' => 255, + ); + $form['drupal_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to Drupal'), + '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), + '#size' => 40, + '#default_Value' => $project->drupal_path, + '#maxlength' => 255, + ); + $form['base_url'] = array( + '#type' => 'textfield', + '#title' => t('Primary Domain'), + '#description' => t('All sites will be under a subdomain of this domain.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $project->base_url, + '#maxlength' => 255, + ); +} + +/** + * STEP 2: Validate + */ +function devshop_project_create_step_settings_validate(&$from, &$form_state) { + $project = &$form_state['project']; + + // Code path and domain must be unique + if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND code_path = "%s";', $form_state['values']['code_path']))){ + form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); + } + if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND base_url = "%s";', $form_state['values']['base_url']))){ + form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); + } + +} + +/** + * STEP 2: Submit + */ +function devshop_project_create_step_settings_submit(&$from, &$form_state) { + $project = &$form_state['project']; + + // Save code path and base url. + $project_node = node_load($project->project_nid); + $project_node->project->code_path = $form_state['values']['code_path']; + $project_node->project->drupal_path = $form_state['values']['drupal_path']; + $project_node->project->base_url = $form_state['values']['base_url']; + + $project_node->no_verify; + node_save($project_node); +} + + +/********** + * STEP 3 + * Project Environments + *********/ + +/** + * STEP 3: Form + */ +function devshop_project_create_step_environments(&$form, &$form_state) { + $project_cache = &$form_state['project']; + $project_node = node_load($project_cache->project_nid); + $project = $project_node->project; + + if ($project_cache->verify_task_status == HOSTING_TASK_QUEUED || $project_cache->verify_task_status == HOSTING_TASK_PROCESSING) { + $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; + $project_cache->no_next = TRUE; + + $form['note'] = array( + '#type' => 'markup', + '#value' => $note, + ); + $form['not_ready'] = array( + '#type' => 'value', + '#value' => TRUE, + ); + // Add code to reload the page when complete. + devshop_form_reloader($form, 'project'); + return; + } + + // this JS handles the form element hiding/showing + $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; + drupal_add_js($path); + + $settings = module_invoke_all('devshop_project_settings', $project_node); + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + $form['project'] = array( + '#tree' => TRUE, + ); + $form['project']['environments'] = array( + '#theme' => 'devshop_projects_create_settings_form', + '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', + ); + + // Ensure a blank row exists (happens when using 'Back' button) + if (!is_array($project->environments['NEW'])){ + $project->environments['NEW'] = array(); + } + foreach ($project->environments as $name => $environment) { + // No platforms exist yet + if ($name == 'NEW') { + $env_title = ''; + } else { + $env_title = $name; + } + + $form['project']['environments'][$name] = array( + '#tree' => TRUE, + '#type' => 'fieldset', + '#theme' => 'devshop_projects_settings_table', + ); + + // Environment properties + $form['project']['environments'][$name]['title'] = array( + '#type' => 'textfield', + '#title' => t('Environment Name'), + '#default_value' => $env_title, + '#size' => 6, + '#maxlength' => 64, + '#attributes' => array( + 'placeholder' => t('name'), + 'autofocus' => 'autofocus', + ), + '#field_prefix' => 'http://', + '#field_suffix' => "." . $project->base_url, + ); + $form['project']['environments'][$name]['site'] = array( + '#type' => 'value', + '#value' => $environment->site, + ); + $form['project']['environments'][$name]['platform'] = array( + '#type' => 'value', + '#value' => $environment->platform, + ); + $form['project']['environments'][$name]['git_ref'] = array( + '#title' => t('Git Branch/Tag'), + '#type' => 'select', + '#options' => devshop_projects_git_ref_options($project), + '#default_value' => $environment->git_ref, + ); + + // Environment settings + // Add environment settings form elements + foreach ($settings as $setting_id => $setting){ + $form['project']['environments'][$name]['settings'][$setting_id] = $setting; + $form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; + $form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; + } + + //Now add button. + $form['add_environment'] = array( + '#type' => 'submit', + '#value' => t('Add environment'), + '#name' => 'add_environment', + '#submit' => array('devshop_projects_create_wizard_add_new_environment'), + '#prefix' => '
', + '#suffix' => '
', + ); + } +} + + +/** + * STEP 3: Validate + */ +function devshop_project_create_step_environments_validate(&$form, &$form_state) { + $project = &$form_state['project']; + + $values = &$form_state['values']; + + // Changes NEW environment data to $title + if ($values['project']['environments']['NEW']['title']){ + $new_env = $values['project']['environments']['NEW']['title']; + $new_env_settings = $values['project']['environments']['NEW']; + $values['project']['environments'][$new_env] = $new_env_settings; + + // Create the next NEW environment. Unset makes sure its always last. + unset($values['project']['environments']['NEW']); + + // If "add environment" button clicked, add another row. + if ($form_state['clicked_button']['#name'] == 'add_environment'){ + $values['project']['environments']['NEW'] = array(); + } + } else { + unset($values['project']['environments']['NEW']); + } + + // Check environment titles + foreach ($values['project']['environments'] as $env => $env_settings) { + // Check for illegal chars + if ($env != 'NEW' && !empty($env_settings['title'])){ + if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { + $form_item = 'environments][' . $env . '][title'; + form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); + } + } + } + + // Reject if empty + if (count($values['project']['environments']) < 1){ + if ($form_state['clicked_button']['#name'] == 'add_environment'){ + form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); + } else { + form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); + } + } +} + +/** + * STEP 3: SUBMIT + */ +function devshop_project_create_step_environments_submit(&$form, &$form_state) { + // Get project and reset properties.. + $project_node = node_load($form_state['values']['nid']); + $settings = module_invoke_all('devshop_project_settings', $project_node); + + // Create these platforms, if they don't exist yet + foreach ($form_state['values']['project']['environments'] as $name => $environment) { + + // If platform exists, it's because user went back in the wizard. + $platform_nid = $project_node->project->environments[$name]->platform; + + // If the platform already exists, update settings and verify it again. + if ($platform_nid) { + // @TODO: Apply settings and re-save platform. + hosting_add_task($platform_nid, 'verify'); + } + // If platform hasn't been created yet, do so now! + else { + + $platform = new stdClass; + $platform->title = $project_node->title . '_' . $name; + + // Platform publish_path + if (!empty($project_node->project->drupal_path)) { + $platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; + } + else { + $platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; + } + + // Other attributes + $platform->web_server = $environment['web_server']; + + foreach ($settings as $setting_name => $element){ + if ($element['#node_type'] == 'platform'){ + $platform->{$setting_name} = $environment['settings'][$setting_name]; + } + } + $platform_node = _devshop_projects_node_create('platform', $platform); + } + + $environment['platform'] = $platform_node->nid; + $environment['git_ref'] = $environment['git_ref']; + $project_node->project->environments[$name] = (object) $environment; + } + + // For all removed platforms, trigger a delete task + $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); + + foreach ($removed_environments as $environment_name => $settings) { + // @TODO: Determine what to do here based on task status... + // if verify task hasn't even run yet (and has never run) we can just delete + // the platform node. + + // Create 'delete' task for the removed platform + $platform_nid = $settings['platform']; + if ($platform_nid){ + hosting_add_task($platform_nid, 'delete'); + } + } + + // Somehow, NEW environment still appears! + unset($project_node->project->environments['NEW']); + $project_node->no_verify = TRUE; + node_save($project_node); +} + +/********** + * STEP 4 + * Project Settings + *********/ + +/** + * STEP 4: Form + */ +function devshop_project_create_step_sites(&$form, &$form_state) { + $project = &$form_state['project']; + $project_node = node_load($project->project_nid); + + $profiles = array(); + $available_profiles = array(); + $completed = TRUE; + + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + + // Display the platforms + $rows = array(); + $header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); + $all_tasks_queued = TRUE; + $all_tasks_succeeded = TRUE; + + foreach ($project_node->project->environments as $name => $environment){ + + // Get platform and latest verify task. + $platform_nid = $environment->platform; + $platform = node_load($platform_nid); + $task = hosting_get_most_recent_task($platform_nid, 'verify'); + + // Build a table. + $row = array(); + $row['name'] = $name; + $row['branch'] = $environment->git_ref; + $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); + + // If platform verified successfully: + if ($task->task_status == HOSTING_TASK_SUCCESS) { + + // It's not really ready until we get a version. + if (empty($platform->release->version)){ + $completed = FALSE; + $row['version'] = '...'; + $row['profiles'] = '...'; + } + + // Collect install profiles + $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); + if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { + $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); + $row['profiles'] = implode(', ', $profiles[$name]); + } else { + $profiles[$name] = array(); + } + + // If no tasks have failed, save available profiles + if ($all_tasks_succeeded){ + if (empty($available_profiles)){ + $available_profiles = $profiles[$name]; + } else { + $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + } + } + } + // If platform verification failed: + elseif ($task->task_status == HOSTING_TASK_ERROR) { + $completed = TRUE; + $all_tasks_succeeded = FALSE; + $available_profiles = array(); + + $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); + + $row['version'] = array( + 'data' => t('Platform verification failed: %error', array('%error' => $error)), + 'colspan' => 2, + ); + + // @TODO: Get task log error message + + } + // If platform is still processing: + elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { + $completed = FALSE; + $row['version'] = '...'; + $row['profiles'] = '...'; + } + + // If a single task is not queued, $all_tasks_queued == FALSE + if ($task->task_status != HOSTING_TASK_QUEUED){ + $all_tasks_queued = FALSE; + } + + // Add hosting task status. + $row['status'] = _hosting_parse_error_code($task->task_status); + + // Store rows for display + $rows[] = $row; + } // end foreach platform + + // Output our table. + $form['platforms'] = array( + '#type' => 'markup', + '#value' => theme('table', $header, $rows), + ); + + // Not completed means show all tasks are not completed (or errored) + if (!$completed){ + $project->no_finish = TRUE; + $note = '

' . t('Please wait while we clone your repo and verify your drupal code.') . '

'; + + $form['help'] = array( + '#type' => 'markup', + '#value' => $note, + ); + + devshop_form_reloader($form, 'platform'); + return $form; + } + // If no available profiles: + elseif (count($available_profiles) == 0) { + $project->no_finish = TRUE; + $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; + + $form['error'] = array( + '#type' => 'markup', + '#value' => $note, + ); + return $form; + } else { + $project->no_finish = FALSE; + + // Install Profile + // Sensible default? + // Lets go with standard for now... we can update later. + if (isset($available_profiles['standard'])) { + $default_profile = 'standard'; + } + // If 'drupal' profile exists, it is likely drupal6! + elseif (isset($available_profiles['drupal'])) { + $default_profile = 'drupal'; + } + + $form['install_profile'] = array( + '#type' => 'radios', + '#options' => $available_profiles, + '#title' => t('Project Install Profile'), + '#required' => 1, + '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), + '#default_value' => $default_profile, + ); + } +} + +/** + * STEP 4: Validate + */ +function devshop_project_create_step_sites_validate(&$from, &$form_state) { + $project = &$form_state['project']; + + if (empty($form_state['values']['install_profile'])){ + form_set_error('install_profile', t('You must choose an install profile')); + } +} + +/** + * STEP 4: Submit + * + * Save install profile to the project object (project and site node creation + * happens on wizard finish.) + */ +function devshop_project_create_step_sites_submit(&$from, &$form_state) { + $project = &$form_state['project']; + $project->install_profile = $form_state['values']['install_profile']; +} + + +/** + * FINISH callback + * Callback generated when the add page process is finished. + */ +function devshop_projects_create_wizard_finish(&$form_state) { + + $project = &$form_state['project']; + $project_node = node_load($project->project_nid); + + // Save the extra options to the project node. + $project_node->project->install_profile = $project->install_profile; + + // Create the site nodes, saving to the environment. + // @TODO: Can we speed things up here by only running install for the first, + // then "Cloning" to create the rest? + foreach ($project_node->project->environments as $environment_name => &$environment) { + // @TODO: Does this set the http_server as well?? Doesn't look like it. + $db_server = $environment->db_server; + $site_node = devshop_projects_create_site($project_node->project, node_load($environment->platform), $environment_name, $db_server); + $environment->site = $site_node->nid; + } + + // Set to not verify and to publish. + $project_node->no_verify = TRUE; + $project_node->status = 1; + node_save($project_node); + + ctools_object_cache_clear('project', $form_state['cache name']); + $form_state['redirect'] = 'node/' . $project_node->nid; + + // Removing last step session variable. + unset($_SESSION['last_step']); + + // Friendly message + drupal_set_message(t('Your project has been created. Your sites are being installed.')); +} + +/** + * Returns JSON showing the state of the project + */ +function devshop_projects_add_status($type = 'platform'){ + $return = array(); + + // Get Project Cache + ctools_include('wizard'); + ctools_include('object-cache'); + $project = ctools_object_cache_get('project', NULL); + + + $project_node = node_load($project->project_nid); + + $all_tasks_completed = TRUE; + $nids = array(); + + // When checking project... + if ($type == 'project') { + $nids = array($project_node->nid); + } + + // When checking platforms... + if ($type == 'platform') { + foreach ($project_node->project->environments as $name => $environment){ + $nids[] = $environment->platform; + } + } + + // Check verification task for all nids + foreach ($nids as $nid){ + $task = hosting_get_most_recent_task($nid, 'verify'); + $return['tasks'][$nid] = _hosting_parse_error_code($task->task_status); + + // If task is not completed, mark all tasks not complete. + if ($task->task_status == HOSTING_TASK_SUCCESS || $task->task_status == HOSTING_TASK_ERROR) { + continue; + } else { + $all_tasks_completed = FALSE; + } + + } + $return['tasks_complete'] = $all_tasks_completed; + drupal_json($return); + exit; +} + +/** + * Helper for adding reloading feature to form + */ +function devshop_form_reloader(&$form, $type = 'platform'){ + // Add JS that reloads the page when tasks finish. + $form['item'] = array( + '#type' => 'item', + '#value' => '', + '#weight' => 10 + ); + $settings['devshopReload'] = array( + 'type' => $type, + 'delay' => 1000, + ); + + drupal_add_js($settings, 'setting'); + drupal_add_js(drupal_get_path('module','devshop_projects') . '/inc/reload.js'); +} + +/** + * Functionality for add a new environment. + */ +function devshop_projects_create_wizard_add_new_environment($form, &$form_state) { + + // Load project node + $project_node = node_load($form_state['values']['nid']); + $environments = $form_state['values']['project']['environments']; + unset($environments['NEW']); + + // Set environments and no_verify + $project_node->project->environments = $environments; + $project_node->no_verify = TRUE; + node_save($project_node); + + // Go back to the same page. + drupal_goto('hosting/projects/add/environments'); +} diff --git a/devshop_projects/inc/create/step-1.inc b/devshop_projects/inc/create/step-1.inc new file mode 100644 index 000000000..e69de29bb From 5e4b9131a56f409d3d96424e4e0e29ecd3e23946 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 4 Sep 2014 18:23:15 -0400 Subject: [PATCH 0956/3476] breaking out create form into steps. --- devshop_projects/devshop_projects.module | 6 +- devshop_projects/inc/create-wizard.inc | 2 + devshop_projects/inc/create/create.inc | 667 +---------------------- devshop_projects/inc/create/step-1.inc | 170 ++++++ devshop_projects/inc/create/step-2.inc | 73 +++ devshop_projects/inc/create/step-3.inc | 235 ++++++++ devshop_projects/inc/create/step-4.inc | 180 ++++++ 7 files changed, 668 insertions(+), 665 deletions(-) create mode 100644 devshop_projects/inc/create/step-2.inc create mode 100644 devshop_projects/inc/create/step-3.inc create mode 100644 devshop_projects/inc/create/step-4.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index c54dee8d7..804a7a0ba 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -113,7 +113,7 @@ function devshop_projects_menu() { 'access arguments' => array('create project'), 'description' => 'Start a new Drupal website project.', 'file' => 'create.inc', - 'file path' => drupal_get_path('module', 'devshop_projects') . '/create', + 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc/create', ); // Ajax endpoint for reloads @@ -121,8 +121,8 @@ function devshop_projects_menu() { 'page callback' => 'devshop_projects_add_status', 'access callback' => 'node_access', 'access arguments' => array('create', 'project'), - 'file' => 'create-wizard.inc', - 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc', + 'file' => 'create.inc', + 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc/create', ); // hosting tasks ajax pages. diff --git a/devshop_projects/inc/create-wizard.inc b/devshop_projects/inc/create-wizard.inc index e69de29bb..a4abe2daf 100644 --- a/devshop_projects/inc/create-wizard.inc +++ b/devshop_projects/inc/create-wizard.inc @@ -0,0 +1,2 @@ +project_nid); - - if ($project->verify_error){ - $form['note'] = array( - '#value' => '
' . $project->verify_error . '
', - '#type' => 'markup', - ); - - // Display some help for certain errors - if ($project->verify_error == '[DEVSHOP] Error retrieving remote information: Host key verification failed. fatal: The remote end hung up unexpectedly'){ - $form['note']['#value'] .= '
' . t('You might have to authorize the host your are trying to connect to first. Add "StrictHostKeyChecking no" to your ~/.ssh/config file to avoid this for all domains.') . '
'; - } - } - - $form['git_url'] = array( - '#type' => 'textfield', - '#required' => 1, - '#title' => t('Git URL'), - '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), - '#default_value' => $project_node->project->git_url, - ); - - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Project Code Name'), - '#required' => TRUE, - '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository. NOTE: You cannot change this, but you can delete and start over.Choose wisely.'), - '#size' => 40, - '#default_value' => $project_node->title, - '#maxlength' => 255, - ); - - // If there is already a title, make this an item. - if (!empty($project->title)) { - // Force title - $form['title']['#value'] = $form['title']['#default_value']; - $form['title']['#type'] = 'value'; - $form['title']['#description'] = t('You cannot change the name of your project once it has been created. If you must, click the "Cancel" button to delete this project, then create a new one.'); - - // Be nice and show it. - $form['title_display'] = $form['title']; - $form['title_display']['#type'] = 'item'; - } - - // Display helpful tips for connecting. - $pubkey = variable_get('devshop_public_key', ''); - - // If we don't yet have the server's public key saved as a variable... - if (empty($pubkey)){ - $output = t("For convenience, the server's SSH public key will be displayed here, once you run the following command on your server:"); - $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; - $output .= "
"; - } else { - // @TODO: Make this Translatable - $output = <<If you haven't granted this server access to your Git repository, you should do so now using it's public SSH key. - -HTML; - } - - // Add info about connecting to Repo - $form['connect'] = array( - '#type' => 'item', - '#title' => t('Repository Access'), - '#description' => $output, - ); -} - -/** - * STEP 1: Validate - * - * This is where we try and connect to the remote branches. - * - * As long as the SSH for www-data is setup right, this will work for private repos. - * @TODO: Figure out any security implications of giving www-data an ssh private key and telling users that - * (if they want web-based validation) they have to add www-data's SSH key to the repo as well. - * - * This is also where we get all the tags and branches. - */ -function devshop_project_create_step_git_validate(&$form, &$form_state) { - $project = &$form_state['project']; - - // No spaces or special characters allowed. - $project_name = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive - if (!_hosting_valid_fqdn($project_name)) { - form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); - } - if (!ctype_alnum($project_name)) { - form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); - } - - form_set_value($form['title'], $project_name, $form_state); - - // Check for duplicate project name here. - $node = hosting_context_load($project_name); - if ($node->nid != $project->project_nid){ - form_set_error('title', t('That name is in use. Please try again.')); - } -} - -/** - * STEP 1: Submit - */ -function devshop_project_create_step_git_submit(&$from, &$form_state) { - global $user; - - $project = &$form_state['project']; - - // If the project already exists, this means the git url has changed... - if ($project->project_nid) { - // Change the git url and save the node. Verification SHOULD run again. - $node = node_load($project->project_nid); - $node->project->git_url = $form_state['values']['git_url']; - node_save($node); - } - // Create new project node - elseif (empty($project->project_nid)){ - // Create the project node now. We will update it with the chosen path. - // This is so we can check for branches and save the hosting_context as soon - // as possible. - $node = new stdClass; - $node->title = $form_state['values']['title']; - - - $node->type = 'project'; - $node->status = 0; - $node->uid = $user->uid; - $node->name = $user->name; - - $node->project = new stdClass(); - $node->project->git_url = $form_state['values']['git_url']; - $node->project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $form_state['values']['title']; - - // Setup project url. - $base_url = $_SERVER['SERVER_NAME']; - $server = variable_get('devshop_project_master_url', $base_url); - $node->project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => $form_state['values']['title'], '@hostname' => $server)); - $node->project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); - - // Save project node. - if ($node = node_submit($node)) { - node_save($node); - } - - // Save NID to ctools object cache. - if ($node->nid){ - $project->project_nid = $node->nid; - } - } - - //Verify if is need to skip. - $skip = variable_get('devshop_projects_skip_settings', TRUE); - if ($skip) { - $project->step = 'environments'; - } -} - -/********** - * STEP 2 - * Project Settings - *********/ - -/** - * STEP 2: Form - */ -function devshop_project_create_step_settings(&$form, &$form_state) { - $project = &$form_state['project']; - - - $form['code_path'] = array( - '#type' => 'textfield', - '#title' => t('Code path'), - '#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $project->code_path, - '#maxlength' => 255, - ); - $form['drupal_path'] = array( - '#type' => 'textfield', - '#title' => t('Path to Drupal'), - '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), - '#size' => 40, - '#default_Value' => $project->drupal_path, - '#maxlength' => 255, - ); - $form['base_url'] = array( - '#type' => 'textfield', - '#title' => t('Primary Domain'), - '#description' => t('All sites will be under a subdomain of this domain.'), - '#required' => TRUE, - '#size' => 40, - '#default_value' => $project->base_url, - '#maxlength' => 255, - ); -} - -/** - * STEP 2: Validate - */ -function devshop_project_create_step_settings_validate(&$from, &$form_state) { - $project = &$form_state['project']; - - // Code path and domain must be unique - if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND code_path = "%s";', $form_state['values']['code_path']))){ - form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); - } - if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND base_url = "%s";', $form_state['values']['base_url']))){ - form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); - } - -} - -/** - * STEP 2: Submit - */ -function devshop_project_create_step_settings_submit(&$from, &$form_state) { - $project = &$form_state['project']; - - // Save code path and base url. - $project_node = node_load($project->project_nid); - $project_node->project->code_path = $form_state['values']['code_path']; - $project_node->project->drupal_path = $form_state['values']['drupal_path']; - $project_node->project->base_url = $form_state['values']['base_url']; - - $project_node->no_verify; - node_save($project_node); -} - - -/********** - * STEP 3 - * Project Environments - *********/ - -/** - * STEP 3: Form - */ -function devshop_project_create_step_environments(&$form, &$form_state) { - $project_cache = &$form_state['project']; - $project_node = node_load($project_cache->project_nid); - $project = $project_node->project; - - if ($project_cache->verify_task_status == HOSTING_TASK_QUEUED || $project_cache->verify_task_status == HOSTING_TASK_PROCESSING) { - $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; - $project_cache->no_next = TRUE; - - $form['note'] = array( - '#type' => 'markup', - '#value' => $note, - ); - $form['not_ready'] = array( - '#type' => 'value', - '#value' => TRUE, - ); - // Add code to reload the page when complete. - devshop_form_reloader($form, 'project'); - return; - } - - // this JS handles the form element hiding/showing - $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; - drupal_add_js($path); - - $settings = module_invoke_all('devshop_project_settings', $project_node); - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - $form['project'] = array( - '#tree' => TRUE, - ); - $form['project']['environments'] = array( - '#theme' => 'devshop_projects_create_settings_form', - '#tree' => TRUE, - '#prefix' => '
', - '#suffix' => '
', - ); - - // Ensure a blank row exists (happens when using 'Back' button) - if (!is_array($project->environments['NEW'])){ - $project->environments['NEW'] = array(); - } - foreach ($project->environments as $name => $environment) { - // No platforms exist yet - if ($name == 'NEW') { - $env_title = ''; - } else { - $env_title = $name; - } - - $form['project']['environments'][$name] = array( - '#tree' => TRUE, - '#type' => 'fieldset', - '#theme' => 'devshop_projects_settings_table', - ); - - // Environment properties - $form['project']['environments'][$name]['title'] = array( - '#type' => 'textfield', - '#title' => t('Environment Name'), - '#default_value' => $env_title, - '#size' => 6, - '#maxlength' => 64, - '#attributes' => array( - 'placeholder' => t('name'), - 'autofocus' => 'autofocus', - ), - '#field_prefix' => 'http://', - '#field_suffix' => "." . $project->base_url, - ); - $form['project']['environments'][$name]['site'] = array( - '#type' => 'value', - '#value' => $environment->site, - ); - $form['project']['environments'][$name]['platform'] = array( - '#type' => 'value', - '#value' => $environment->platform, - ); - $form['project']['environments'][$name]['git_ref'] = array( - '#title' => t('Git Branch/Tag'), - '#type' => 'select', - '#options' => devshop_projects_git_ref_options($project), - '#default_value' => $environment->git_ref, - ); - - // Environment settings - // Add environment settings form elements - foreach ($settings as $setting_id => $setting){ - $form['project']['environments'][$name]['settings'][$setting_id] = $setting; - $form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; - $form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; - $form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; - } - - //Now add button. - $form['add_environment'] = array( - '#type' => 'submit', - '#value' => t('Add environment'), - '#name' => 'add_environment', - '#submit' => array('devshop_projects_create_wizard_add_new_environment'), - '#prefix' => '
', - '#suffix' => '
', - ); - } -} - - -/** - * STEP 3: Validate - */ -function devshop_project_create_step_environments_validate(&$form, &$form_state) { - $project = &$form_state['project']; - - $values = &$form_state['values']; - - // Changes NEW environment data to $title - if ($values['project']['environments']['NEW']['title']){ - $new_env = $values['project']['environments']['NEW']['title']; - $new_env_settings = $values['project']['environments']['NEW']; - $values['project']['environments'][$new_env] = $new_env_settings; - - // Create the next NEW environment. Unset makes sure its always last. - unset($values['project']['environments']['NEW']); - - // If "add environment" button clicked, add another row. - if ($form_state['clicked_button']['#name'] == 'add_environment'){ - $values['project']['environments']['NEW'] = array(); - } - } else { - unset($values['project']['environments']['NEW']); - } - - // Check environment titles - foreach ($values['project']['environments'] as $env => $env_settings) { - // Check for illegal chars - if ($env != 'NEW' && !empty($env_settings['title'])){ - if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { - $form_item = 'environments][' . $env . '][title'; - form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); - } - } - } - - // Reject if empty - if (count($values['project']['environments']) < 1){ - if ($form_state['clicked_button']['#name'] == 'add_environment'){ - form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); - } else { - form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); - } - } -} - -/** - * STEP 3: SUBMIT - */ -function devshop_project_create_step_environments_submit(&$form, &$form_state) { - // Get project and reset properties.. - $project_node = node_load($form_state['values']['nid']); - $settings = module_invoke_all('devshop_project_settings', $project_node); - - // Create these platforms, if they don't exist yet - foreach ($form_state['values']['project']['environments'] as $name => $environment) { - - // If platform exists, it's because user went back in the wizard. - $platform_nid = $project_node->project->environments[$name]->platform; - - // If the platform already exists, update settings and verify it again. - if ($platform_nid) { - // @TODO: Apply settings and re-save platform. - hosting_add_task($platform_nid, 'verify'); - } - // If platform hasn't been created yet, do so now! - else { - - $platform = new stdClass; - $platform->title = $project_node->title . '_' . $name; - - // Platform publish_path - if (!empty($project_node->project->drupal_path)) { - $platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; - } - else { - $platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; - } - - // Other attributes - $platform->web_server = $environment['web_server']; - - foreach ($settings as $setting_name => $element){ - if ($element['#node_type'] == 'platform'){ - $platform->{$setting_name} = $environment['settings'][$setting_name]; - } - } - $platform_node = _devshop_projects_node_create('platform', $platform); - } - - $environment['platform'] = $platform_node->nid; - $environment['git_ref'] = $environment['git_ref']; - $project_node->project->environments[$name] = (object) $environment; - } - - // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); - - foreach ($removed_environments as $environment_name => $settings) { - // @TODO: Determine what to do here based on task status... - // if verify task hasn't even run yet (and has never run) we can just delete - // the platform node. - - // Create 'delete' task for the removed platform - $platform_nid = $settings['platform']; - if ($platform_nid){ - hosting_add_task($platform_nid, 'delete'); - } - } - - // Somehow, NEW environment still appears! - unset($project_node->project->environments['NEW']); - $project_node->no_verify = TRUE; - node_save($project_node); -} - -/********** - * STEP 4 - * Project Settings - *********/ - -/** - * STEP 4: Form - */ -function devshop_project_create_step_sites(&$form, &$form_state) { - $project = &$form_state['project']; - $project_node = node_load($project->project_nid); - - $profiles = array(); - $available_profiles = array(); - $completed = TRUE; - - $form['nid'] = array( - '#type' => 'value', - '#value' => $project_node->nid, - ); - - // Display the platforms - $rows = array(); - $header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); - $all_tasks_queued = TRUE; - $all_tasks_succeeded = TRUE; - - foreach ($project_node->project->environments as $name => $environment){ - - // Get platform and latest verify task. - $platform_nid = $environment->platform; - $platform = node_load($platform_nid); - $task = hosting_get_most_recent_task($platform_nid, 'verify'); - - // Build a table. - $row = array(); - $row['name'] = $name; - $row['branch'] = $environment->git_ref; - $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); - - // If platform verified successfully: - if ($task->task_status == HOSTING_TASK_SUCCESS) { - - // It's not really ready until we get a version. - if (empty($platform->release->version)){ - $completed = FALSE; - $row['version'] = '...'; - $row['profiles'] = '...'; - } - - // Collect install profiles - $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); - if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { - $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); - $row['profiles'] = implode(', ', $profiles[$name]); - } else { - $profiles[$name] = array(); - } - - // If no tasks have failed, save available profiles - if ($all_tasks_succeeded){ - if (empty($available_profiles)){ - $available_profiles = $profiles[$name]; - } else { - $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); - } - } - } - // If platform verification failed: - elseif ($task->task_status == HOSTING_TASK_ERROR) { - $completed = TRUE; - $all_tasks_succeeded = FALSE; - $available_profiles = array(); - - $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); - - $row['version'] = array( - 'data' => t('Platform verification failed: %error', array('%error' => $error)), - 'colspan' => 2, - ); - - // @TODO: Get task log error message - - } - // If platform is still processing: - elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { - $completed = FALSE; - $row['version'] = '...'; - $row['profiles'] = '...'; - } - - // If a single task is not queued, $all_tasks_queued == FALSE - if ($task->task_status != HOSTING_TASK_QUEUED){ - $all_tasks_queued = FALSE; - } - - // Add hosting task status. - $row['status'] = _hosting_parse_error_code($task->task_status); - - // Store rows for display - $rows[] = $row; - } // end foreach platform - - // Output our table. - $form['platforms'] = array( - '#type' => 'markup', - '#value' => theme('table', $header, $rows), - ); - - // Not completed means show all tasks are not completed (or errored) - if (!$completed){ - $project->no_finish = TRUE; - $note = '

' . t('Please wait while we clone your repo and verify your drupal code.') . '

'; - - $form['help'] = array( - '#type' => 'markup', - '#value' => $note, - ); - - devshop_form_reloader($form, 'platform'); - return $form; - } - // If no available profiles: - elseif (count($available_profiles) == 0) { - $project->no_finish = TRUE; - $note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; - - $form['error'] = array( - '#type' => 'markup', - '#value' => $note, - ); - return $form; - } else { - $project->no_finish = FALSE; - - // Install Profile - // Sensible default? - // Lets go with standard for now... we can update later. - if (isset($available_profiles['standard'])) { - $default_profile = 'standard'; - } - // If 'drupal' profile exists, it is likely drupal6! - elseif (isset($available_profiles['drupal'])) { - $default_profile = 'drupal'; - } - - $form['install_profile'] = array( - '#type' => 'radios', - '#options' => $available_profiles, - '#title' => t('Project Install Profile'), - '#required' => 1, - '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), - '#default_value' => $default_profile, - ); - } -} - -/** - * STEP 4: Validate - */ -function devshop_project_create_step_sites_validate(&$from, &$form_state) { - $project = &$form_state['project']; - - if (empty($form_state['values']['install_profile'])){ - form_set_error('install_profile', t('You must choose an install profile')); - } -} - -/** - * STEP 4: Submit - * - * Save install profile to the project object (project and site node creation - * happens on wizard finish.) - */ -function devshop_project_create_step_sites_submit(&$from, &$form_state) { - $project = &$form_state['project']; - $project->install_profile = $form_state['values']['install_profile']; -} - - /** * FINISH callback * Callback generated when the add page process is finished. diff --git a/devshop_projects/inc/create/step-1.inc b/devshop_projects/inc/create/step-1.inc index e69de29bb..ec0efb26c 100644 --- a/devshop_projects/inc/create/step-1.inc +++ b/devshop_projects/inc/create/step-1.inc @@ -0,0 +1,170 @@ +project_nid); + + if ($project->verify_error){ + $form['note'] = array( + '#value' => '
' . $project->verify_error . '
', + '#type' => 'markup', + ); + + // Display some help for certain errors + if ($project->verify_error == '[DEVSHOP] Error retrieving remote information: Host key verification failed. fatal: The remote end hung up unexpectedly'){ + $form['note']['#value'] .= '
' . t('You might have to authorize the host your are trying to connect to first. Add "StrictHostKeyChecking no" to your ~/.ssh/config file to avoid this for all domains.') . '
'; + } + } + + $form['git_url'] = array( + '#type' => 'textfield', + '#required' => 1, + '#title' => t('Git URL'), + '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), + '#default_value' => $project_node->project->git_url, + ); + + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Project Code Name'), + '#required' => TRUE, + '#description' => t('Choose a unique name for your project. For consistency, its a good idea to use the name of your git repository. NOTE: You cannot change this, but you can delete and start over.Choose wisely.'), + '#size' => 40, + '#default_value' => $project_node->title, + '#maxlength' => 255, + ); + + // If there is already a title, make this an item. + if (!empty($project->title)) { + // Force title + $form['title']['#value'] = $form['title']['#default_value']; + $form['title']['#type'] = 'value'; + $form['title']['#description'] = t('You cannot change the name of your project once it has been created. If you must, click the "Cancel" button to delete this project, then create a new one.'); + + // Be nice and show it. + $form['title_display'] = $form['title']; + $form['title_display']['#type'] = 'item'; + } + + // Display helpful tips for connecting. + $pubkey = variable_get('devshop_public_key', ''); + + // If we don't yet have the server's public key saved as a variable... + if (empty($pubkey)){ + $output = t("For convenience, the server's SSH public key will be displayed here, once you run the following command on your server:"); + $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes'; + $output .= "
"; + } else { + // @TODO: Make this Translatable + $output = <<If you haven't granted this server access to your Git repository, you should do so now using it's public SSH key. + +HTML; + } + + // Add info about connecting to Repo + $form['connect'] = array( + '#type' => 'item', + '#title' => t('Repository Access'), + '#description' => $output, + ); +} + +/** + * STEP 1: Validate + * + * This is where we try and connect to the remote branches. + * + * As long as the SSH for www-data is setup right, this will work for private repos. + * @TODO: Figure out any security implications of giving www-data an ssh private key and telling users that + * (if they want web-based validation) they have to add www-data's SSH key to the repo as well. + * + * This is also where we get all the tags and branches. + */ +function devshop_project_create_step_git_validate(&$form, &$form_state) { + $project = &$form_state['project']; + + // No spaces or special characters allowed. + $project_name = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive + if (!_hosting_valid_fqdn($project_name)) { + form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); + } + if (!ctype_alnum($project_name)) { + form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); + } + + form_set_value($form['title'], $project_name, $form_state); + + // Check for duplicate project name here. + $node = hosting_context_load($project_name); + if ($node->nid != $project->project_nid){ + form_set_error('title', t('That name is in use. Please try again.')); + } +} + +/** + * STEP 1: Submit + */ +function devshop_project_create_step_git_submit(&$from, &$form_state) { + global $user; + + $project = &$form_state['project']; + + // If the project already exists, this means the git url has changed... + if ($project->project_nid) { + // Change the git url and save the node. Verification SHOULD run again. + $node = node_load($project->project_nid); + $node->project->git_url = $form_state['values']['git_url']; + node_save($node); + } + // Create new project node + elseif (empty($project->project_nid)){ + // Create the project node now. We will update it with the chosen path. + // This is so we can check for branches and save the hosting_context as soon + // as possible. + $node = new stdClass; + $node->title = $form_state['values']['title']; + + + $node->type = 'project'; + $node->status = 0; + $node->uid = $user->uid; + $node->name = $user->name; + + $node->project = new stdClass(); + $node->project->git_url = $form_state['values']['git_url']; + $node->project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $form_state['values']['title']; + + // Setup project url. + $base_url = $_SERVER['SERVER_NAME']; + $server = variable_get('devshop_project_master_url', $base_url); + $node->project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => $form_state['values']['title'], '@hostname' => $server)); + $node->project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); + + // Save project node. + if ($node = node_submit($node)) { + node_save($node); + } + + // Save NID to ctools object cache. + if ($node->nid){ + $project->project_nid = $node->nid; + } + } + + //Verify if is need to skip. + $skip = variable_get('devshop_projects_skip_settings', TRUE); + if ($skip) { + $project->step = 'environments'; + } +} diff --git a/devshop_projects/inc/create/step-2.inc b/devshop_projects/inc/create/step-2.inc new file mode 100644 index 000000000..c36de8730 --- /dev/null +++ b/devshop_projects/inc/create/step-2.inc @@ -0,0 +1,73 @@ + 'textfield', +'#title' => t('Code path'), +'#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project.'), +'#required' => TRUE, +'#size' => 40, +'#default_value' => $project->code_path, +'#maxlength' => 255, +); +$form['drupal_path'] = array( +'#type' => 'textfield', +'#title' => t('Path to Drupal'), +'#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), +'#size' => 40, +'#default_Value' => $project->drupal_path, +'#maxlength' => 255, +); +$form['base_url'] = array( +'#type' => 'textfield', +'#title' => t('Primary Domain'), +'#description' => t('All sites will be under a subdomain of this domain.'), +'#required' => TRUE, +'#size' => 40, +'#default_value' => $project->base_url, +'#maxlength' => 255, +); +} + +/** +* STEP 2: Validate +*/ +function devshop_project_create_step_settings_validate(&$from, &$form_state) { +$project = &$form_state['project']; + +// Code path and domain must be unique +if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND code_path = "%s";', $form_state['values']['code_path']))){ +form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); +} +if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND base_url = "%s";', $form_state['values']['base_url']))){ +form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); +} + +} + +/** +* STEP 2: Submit +*/ +function devshop_project_create_step_settings_submit(&$from, &$form_state) { +$project = &$form_state['project']; + +// Save code path and base url. +$project_node = node_load($project->project_nid); +$project_node->project->code_path = $form_state['values']['code_path']; +$project_node->project->drupal_path = $form_state['values']['drupal_path']; +$project_node->project->base_url = $form_state['values']['base_url']; + +$project_node->no_verify; +node_save($project_node); +} diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc new file mode 100644 index 000000000..46f289cde --- /dev/null +++ b/devshop_projects/inc/create/step-3.inc @@ -0,0 +1,235 @@ +project_nid); +$project = $project_node->project; + +if ($project_cache->verify_task_status == HOSTING_TASK_QUEUED || $project_cache->verify_task_status == HOSTING_TASK_PROCESSING) { +$note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; +$project_cache->no_next = TRUE; + +$form['note'] = array( +'#type' => 'markup', +'#value' => $note, +); +$form['not_ready'] = array( +'#type' => 'value', +'#value' => TRUE, +); +// Add code to reload the page when complete. +devshop_form_reloader($form, 'project'); +return; +} + +// this JS handles the form element hiding/showing +$path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; +drupal_add_js($path); + +$settings = module_invoke_all('devshop_project_settings', $project_node); +$form['nid'] = array( +'#type' => 'value', +'#value' => $project_node->nid, +); +$form['project'] = array( +'#tree' => TRUE, +); +$form['project']['environments'] = array( +'#theme' => 'devshop_projects_create_settings_form', +'#tree' => TRUE, +'#prefix' => '
', + '#suffix' => '
', +); + +// Ensure a blank row exists (happens when using 'Back' button) +if (!is_array($project->environments['NEW'])){ +$project->environments['NEW'] = array(); +} +foreach ($project->environments as $name => $environment) { +// No platforms exist yet +if ($name == 'NEW') { +$env_title = ''; +} else { +$env_title = $name; +} + +$form['project']['environments'][$name] = array( +'#tree' => TRUE, +'#type' => 'fieldset', +'#theme' => 'devshop_projects_settings_table', +); + +// Environment properties +$form['project']['environments'][$name]['title'] = array( +'#type' => 'textfield', +'#title' => t('Environment Name'), +'#default_value' => $env_title, +'#size' => 6, +'#maxlength' => 64, +'#attributes' => array( +'placeholder' => t('name'), +'autofocus' => 'autofocus', +), +'#field_prefix' => 'http://', +'#field_suffix' => "." . $project->base_url, +); +$form['project']['environments'][$name]['site'] = array( +'#type' => 'value', +'#value' => $environment->site, +); +$form['project']['environments'][$name]['platform'] = array( +'#type' => 'value', +'#value' => $environment->platform, +); +$form['project']['environments'][$name]['git_ref'] = array( +'#title' => t('Git Branch/Tag'), +'#type' => 'select', +'#options' => devshop_projects_git_ref_options($project), +'#default_value' => $environment->git_ref, +); + +// Environment settings +// Add environment settings form elements +foreach ($settings as $setting_id => $setting){ +$form['project']['environments'][$name]['settings'][$setting_id] = $setting; +$form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; +$form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; +$form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; +} + +//Now add button. +$form['add_environment'] = array( +'#type' => 'submit', +'#value' => t('Add environment'), +'#name' => 'add_environment', +'#submit' => array('devshop_projects_create_wizard_add_new_environment'), +'#prefix' => '
', + '#suffix' => '
', +); +} +} + + +/** +* STEP 3: Validate +*/ +function devshop_project_create_step_environments_validate(&$form, &$form_state) { +$project = &$form_state['project']; + +$values = &$form_state['values']; + +// Changes NEW environment data to $title +if ($values['project']['environments']['NEW']['title']){ +$new_env = $values['project']['environments']['NEW']['title']; +$new_env_settings = $values['project']['environments']['NEW']; +$values['project']['environments'][$new_env] = $new_env_settings; + +// Create the next NEW environment. Unset makes sure its always last. +unset($values['project']['environments']['NEW']); + +// If "add environment" button clicked, add another row. +if ($form_state['clicked_button']['#name'] == 'add_environment'){ +$values['project']['environments']['NEW'] = array(); +} +} else { +unset($values['project']['environments']['NEW']); +} + +// Check environment titles +foreach ($values['project']['environments'] as $env => $env_settings) { +// Check for illegal chars +if ($env != 'NEW' && !empty($env_settings['title'])){ +if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { +$form_item = 'environments][' . $env . '][title'; +form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); +} +} +} + +// Reject if empty +if (count($values['project']['environments']) < 1){ +if ($form_state['clicked_button']['#name'] == 'add_environment'){ +form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); +} else { +form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); +} +} +} + +/** +* STEP 3: SUBMIT +*/ +function devshop_project_create_step_environments_submit(&$form, &$form_state) { +// Get project and reset properties.. +$project_node = node_load($form_state['values']['nid']); +$settings = module_invoke_all('devshop_project_settings', $project_node); + +// Create these platforms, if they don't exist yet +foreach ($form_state['values']['project']['environments'] as $name => $environment) { + +// If platform exists, it's because user went back in the wizard. +$platform_nid = $project_node->project->environments[$name]->platform; + +// If the platform already exists, update settings and verify it again. +if ($platform_nid) { +// @TODO: Apply settings and re-save platform. +hosting_add_task($platform_nid, 'verify'); +} +// If platform hasn't been created yet, do so now! +else { + +$platform = new stdClass; +$platform->title = $project_node->title . '_' . $name; + +// Platform publish_path +if (!empty($project_node->project->drupal_path)) { +$platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; +} +else { +$platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; +} + +// Other attributes +$platform->web_server = $environment['web_server']; + +foreach ($settings as $setting_name => $element){ +if ($element['#node_type'] == 'platform'){ +$platform->{$setting_name} = $environment['settings'][$setting_name]; +} +} +$platform_node = _devshop_projects_node_create('platform', $platform); +} + +$environment['platform'] = $platform_node->nid; +$environment['git_ref'] = $environment['git_ref']; +$project_node->project->environments[$name] = (object) $environment; +} + +// For all removed platforms, trigger a delete task +$removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); + +foreach ($removed_environments as $environment_name => $settings) { +// @TODO: Determine what to do here based on task status... +// if verify task hasn't even run yet (and has never run) we can just delete +// the platform node. + +// Create 'delete' task for the removed platform +$platform_nid = $settings['platform']; +if ($platform_nid){ +hosting_add_task($platform_nid, 'delete'); +} +} + +// Somehow, NEW environment still appears! +unset($project_node->project->environments['NEW']); +$project_node->no_verify = TRUE; +node_save($project_node); +} \ No newline at end of file diff --git a/devshop_projects/inc/create/step-4.inc b/devshop_projects/inc/create/step-4.inc new file mode 100644 index 000000000..0ad59fcbf --- /dev/null +++ b/devshop_projects/inc/create/step-4.inc @@ -0,0 +1,180 @@ +project_nid); + +$profiles = array(); +$available_profiles = array(); +$completed = TRUE; + +$form['nid'] = array( +'#type' => 'value', +'#value' => $project_node->nid, +); + +// Display the platforms +$rows = array(); +$header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); +$all_tasks_queued = TRUE; +$all_tasks_succeeded = TRUE; + +foreach ($project_node->project->environments as $name => $environment){ + +// Get platform and latest verify task. +$platform_nid = $environment->platform; +$platform = node_load($platform_nid); +$task = hosting_get_most_recent_task($platform_nid, 'verify'); + +// Build a table. +$row = array(); +$row['name'] = $name; +$row['branch'] = $environment->git_ref; +$row['version'] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); + +// If platform verified successfully: +if ($task->task_status == HOSTING_TASK_SUCCESS) { + +// It's not really ready until we get a version. +if (empty($platform->release->version)){ +$completed = FALSE; +$row['version'] = '...'; +$row['profiles'] = '...'; +} + +// Collect install profiles +$profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); +if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { +$profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); +$row['profiles'] = implode(', ', $profiles[$name]); +} else { +$profiles[$name] = array(); +} + +// If no tasks have failed, save available profiles +if ($all_tasks_succeeded){ +if (empty($available_profiles)){ +$available_profiles = $profiles[$name]; +} else { +$available_profiles = array_intersect_key($available_profiles, $profiles[$name]); +} +} +} +// If platform verification failed: +elseif ($task->task_status == HOSTING_TASK_ERROR) { +$completed = TRUE; +$all_tasks_succeeded = FALSE; +$available_profiles = array(); + +$error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); + +$row['version'] = array( +'data' => t('Platform verification failed: %error', array('%error' => $error)), +'colspan' => 2, +); + +// @TODO: Get task log error message + +} +// If platform is still processing: +elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { +$completed = FALSE; +$row['version'] = '...'; +$row['profiles'] = '...'; +} + +// If a single task is not queued, $all_tasks_queued == FALSE +if ($task->task_status != HOSTING_TASK_QUEUED){ +$all_tasks_queued = FALSE; +} + +// Add hosting task status. +$row['status'] = _hosting_parse_error_code($task->task_status); + +// Store rows for display +$rows[] = $row; +} // end foreach platform + +// Output our table. +$form['platforms'] = array( +'#type' => 'markup', +'#value' => theme('table', $header, $rows), +); + +// Not completed means show all tasks are not completed (or errored) +if (!$completed){ +$project->no_finish = TRUE; +$note = '

' . t('Please wait while we clone your repo and verify your drupal code.') . '

'; + +$form['help'] = array( +'#type' => 'markup', +'#value' => $note, +); + +devshop_form_reloader($form, 'platform'); +return $form; +} +// If no available profiles: +elseif (count($available_profiles) == 0) { +$project->no_finish = TRUE; +$note = '

' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

'; + +$form['error'] = array( +'#type' => 'markup', +'#value' => $note, +); +return $form; +} else { +$project->no_finish = FALSE; + +// Install Profile +// Sensible default? +// Lets go with standard for now... we can update later. +if (isset($available_profiles['standard'])) { +$default_profile = 'standard'; +} +// If 'drupal' profile exists, it is likely drupal6! +elseif (isset($available_profiles['drupal'])) { +$default_profile = 'drupal'; +} + +$form['install_profile'] = array( +'#type' => 'radios', +'#options' => $available_profiles, +'#title' => t('Project Install Profile'), +'#required' => 1, +'#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), +'#default_value' => $default_profile, +); +} +} + +/** +* STEP 4: Validate +*/ +function devshop_project_create_step_sites_validate(&$from, &$form_state) { +$project = &$form_state['project']; + +if (empty($form_state['values']['install_profile'])){ +form_set_error('install_profile', t('You must choose an install profile')); +} +} + +/** +* STEP 4: Submit +* +* Save install profile to the project object (project and site node creation +* happens on wizard finish.) +*/ +function devshop_project_create_step_sites_submit(&$from, &$form_state) { +$project = &$form_state['project']; +$project->install_profile = $form_state['values']['install_profile']; +} From a90f343e37a3b3bf62dfb81a9e6f445cffb4eab1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 4 Sep 2014 18:38:10 -0400 Subject: [PATCH 0957/3476] Making new updates 600x --- devshop_projects/devshop_projects.install | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index 6d0120a7a..f9fe5d3de 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -216,11 +216,16 @@ function devshop_projects_update_9() { return $ret; } +/** + * 6.x-1.9-beta4 -^ + * + * 6.x-1.9-rc1 -v (FUTURE RELEASE) + */ /** - * Remove live_domain from hosting_devshop_project schema and disable devshop_live if needed. + * Remove live_domain from hosting_devshop_project schema. */ -function devshop_projects_update_10() { +function devshop_projects_update_6000() { $ret = array(); if (db_column_exists('hosting_devshop_project', 'live_domain')){ $ret[] = db_drop_field($ret, 'hosting_devshop_project', 'live_domain'); @@ -231,7 +236,7 @@ function devshop_projects_update_10() { /** * Create new hosting_devshop_environment table. */ -function devshop_projects_update_11() { +function devshop_projects_update_6001() { // Create only the new table // Clone of drupal_install_schema('devshop_projects'); $module = 'devshop_projects'; @@ -247,9 +252,8 @@ function devshop_projects_update_11() { /** * Enable hosting_alias module to allow custom domains on sites. - * */ -function devshop_projects_update_12() { +function devshop_projects_update_6002() { module_enable(array('hosting_alias')); return array(); } @@ -257,7 +261,7 @@ function devshop_projects_update_12() { /** * Remove legacy `hosting_devshop_project_object` table. */ -function devshop_projects_update_13(){ +function devshop_projects_update_6003(){ $ret = array(); db_drop_table($ret, 'hosting_devshop_project_object'); return $ret; @@ -266,7 +270,7 @@ function devshop_projects_update_13(){ /** * Change "data" column to "settings" column. */ -function devshop_projects_update_14(){ +function devshop_projects_update_6004(){ $ret = array(); db_change_column($ret, 'hosting_devshop_project', 'data', 'settings', 'text', array( 'not null' => FALSE, From 7b1381a0afc145d5e7ae3ba1349969d4325f84de Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 12:56:06 -0400 Subject: [PATCH 0958/3476] Improved environment saving. --- devshop_projects/inc/nodes.inc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 47711c39b..c38c2c184 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -203,7 +203,7 @@ function devshop_projects_update($node) { // Save Environment records. if (!empty($project->environments )) { // Delete existing environment records - db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); + // db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); // Save each environment foreach ($project->environments as $name => $environment){ @@ -226,8 +226,16 @@ function devshop_projects_update($node) { unset($environment->settings['platform']); $info->settings = serialize($environment->settings); + // Detect existing environment record + if (db_result(db_query('SELECT name FROM {hosting_devshop_project_environment} WHERE project_nid = %d AND name = "%s"', $node->nid, $environment->name))) { + $update = array('project_nid', 'name'); + } + else { + $update = array(); + } + // Save environment record. - drupal_write_record('hosting_devshop_project_environment', $info); + drupal_write_record('hosting_devshop_project_environment', $info, $update); // Save aegir objects if (!empty($environment->site)){ From 6e6a0e98ba586fa4ba73653eb8e443dbda14b530 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 12:56:39 -0400 Subject: [PATCH 0959/3476] Adding a helper to devise an environment url --- devshop_projects/devshop_projects.module | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 804a7a0ba..594a33383 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -398,3 +398,25 @@ function _devshop_projects_project_has_module($node, $module) { $environment = key($node->project->environments); return _devshop_projects_site_has_module($node->project->environments[$environment]->site, $module); } + +/** + * Helper function to generate the URL for an environment. + * @param $name + * @param $environment + * @return string + */ +function devshop_projects_url($name, $environment = NULL){ + + // If we don't have a name yet, just return the server name. + if (empty($name)){ + return $_SERVER['SERVER_NAME']; + } + else { + $pattern = variable_get('devshop_project_environment_url_pattern', "@environment.@project.@hostname"); + return strtr($pattern, array( + '@environment' => $environment, + '@project' => $name, + '@hostname' => $_SERVER['SERVER_NAME'], + )); + } +} \ No newline at end of file From 21effa30a9b86561e97aa7ea986dde04fc20f63f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 12:57:02 -0400 Subject: [PATCH 0960/3476] Refactoring project creation --- devshop_projects/inc/admin.inc | 42 ++- devshop_projects/inc/create/create.inc | 17 +- devshop_projects/inc/create/create.js | 14 + devshop_projects/inc/create/step-1.inc | 115 ++++--- devshop_projects/inc/create/step-2.inc | 56 +--- devshop_projects/inc/create/step-3.inc | 416 +++++++++++++------------ 6 files changed, 347 insertions(+), 313 deletions(-) create mode 100644 devshop_projects/inc/create/create.js diff --git a/devshop_projects/inc/admin.inc b/devshop_projects/inc/admin.inc index 89b0088c4..82935be39 100644 --- a/devshop_projects/inc/admin.inc +++ b/devshop_projects/inc/admin.inc @@ -8,31 +8,47 @@ function devshop_projects_settings_form() { $form = array(); - $form['devshop_project_default_path'] = array( + $form['paths'] = array( + '#type' => 'fieldset', + '#title' => t('Paths'), + ); + $form['paths']['devshop_projects_allow_custom_code_path'] = array( + '#title' => t('Allow custom code path per project.'), + '#description' => t('Allow each project to have a custom "Code Path". If not checked, project paths are set as "/var/aegir/projects/{PROJECT_NAME}.'), + '#type' => 'checkbox', + '#default_value' => variable_get('devshop_projects_allow_custom_code_path', FALSE), + ); + $form['paths']['devshop_project_base_path'] = array( '#title' => t('Projects Base Path'), '#type' => 'textfield', - '#description' => t('The folder that all projects will be created in. Each project gets their own folder, inside that is a folder for each environment.'), + '#description' => t('The default base path that all projects will be created in. Projects each get their own folder inside this path.'), '#default_value' => variable_get('devshop_project_default_path', '/var/aegir/projects'), ); - $form['devshop_project_default_drupal_path'] = array( + $form['paths']['devshop_project_default_drupal_path'] = array( '#title' => t('Default path to Drupal'), '#type' => 'textfield', '#description' => t("If index.php isn't in the root of the git repo, you can edit the 'Path to Drupal' setting on each project. Set a default 'Path to Drupal' here. (For example, an Acquia hosted repo uses 'docroot'.)"), '#default_value' => variable_get('devshop_project_default_drupal_path', ''), ); + + + $form['urls'] = array( + '#type' => 'fieldset', + '#title' => t('URLs'), + ); + + $form['urls']['devshop_projects_allow_custom_base_url'] = array( + '#title' => t('Allow custom base URL per project.'), + '#description' => t('All environments are made available at http://{ENVIRONMENT}.BASE_URL. Check this box to make the Base URL configurable per project.'), + '#type' => 'checkbox', + '#default_value' => variable_get('devshop_projects_allow_custom_base_url', FALSE), + ); // @TODO: Add @environment as a replacement pattern. - $form['devshop_project_default_base_url_pattern'] = array( + $form['urls']['devshop_project_environment_url_pattern'] = array( '#title' => t('Domain Name Pattern'), '#type' => 'textfield', - '#description' => t('Each environment (aegir site) gets an initial URL. You can use @project for project name and @hostname for %host. Environments are put underneath this URL as subdomains.', array('%host' => $_SERVER['hostname'])), - '#default_value' => variable_get('devshop_project_default_base_url_pattern', '@project.@hostname'), + '#description' => t('Each environment (aegir site) gets an initial URL. You can use @project for project name and @hostname for "%host". Environments are put underneath this URL as subdomains.', array('%host' => $_SERVER['SERVER_NAME'])), + '#default_value' => variable_get('devshop_project_environment_url_pattern', '@environment.@project.@hostname'), ); - $form['devshop_projects_skip_settings'] = array( - '#title' => t('Block users from editing the "Code Path", and "Path to Drupal", and "Base URL" settings.'), - '#description' => t('When starting a new project, you can prevent users from altering these settings, forcing projects to use the defaults set on this page. Check this box to block these settings from being changed.'), - '#type' => 'checkbox', - '#default_value' => variable_get('devshop_projects_skip_settings', TRUE), - ); - return system_settings_form($form); } diff --git a/devshop_projects/inc/create/create.inc b/devshop_projects/inc/create/create.inc index 5f843d851..79725d6de 100644 --- a/devshop_projects/inc/create/create.inc +++ b/devshop_projects/inc/create/create.inc @@ -20,16 +20,6 @@ function devshop_projects_create_wizard($step = NULL){ 'cache name' => NULL, ); - // Skip step 2 if needed - $skip = variable_get('devshop_projects_skip_settings', TRUE); - if ($skip) { - $form_info['order'] = array( - 'git_url' => t('Step 1: Source'), - 'environments' => t('Step 2: Environments'), - 'sites' => t('Step 3: Install Profile'), - ); - } - // Setup project $project = ctools_object_cache_get('project', NULL); @@ -46,8 +36,11 @@ function devshop_projects_create_wizard($step = NULL){ $project->step = $step; $project->git_url = ''; $project->project_nid = NULL; - $project->title = ''; - $project->environments = array('NEW' => array()); + $project->name = ''; + + // These are empty until we have our project name + $project->code_path = ''; + $project->base_url = ''; // ** set the storage object so its ready for whatever comes next ctools_object_cache_set('project', $form_state['cache name'], $project); diff --git a/devshop_projects/inc/create/create.js b/devshop_projects/inc/create/create.js new file mode 100644 index 000000000..8c16d390e --- /dev/null +++ b/devshop_projects/inc/create/create.js @@ -0,0 +1,14 @@ + +Drupal.behaviors.createStep1 = function() { + $( "#edit-title" ).keyup(function(event) { + + // Extract project name and base path and URL + var projectName = $(this).val(); + var base_path = $('#edit-code-path').attr('data-base_path') + '/' + projectName; + var base_url = projectName + '.' + $('#edit-base-url').attr('data-base_url'); + + $('#edit-code-path').val(base_path); + $('#edit-base-url').val(base_url); + + }); +} \ No newline at end of file diff --git a/devshop_projects/inc/create/step-1.inc b/devshop_projects/inc/create/step-1.inc index ec0efb26c..2d0b307b5 100644 --- a/devshop_projects/inc/create/step-1.inc +++ b/devshop_projects/inc/create/step-1.inc @@ -14,6 +14,8 @@ function devshop_project_create_step_git(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); + drupal_add_js(drupal_get_path('module', 'devshop_projects') . '/inc/create/create.js'); + if ($project->verify_error){ $form['note'] = array( '#value' => '
' . $project->verify_error . '
', @@ -26,14 +28,6 @@ function devshop_project_create_step_git(&$form, &$form_state) { } } - $form['git_url'] = array( - '#type' => 'textfield', - '#required' => 1, - '#title' => t('Git URL'), - '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), - '#default_value' => $project_node->project->git_url, - ); - $form['title'] = array( '#type' => 'textfield', '#title' => t('Project Code Name'), @@ -56,6 +50,42 @@ function devshop_project_create_step_git(&$form, &$form_state) { $form['title_display']['#type'] = 'item'; } + $form['git_url'] = array( + '#type' => 'textfield', + '#required' => 1, + '#title' => t('Git URL'), + '#description' => t('Enter the Git URL for your drupal project. The root of the repo must contain Drupal\'s index.php. A clone of !link is a good place to start.', array('!link' => l('http://git.drupal.org/project/drupal.git', 'http://git.drupal.org/project/drupal.git'))), + '#default_value' => $project->git_url, + ); + + // Project code path. + $form['code_path'] = array( + '#type' => variable_get('devshop_projects_allow_custom_code_path', FALSE)? 'textfield': 'value', + '#title' => t('Code path'), + '#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project. There must not be a file or directory at this path.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $project->code_path, + '#maxlength' => 255, + '#attributes'=> array( + 'data-base_path' => variable_get('devshop_project_base_path', '/var/aegir/projects'), + ), + ); + + // Project base url + $form['base_url'] = array( + '#type' => variable_get('devshop_projects_allow_custom_base_url', FALSE)? 'textfield': 'value', + '#title' => t('Base URL'), + '#description' => t('All sites will be under a subdomain of this domain.'), + '#required' => TRUE, + '#size' => 40, + '#default_value' => $project->base_url, + '#maxlength' => 255, + '#attributes'=> array( + 'data-base_url' => devshop_projects_url($project->name, ''), + ), + ); + // Display helpful tips for connecting. $pubkey = variable_get('devshop_public_key', ''); @@ -82,34 +112,48 @@ HTML; /** * STEP 1: Validate - * - * This is where we try and connect to the remote branches. - * - * As long as the SSH for www-data is setup right, this will work for private repos. - * @TODO: Figure out any security implications of giving www-data an ssh private key and telling users that - * (if they want web-based validation) they have to add www-data's SSH key to the repo as well. - * - * This is also where we get all the tags and branches. */ function devshop_project_create_step_git_validate(&$form, &$form_state) { $project = &$form_state['project']; + // PROJECT NAME // No spaces or special characters allowed. $project_name = strtolower(trim($form_state['values']['title'])); // domain names are case-insensitive - if (!_hosting_valid_fqdn($project_name)) { - form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); - } + + // Set the value to our trimmed and lowercased proejct name. + form_set_value($form['title'], $project_name, $form_state); + + // Check for bad characters. if (!ctype_alnum($project_name)) { form_set_error('title', t("You have not specified a valid project codename. Only numbers and letters are allowed.")); } - form_set_value($form['title'], $project_name, $form_state); + // Check for duplicate project name here. $node = hosting_context_load($project_name); if ($node->nid != $project->project_nid){ form_set_error('title', t('That name is in use. Please try again.')); } + + // CODE PATH & BASE URL + // Code path and base url must be unique + if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND code_path = "%s";', $form_state['values']['code_path']))){ + form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); + } + if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND base_url = "%s";', $form_state['values']['base_url']))){ + form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); + } + + // If custom code paths are not allowed, set the value here using the project name. + if (!variable_get('devshop_projects_allow_custom_code_path', FALSE)){ + form_set_value($form['code_path'], $form_state['values']['code_path'] . '/' . $project_name, $form_state); + } + + // If custom base URLs are not allowed, set the value here using the project name. + if (!variable_get('devshop_projects_allow_custom_code_path', FALSE)){ + form_set_value($form['base_url'], $form_state['values']['base_url'] . '/' . $project_name, $form_state); + } } /** @@ -122,9 +166,11 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { // If the project already exists, this means the git url has changed... if ($project->project_nid) { - // Change the git url and save the node. Verification SHOULD run again. + // Change the git url and save the node. Verification will run again. $node = node_load($project->project_nid); $node->project->git_url = $form_state['values']['git_url']; + $node->project->code_path = $form_state['values']['code_path']; + $node->project->base_url = $form_state['values']['base_url']; node_save($node); } // Create new project node @@ -135,23 +181,24 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $node = new stdClass; $node->title = $form_state['values']['title']; - $node->type = 'project'; $node->status = 0; $node->uid = $user->uid; $node->name = $user->name; - $node->project = new stdClass(); - $node->project->git_url = $form_state['values']['git_url']; - $node->project->code_path = variable_get('devshop_projects_default_path', '/var/aegir/projects'). '/'. $form_state['values']['title']; + // Create project object + $project->name = $form_state['values']['title']; + $project->git_url = $form_state['values']['git_url']; + $project->code_path = $form_state['values']['code_path']; + $project->base_url = $form_state['values']['base_url']; + + // @TODO: We will clone the code for step 2 and look for drupal. + $project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); - // Setup project url. - $base_url = $_SERVER['SERVER_NAME']; - $server = variable_get('devshop_project_master_url', $base_url); - $node->project->base_url = strtr(variable_get('devshop_project_default_base_url_pattern', "@project.@hostname"), array('@project' => $form_state['values']['title'], '@hostname' => $server)); - $node->project->drupal_path = variable_get('devshop_projects_default_drupal_path', ''); + // Attach to node + $node->project = $project; - // Save project node. + // Save project node, triggering verification. if ($node = node_submit($node)) { node_save($node); } @@ -161,10 +208,4 @@ function devshop_project_create_step_git_submit(&$from, &$form_state) { $project->project_nid = $node->nid; } } - - //Verify if is need to skip. - $skip = variable_get('devshop_projects_skip_settings', TRUE); - if ($skip) { - $project->step = 'environments'; - } } diff --git a/devshop_projects/inc/create/step-2.inc b/devshop_projects/inc/create/step-2.inc index c36de8730..ae2e42695 100644 --- a/devshop_projects/inc/create/step-2.inc +++ b/devshop_projects/inc/create/step-2.inc @@ -11,48 +11,20 @@ function devshop_project_create_step_settings(&$form, &$form_state) { $project = &$form_state['project']; - -$form['code_path'] = array( -'#type' => 'textfield', -'#title' => t('Code path'), -'#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project.'), -'#required' => TRUE, -'#size' => 40, -'#default_value' => $project->code_path, -'#maxlength' => 255, -); -$form['drupal_path'] = array( -'#type' => 'textfield', -'#title' => t('Path to Drupal'), -'#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), -'#size' => 40, -'#default_Value' => $project->drupal_path, -'#maxlength' => 255, -); -$form['base_url'] = array( -'#type' => 'textfield', -'#title' => t('Primary Domain'), -'#description' => t('All sites will be under a subdomain of this domain.'), -'#required' => TRUE, -'#size' => 40, -'#default_value' => $project->base_url, -'#maxlength' => 255, -); + $form['drupal_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to Drupal'), + '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), + '#size' => 40, + '#default_Value' => $project->drupal_path, + '#maxlength' => 255, + ); } /** * STEP 2: Validate */ function devshop_project_create_step_settings_validate(&$from, &$form_state) { -$project = &$form_state['project']; - -// Code path and domain must be unique -if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND code_path = "%s";', $form_state['values']['code_path']))){ -form_set_error('code_path', t('Another project already has this code path. Code path must be unique.')); -} -if (db_result(db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = 1 AND base_url = "%s";', $form_state['values']['base_url']))){ -form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.')); -} } @@ -60,14 +32,6 @@ form_set_error('base_url', t('Another project already has this base url. Base U * STEP 2: Submit */ function devshop_project_create_step_settings_submit(&$from, &$form_state) { -$project = &$form_state['project']; - -// Save code path and base url. -$project_node = node_load($project->project_nid); -$project_node->project->code_path = $form_state['values']['code_path']; -$project_node->project->drupal_path = $form_state['values']['drupal_path']; -$project_node->project->base_url = $form_state['values']['base_url']; - -$project_node->no_verify; -node_save($project_node); + $project = &$form_state['project']; + $project->drupal_path = $form_state['values']['drupal_path']; } diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index 46f289cde..c1e8dd93a 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -1,235 +1,241 @@ project_nid); -$project = $project_node->project; - -if ($project_cache->verify_task_status == HOSTING_TASK_QUEUED || $project_cache->verify_task_status == HOSTING_TASK_PROCESSING) { -$note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; -$project_cache->no_next = TRUE; - -$form['note'] = array( -'#type' => 'markup', -'#value' => $note, -); -$form['not_ready'] = array( -'#type' => 'value', -'#value' => TRUE, -); -// Add code to reload the page when complete. -devshop_form_reloader($form, 'project'); -return; -} - -// this JS handles the form element hiding/showing -$path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; -drupal_add_js($path); - -$settings = module_invoke_all('devshop_project_settings', $project_node); -$form['nid'] = array( -'#type' => 'value', -'#value' => $project_node->nid, -); -$form['project'] = array( -'#tree' => TRUE, -); -$form['project']['environments'] = array( -'#theme' => 'devshop_projects_create_settings_form', -'#tree' => TRUE, -'#prefix' => '
', - '#suffix' => '
', -); - -// Ensure a blank row exists (happens when using 'Back' button) -if (!is_array($project->environments['NEW'])){ -$project->environments['NEW'] = array(); -} -foreach ($project->environments as $name => $environment) { -// No platforms exist yet -if ($name == 'NEW') { -$env_title = ''; -} else { -$env_title = $name; -} - -$form['project']['environments'][$name] = array( -'#tree' => TRUE, -'#type' => 'fieldset', -'#theme' => 'devshop_projects_settings_table', -); - -// Environment properties -$form['project']['environments'][$name]['title'] = array( -'#type' => 'textfield', -'#title' => t('Environment Name'), -'#default_value' => $env_title, -'#size' => 6, -'#maxlength' => 64, -'#attributes' => array( -'placeholder' => t('name'), -'autofocus' => 'autofocus', -), -'#field_prefix' => 'http://', -'#field_suffix' => "." . $project->base_url, -); -$form['project']['environments'][$name]['site'] = array( -'#type' => 'value', -'#value' => $environment->site, -); -$form['project']['environments'][$name]['platform'] = array( -'#type' => 'value', -'#value' => $environment->platform, -); -$form['project']['environments'][$name]['git_ref'] = array( -'#title' => t('Git Branch/Tag'), -'#type' => 'select', -'#options' => devshop_projects_git_ref_options($project), -'#default_value' => $environment->git_ref, -); - -// Environment settings -// Add environment settings form elements -foreach ($settings as $setting_id => $setting){ -$form['project']['environments'][$name]['settings'][$setting_id] = $setting; -$form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; -$form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; -$form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; -} + $project_cache = & $form_state['project']; + $project_node = node_load($project_cache->project_nid); + $project = $project_node->project; + + if ($project_cache->verify_task_status == HOSTING_TASK_QUEUED || $project_cache->verify_task_status == HOSTING_TASK_PROCESSING) { + $note = '

' . t('Please wait while we connect to your repository and determine any branches.') . '

'; + $project_cache->no_next = TRUE; + + $form['note'] = array( + '#type' => 'markup', + '#value' => $note, + ); + $form['not_ready'] = array( + '#type' => 'value', + '#value' => TRUE, + ); + + // Add code to reload the page when complete. + devshop_form_reloader($form, 'project'); + return; + } + + // this JS handles the form element hiding/showing + $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; + drupal_add_js($path); + + $settings = module_invoke_all('devshop_project_settings', $project_node); + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); + $form['project'] = array( + '#tree' => TRUE, + ); + $form['project']['environments'] = array( + '#theme' => 'devshop_projects_create_settings_form', + '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', + ); + + // Ensure a blank row exists (happens when using 'Back' button) + if (!is_array($project->environments['NEW'])) { + $project->environments['NEW'] = array(); + } + foreach ($project->environments as $name => $environment) { + // No platforms exist yet + if ($name == 'NEW') { + $env_title = ''; + } else { + $env_title = $name; + } + + $form['project']['environments'][$name] = array( + '#tree' => TRUE, + '#type' => 'fieldset', + '#theme' => 'devshop_projects_settings_table', + ); + + // Environment properties + $form['project']['environments'][$name]['title'] = array( + '#type' => 'textfield', + '#title' => t('Environment Name'), + '#default_value' => $env_title, + '#size' => 6, + '#maxlength' => 64, + '#attributes' => array( + 'placeholder' => t('name'), + 'autofocus' => 'autofocus', + ), + '#field_prefix' => 'http://', + '#field_suffix' => "." . $project->base_url, + ); + $form['project']['environments'][$name]['site'] = array( + '#type' => 'value', + '#value' => $environment->site, + ); + $form['project']['environments'][$name]['platform'] = array( + '#type' => 'value', + '#value' => $environment->platform, + ); + $form['project']['environments'][$name]['git_ref'] = array( + '#title' => t('Git Branch/Tag'), + '#type' => 'select', + '#options' => devshop_projects_git_ref_options($project), + '#default_value' => $environment->git_ref, + ); + + // Environment settings + // Add environment settings form elements + foreach ($settings as $setting_id => $setting) { + $form['project']['environments'][$name]['settings'][$setting_id] = $setting; + $form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; + $form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; + $form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; + } //Now add button. -$form['add_environment'] = array( -'#type' => 'submit', -'#value' => t('Add environment'), -'#name' => 'add_environment', -'#submit' => array('devshop_projects_create_wizard_add_new_environment'), -'#prefix' => '
', - '#suffix' => '
', -); -} + $form['add_environment'] = array( + '#type' => 'submit', + '#value' => t('Add environment'), + '#name' => 'add_environment', + '#submit' => array('devshop_projects_create_wizard_add_new_environment'), + '#prefix' => '
', + '#suffix' => '
', + ); + } } /** -* STEP 3: Validate -*/ + * STEP 3: Validate + */ function devshop_project_create_step_environments_validate(&$form, &$form_state) { -$project = &$form_state['project']; + $project = &$form_state['project']; -$values = &$form_state['values']; + $values = &$form_state['values']; // Changes NEW environment data to $title -if ($values['project']['environments']['NEW']['title']){ -$new_env = $values['project']['environments']['NEW']['title']; -$new_env_settings = $values['project']['environments']['NEW']; -$values['project']['environments'][$new_env] = $new_env_settings; + if ($values['project']['environments']['NEW']['title']) { + $new_env = $values['project']['environments']['NEW']['title']; + $new_env_settings = $values['project']['environments']['NEW']; + $values['project']['environments'][$new_env] = $new_env_settings; // Create the next NEW environment. Unset makes sure its always last. -unset($values['project']['environments']['NEW']); + unset($values['project']['environments']['NEW']); // If "add environment" button clicked, add another row. -if ($form_state['clicked_button']['#name'] == 'add_environment'){ -$values['project']['environments']['NEW'] = array(); -} -} else { -unset($values['project']['environments']['NEW']); -} + if ($form_state['clicked_button']['#name'] == 'add_environment') { + $values['project']['environments']['NEW'] = array(); + } + } else { + unset($values['project']['environments']['NEW']); + } // Check environment titles -foreach ($values['project']['environments'] as $env => $env_settings) { + foreach ($values['project']['environments'] as $env => $env_settings) { // Check for illegal chars -if ($env != 'NEW' && !empty($env_settings['title'])){ -if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { -$form_item = 'environments][' . $env . '][title'; -form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); -} -} -} + if ($env != 'NEW' && !empty($env_settings['title'])) { + if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { + $form_item = 'environments][' . $env . '][title'; + form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); + } + } + } // Reject if empty -if (count($values['project']['environments']) < 1){ -if ($form_state['clicked_button']['#name'] == 'add_environment'){ -form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); -} else { -form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); -} -} + if (count($values['project']['environments']) < 1) { + if ($form_state['clicked_button']['#name'] == 'add_environment') { + form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); + } else { + form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); + } + } } /** -* STEP 3: SUBMIT -*/ + * STEP 3: SUBMIT + */ function devshop_project_create_step_environments_submit(&$form, &$form_state) { -// Get project and reset properties.. -$project_node = node_load($form_state['values']['nid']); -$settings = module_invoke_all('devshop_project_settings', $project_node); - -// Create these platforms, if they don't exist yet -foreach ($form_state['values']['project']['environments'] as $name => $environment) { - -// If platform exists, it's because user went back in the wizard. -$platform_nid = $project_node->project->environments[$name]->platform; - -// If the platform already exists, update settings and verify it again. -if ($platform_nid) { -// @TODO: Apply settings and re-save platform. -hosting_add_task($platform_nid, 'verify'); -} -// If platform hasn't been created yet, do so now! -else { - -$platform = new stdClass; -$platform->title = $project_node->title . '_' . $name; - -// Platform publish_path -if (!empty($project_node->project->drupal_path)) { -$platform->publish_path = $project_node->project->code_path . '/' . $environment['title'] . '/' . $project_node->project->drupal_path; -} -else { -$platform->publish_path = $project_node->project->code_path . '/' . $environment['title']; -} - -// Other attributes -$platform->web_server = $environment['web_server']; - -foreach ($settings as $setting_name => $element){ -if ($element['#node_type'] == 'platform'){ -$platform->{$setting_name} = $environment['settings'][$setting_name]; -} -} -$platform_node = _devshop_projects_node_create('platform', $platform); -} - -$environment['platform'] = $platform_node->nid; -$environment['git_ref'] = $environment['git_ref']; -$project_node->project->environments[$name] = (object) $environment; -} - -// For all removed platforms, trigger a delete task -$removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); - -foreach ($removed_environments as $environment_name => $settings) { -// @TODO: Determine what to do here based on task status... -// if verify task hasn't even run yet (and has never run) we can just delete -// the platform node. - -// Create 'delete' task for the removed platform -$platform_nid = $settings['platform']; -if ($platform_nid){ -hosting_add_task($platform_nid, 'delete'); -} -} - -// Somehow, NEW environment still appears! -unset($project_node->project->environments['NEW']); -$project_node->no_verify = TRUE; -node_save($project_node); + // Get project and reset properties.. + $project = $form_state['project']; + $project_node = node_load($project->project_nid); + + $settings = module_invoke_all('devshop_project_settings', $project_node); + + // Create these platforms, if they don't exist yet + foreach ($form_state['values']['project']['environments'] as $name => $environment) { + + $environment = (object) $environment; + + // If platform exists, it's because user went back in the wizard. + $platform_nid = $project->environments[$name]->platform; + + // If the platform already exists, update settings and verify it again. + if ($platform_nid) { + // @TODO: Apply settings and re-save platform. + hosting_add_task($platform_nid, 'verify'); + } + + // If platform hasn't been created yet, do so now! + else { + + // Build platform node + $platform = new stdClass; + $platform->title = $project->name . '_' . $name; + + // Platform publish_path + if (!empty($project->drupal_path)) { + $platform->publish_path = $project->code_path . '/' . $environment->name . '/' . $project->drupal_path; + } else { + $platform->publish_path = $project->code_path . '/' . $environment->name; + } + + // Other attributes + $platform->web_server = $environment->settings['web_server']; + foreach ($settings as $setting_name => $element) { + if ($element['#node_type'] == 'platform') { + $platform->{$setting_name} = $environment->settings[$setting_name]; + } + } + $platform_node = _devshop_projects_node_create('platform', $platform); + } + $environment->platform = $platform_node->nid; + $project->environments[$name] = (object) $environment; + } + + // Somehow, NEW environment still appears! + unset($project_node->project->environments['NEW']); + + // For all removed platforms, trigger a delete task + $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); + + foreach ($removed_environments as $environment_name => $settings) { + // @TODO: Determine what to do here based on task status... + // if verify task hasn't even run yet (and has never run) we can just delete + // the platform node. + + // Create 'delete' task for the removed platform + $platform_nid = $settings['platform']; + if ($platform_nid) { + hosting_add_task($platform_nid, 'delete'); + } + } + + // Save the project node but don't verify. This triggers environment saving. + $project_node->project = $project; + $project_node->no_verify = TRUE; + node_save($project_node); } \ No newline at end of file From e5efbb3e8722c823e1df86bd2c3db111ca2c42b2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 14:28:59 -0400 Subject: [PATCH 0961/3476] Changing environment title to name. --- devshop_projects/inc/create/step-3.inc | 16 ++++++++-------- devshop_projects/inc/theme.inc | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index c1e8dd93a..2bc384fe9 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -69,7 +69,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { ); // Environment properties - $form['project']['environments'][$name]['title'] = array( + $form['project']['environments'][$name]['name'] = array( '#type' => 'textfield', '#title' => t('Environment Name'), '#default_value' => $env_title, @@ -128,8 +128,8 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) $values = &$form_state['values']; // Changes NEW environment data to $title - if ($values['project']['environments']['NEW']['title']) { - $new_env = $values['project']['environments']['NEW']['title']; + if ($values['project']['environments']['NEW']['name']) { + $new_env = $values['project']['environments']['NEW']['name']; $new_env_settings = $values['project']['environments']['NEW']; $values['project']['environments'][$new_env] = $new_env_settings; @@ -147,9 +147,9 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // Check environment titles foreach ($values['project']['environments'] as $env => $env_settings) { // Check for illegal chars - if ($env != 'NEW' && !empty($env_settings['title'])) { - if (!preg_match('!^[a-z0-9_]+$!', $env_settings['title'])) { - $form_item = 'environments][' . $env . '][title'; + if ($env != 'NEW' && !empty($env_settings['name'])) { + if (!preg_match('!^[a-z0-9_]+$!', $env_settings['name'])) { + $form_item = 'environments][' . $env . '][name'; form_set_error($form_item, t('The environment name must contain only lowercase letters, numbers, and underscores.')); } } @@ -158,9 +158,9 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) // Reject if empty if (count($values['project']['environments']) < 1) { if ($form_state['clicked_button']['#name'] == 'add_environment') { - form_set_error('project][environments][NEW][title', t('Name this environment before you add another.')); + form_set_error('project][environments][NEW][name', t('Name this environment before you add another.')); } else { - form_set_error('project][environments][NEW][title', t('You must add at least one environment.')); + form_set_error('project][environments][NEW][name', t('You must add at least one environment.')); } } } diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 6600f4941..ea1fe4f7a 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -50,9 +50,9 @@ function theme_devshop_projects_create_settings_form($form) { $header = array(); foreach (element_children($form) as $env_name) { $row = array(); - $header['title'] = 'Name'; + $header['name'] = 'Name'; $header['git_ref'] = t('Branch/Tag'); - $row[] = drupal_render($form[$env_name]['title']); + $row[] = drupal_render($form[$env_name]['name']); $row[] = drupal_render($form[$env_name]['git_ref']); foreach(element_children($form[$env_name]['settings']) as $setting){ From 46df23d08b954f89b4f6229461aa2aa1f0e3ec3a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 17:41:54 -0400 Subject: [PATCH 0962/3476] Adding commented out theme enabling for boots!. --- devshop_projects/devshop_projects.install | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.install b/devshop_projects/devshop_projects.install index f9fe5d3de..ac53b4954 100644 --- a/devshop_projects/devshop_projects.install +++ b/devshop_projects/devshop_projects.install @@ -278,4 +278,13 @@ function devshop_projects_update_6004(){ 'description' => 'A serialized array of settings for this project.', )); return $ret; -} \ No newline at end of file +} + +/** + * Turn on the new Boots theme. + * Uncomment once the theme is ready. +function devshop_projects_update_600X() { + variable_set('theme_default','boots'); + return array(); +} + */ From b05000afe4e52e0bf81267e0164734bab36ef05e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 18:09:54 -0400 Subject: [PATCH 0963/3476] Adding javascript to hide live settings if there is no live domain. --- devshop_projects/inc/create/create.inc | 1 + devshop_projects/inc/create/create.js | 29 +++++++++++++++++++++++++- devshop_projects/inc/create/step-1.inc | 1 - devshop_projects/inc/create/step-2.inc | 21 ++++++++++++++++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create/create.inc b/devshop_projects/inc/create/create.inc index 79725d6de..bdad7c77a 100644 --- a/devshop_projects/inc/create/create.inc +++ b/devshop_projects/inc/create/create.inc @@ -13,6 +13,7 @@ function devshop_projects_create_wizard($step = NULL){ ctools_include('wizard'); ctools_include('object-cache'); drupal_add_css(drupal_get_path('module', 'devshop_projects') . '/inc/devshop.css'); + drupal_add_js(drupal_get_path('module', 'devshop_projects') . '/inc/create/create.js'); // Setup form info $form_info = devshop_projects_create_wizard_info(); diff --git a/devshop_projects/inc/create/create.js b/devshop_projects/inc/create/create.js index 8c16d390e..a09725a78 100644 --- a/devshop_projects/inc/create/create.js +++ b/devshop_projects/inc/create/create.js @@ -1,4 +1,8 @@ - +/** + * Start New Project: + * + * Step 1: Takes the entered name and creates a base url and code path from it. + */ Drupal.behaviors.createStep1 = function() { $( "#edit-title" ).keyup(function(event) { @@ -11,4 +15,27 @@ Drupal.behaviors.createStep1 = function() { $('#edit-base-url').val(base_url); }); +} + +/** + * Step 2: Settings + */ +Drupal.behaviors.createStep2 = function() { + + // Hide unless + $('#edit-project-settings-live-live-domain-www-wrapper').hide(); + $('#edit-project-settings-live-environment-aliases-wrapper').hide(); + + + $('#edit-project-settings-live-live-domain').keyup(function(){ + if ($(this).val()){ + $('#edit-project-settings-live-live-domain-www-wrapper').show(); + $('#edit-project-settings-live-environment-aliases-wrapper').show(); + } + else { + $('#edit-project-settings-live-live-domain-www-wrapper').hide(); + $('#edit-project-settings-live-environment-aliases-wrapper').hide(); + } + }); + } \ No newline at end of file diff --git a/devshop_projects/inc/create/step-1.inc b/devshop_projects/inc/create/step-1.inc index 2d0b307b5..4745e169a 100644 --- a/devshop_projects/inc/create/step-1.inc +++ b/devshop_projects/inc/create/step-1.inc @@ -14,7 +14,6 @@ function devshop_project_create_step_git(&$form, &$form_state) { $project = &$form_state['project']; $project_node = node_load($project->project_nid); - drupal_add_js(drupal_get_path('module', 'devshop_projects') . '/inc/create/create.js'); if ($project->verify_error){ $form['note'] = array( diff --git a/devshop_projects/inc/create/step-2.inc b/devshop_projects/inc/create/step-2.inc index ae2e42695..63843b811 100644 --- a/devshop_projects/inc/create/step-2.inc +++ b/devshop_projects/inc/create/step-2.inc @@ -9,8 +9,18 @@ * STEP 2: Form */ function devshop_project_create_step_settings(&$form, &$form_state) { -$project = &$form_state['project']; + // Load project and it's form + $project = &$form_state['project']; + $node = node_load($project->project_nid); + $form += devshop_projects_form($node); + $form['#node'] = $node; + devshop_pull_form_alter($form, $form_state, 'project_node_form'); + + // Remove "environment" selector. + $form['project']['settings']['live']['live_environment']['#type'] = 'value'; + + // Add "Path to Drupal". $form['drupal_path'] = array( '#type' => 'textfield', '#title' => t('Path to Drupal'), @@ -25,7 +35,11 @@ $project = &$form_state['project']; * STEP 2: Validate */ function devshop_project_create_step_settings_validate(&$from, &$form_state) { + // Pass through to devshop validate + // devshop_projects_validate($node, $form); + dsm($form_state); + form_set_error('wtf', 'wtf'); } /** @@ -34,4 +48,9 @@ function devshop_project_create_step_settings_validate(&$from, &$form_state) { function devshop_project_create_step_settings_submit(&$from, &$form_state) { $project = &$form_state['project']; $project->drupal_path = $form_state['values']['drupal_path']; + + + $project->settings = $form_state['values']['project']['settings']; + + // Save the settings to the project node } From 504f556b0b0a29ab737a8febde9dae1a11df3757 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 18:24:53 -0400 Subject: [PATCH 0964/3476] Adding project settings to project creation! --- devshop_projects/inc/create/step-2.inc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/create/step-2.inc b/devshop_projects/inc/create/step-2.inc index 63843b811..8a86ad415 100644 --- a/devshop_projects/inc/create/step-2.inc +++ b/devshop_projects/inc/create/step-2.inc @@ -20,8 +20,11 @@ function devshop_project_create_step_settings(&$form, &$form_state) { // Remove "environment" selector. $form['project']['settings']['live']['live_environment']['#type'] = 'value'; + // Default to pull on commit + $form['project']['settings']['pull']['pull_enabled']['#default_value'] = TRUE; + // Add "Path to Drupal". - $form['drupal_path'] = array( + $form['project']['drupal_path'] = array( '#type' => 'textfield', '#title' => t('Path to Drupal'), '#description' => t('Enter the relative path to the index.php file in your repository. Leave blank if index.php is in the root.'), @@ -35,11 +38,8 @@ function devshop_project_create_step_settings(&$form, &$form_state) { * STEP 2: Validate */ function devshop_project_create_step_settings_validate(&$from, &$form_state) { - // Pass through to devshop validate + // @TODO: Pass through to devshop validate // devshop_projects_validate($node, $form); - - dsm($form_state); - form_set_error('wtf', 'wtf'); } /** @@ -47,10 +47,14 @@ function devshop_project_create_step_settings_validate(&$from, &$form_state) { */ function devshop_project_create_step_settings_submit(&$from, &$form_state) { $project = &$form_state['project']; - $project->drupal_path = $form_state['values']['drupal_path']; - - + $project->drupal_path = $form_state['values']['project']['drupal_path']; $project->settings = $form_state['values']['project']['settings']; + // Save the settings to the project node + $node = $form_state['values']['old']; + $node->project = $project; + + $node->no_verify = TRUE; + node_save($node); } From eaf1199039ef4904cd1dd47c08318e2aa7329657 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 18:25:23 -0400 Subject: [PATCH 0965/3476] fixing git branch saving in the project form. --- devshop_projects/inc/forms.inc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 8727fa98e..faaf2108c 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -341,11 +341,11 @@ function devshop_projects_form(&$node) { // Save git branches and tags $form['project']['settings']['git']['branches'] = array( '#type' => 'value', - '#value' => $node->project->data->git['branches'], + '#value' => $project->settings->git['branches'], ); $form['project']['settings']['git']['tags'] = array( '#type' => 'value', - '#value' => $node->project->data->git['tags'], + '#value' => $project->settings->git['tags'], ); // Live Environment settings. @@ -364,7 +364,7 @@ function devshop_projects_form(&$node) { ); // Live Environment - $environments = array_keys($node->project->environments); + $environments = array_keys($project->environments); $environments_options = array_combine($environments, $environments); $environments_options[''] = t('No live environment'); @@ -373,7 +373,7 @@ function devshop_projects_form(&$node) { '#title' => t('Live environment'), '#description' => t('The environment for the live website.'), '#options' => $environments_options, - '#default_value' => $node->project->settings->live['live_environment'], + '#default_value' => $project->settings->live['live_environment'], ); // Use www @@ -381,7 +381,7 @@ function devshop_projects_form(&$node) { '#type' => 'checkbox', '#title' => t('Add www as a ServerAlias for Live Environment'), '#description' => t('Access the site at http://livedomain.com and http://www.livedomain.com'), - '#default_value' => $node->project->settings->live['live_domain_www'], + '#default_value' => $project->settings->live['live_domain_www'], ); // Use aliases @@ -389,7 +389,7 @@ function devshop_projects_form(&$node) { '#type' => 'checkbox', '#title' => t('Environment Subdomains'), '#description' => t('Create subdomains under the live domain for all environments.'), - '#default_value' => $node->project->settings->live['environment_aliases'], + '#default_value' => $project->settings->live['environment_aliases'], ); return $form; From bf1e180fc667022d4a12f4be558a45d59ddb7e2e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 18:26:10 -0400 Subject: [PATCH 0966/3476] Removing the hacky javascript. --- devshop_pull/devshop_pull.module | 8 -------- 1 file changed, 8 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 0d16abd13..14eff0d85 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -110,17 +110,9 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { '#description' => t('Run a "Pull Code" task when a commit notification is received. All environments set to a branch will have their code updated.'), '#default_value' => $node->project->settings->pull['pull_enabled'], '#attributes' => array( - 'onclick' => 'if ($(this).attr("checked")) { $("#pull-url").show(); } else { $("#pull-url").hide(); }', ), ); - if (!$node->project->settings->pull['pull_enabled']){ - $display_none = 'style="display:none;"'; - } - else { - $display_none = ''; - } - module_load_include('inc', 'devshop_pull'); $form['project']['settings']['pull']['pull_url'] = array( '#type' => 'item', From cd040c88267c0decd4d75dfb7e868a8f4a7eb04b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 18:30:09 -0400 Subject: [PATCH 0967/3476] Removing hook_devshop_project_settings() from devshop_pull! use form alter instead. --- devshop_pull/devshop_pull.module | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 14eff0d85..10e2e1b6d 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -74,20 +74,6 @@ function devshop_pull_hosting_queues() { return $items; } -/** - * Implements hook_devshop_project_settings() - */ -function devshop_pull_devshop_project_settings(){ - return array( - 'pull_disabled' => array( - '#title' => t('Disable Pull on Commit'), - '#node_type' => 'platform', - '#type' => 'checkbox', - '#description' => t('Turn off automatic code updates for this environment.'), - ), - ); -} - /** * Implements hook_form_alter(). */ @@ -173,6 +159,17 @@ function devshop_pull_form_alter(&$form, &$form_state, $form_id) { ); } + + // "Environment Settings" form. + if ($form_id == 'site_node_form' && isset($form['#node']->project)) { + $form['environment']['settings']['pull_disabled'] = array( + '#title' => t('Disable Pull on Commit'), + '#node_type' => 'platform', + '#type' => 'checkbox', + '#description' => t('Turn off automatic code updates for this environment.'), + '#default_value' => $form['#node']->environment->settings->pull_disabled, + ); + } } /** From c0f4512c9dfb27b7e64c1de60f61bc8a2bd8e7c8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 5 Sep 2014 18:52:39 -0400 Subject: [PATCH 0968/3476] Loading environment settings into environment form. --- devshop_projects/tasks/create.inc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 82fd345f5..20efb670a 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -92,6 +92,12 @@ function hosting_task_devshop_create_form($node) { '#type' => 'value', '#value' => $node->nid, ); + + // Load in environment settings + // Load project and it's form +// $site_form = array(); + $form_state = array(); + devshop_projects_form_alter($form, $form_state, 'site_node_form'); return $form; } @@ -136,8 +142,9 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { $environment_name = $form_state['values']['parameters']['environment_name']; $branch = !empty($form_state['values']['parameters']['new_branch'])? $form_state['values']['parameters']['new_branch']: $form_state['values']['parameters']['branch']; $environment_source = $form_state['values']['parameters']['environment_source']; + $environment_settings = $form_state['values']['parameters']['project']['environment']['settings']; - hosting_create_environment($project, $environment_name, $branch, $environment_source); + hosting_create_environment($project, $environment_name, $branch, $environment_source, $environment_settings); // We are replacing hosting_confirm_form_submit here, so just do what it does, // minus the hosting task creation! @@ -163,7 +170,7 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { * @param $fork_source * If desired, the environment to fork off of. (Copy the database and create a new branch from) */ -function hosting_create_environment($project_node, $environment_name, $branch, $fork_source = NULL) { +function hosting_create_environment($project_node, $environment_name, $branch, $fork_source = NULL, $settings = NULL) { global $user; $project = $project_node->project; @@ -176,7 +183,7 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ $platform->name = $user->name; // Set servers - // @TODO: Respect environment settings. + // @TODO: Use $settings to retrieve these. $servers = hosting_get_servers('http'); $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers));; @@ -214,8 +221,9 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ $environment = new stdClass(); $environment->git_ref = $branch; $environment->platform = $platform->nid; - // @TODO: Implement default environment settings. + // Use settings passed in to this function. + $environment->settings = $settings; } $project->environments[$environment_name] = $environment; From efce4655c566b2697014e94c237621ab4ae4aa51 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Sep 2014 14:22:18 -0400 Subject: [PATCH 0969/3476] Adding devshop-deploy. task permission --- devshop_projects/devshop_projects.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 82a66e27b..f7281649b 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -42,6 +42,7 @@ function devshop_projects_perm() { 'create devshop-pull task', 'create devshop-sync task', 'create devshop-delete task', + 'create devshop-deploy task', 'create project', 'view project', 'edit project', From e08e1aeb8be4db19d8e49c917608c8a7023f3b7b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Sep 2014 14:29:11 -0400 Subject: [PATCH 0970/3476] Fixing links to tasks, adding token so we don't get access denied. --- devshop_projects/inc/ui.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 23fa03a1c..823cbb7af 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -350,6 +350,11 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } } + // Generate URLs for all actions. + foreach ($actions as &$action) { + $action['url'] = url($action['href'], $action); + } + $node->environment_actions[$environment->name] = $actions; $row[] = theme('ctools_dropdown', t('Actions'), $actions); From f8d3ab3eae711a42b16121170c2d24343b24c34b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 10:41:47 -0400 Subject: [PATCH 0971/3476] Adding tasks viewer and environment status display. --- devshop_projects/inc/ui.inc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 823cbb7af..d5514994a 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -365,6 +365,29 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { else { $rows[] = $row; } + + // Environment Tasks + $environment->tasks = hosting_get_tasks('rid', $environment->site); + $environment->task_count = count($environment->tasks); + $environment->active_tasks = 0; + + if (function_exists('boots_render_tasks')){ + $environment->tasks_list = boots_render_tasks($environment->tasks, 'environment btn btn-small btn-link'); + } + + foreach ($environment->tasks as &$task) { + if ($task->task_status == HOSTING_TASK_QUEUED || $task->task_status == HOSTING_TASK_PROCESSING){ + $environment->active_tasks++; + } + } + + // Progress Bar + if (rand(0,1) || $environment->active_tasks > 0){ + $environment->progress_classes = 'progress-bar-striped progress-bar-warning active'; + $environment->active_tasks++; + } else { + $environment->progress_classes = 'inactive'; + } } $header = array(); $table = theme('table', $header, $rows, array('class' => 'environments-table')); From 6c6f196458f301133fbfaeef9d3c72ce32795e92 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 10:42:07 -0400 Subject: [PATCH 0972/3476] removing test code. --- devshop_projects/inc/ui.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index d5514994a..28e5476aa 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -382,7 +382,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } // Progress Bar - if (rand(0,1) || $environment->active_tasks > 0){ + if ($environment->active_tasks > 0){ $environment->progress_classes = 'progress-bar-striped progress-bar-warning active'; $environment->active_tasks++; } else { From 1564e255c751f9a711e94fe2622c38ff40257e29 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 11:23:14 -0400 Subject: [PATCH 0973/3476] Look for platform tasks if site isn't present. --- devshop_projects/inc/ui.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 28e5476aa..46a17851e 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -367,7 +367,12 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } // Environment Tasks - $environment->tasks = hosting_get_tasks('rid', $environment->site); + if ($environment->site) { + $environment->tasks = hosting_get_tasks('rid', $environment->site); + } + else { + $environment->tasks = hosting_get_tasks('rid', $environment->platform); + } $environment->task_count = count($environment->tasks); $environment->active_tasks = 0; From b237f7cc05a32f3d278a26f3e0bd0bb44f417974 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 11:45:49 -0400 Subject: [PATCH 0974/3476] Fixing project creation form when path settings are automatically determined. --- devshop_projects/devshop_projects.module | 10 ++++++++-- devshop_projects/inc/create/step-1.inc | 11 ++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index f7281649b..ba8bfae52 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -441,10 +441,16 @@ function devshop_projects_url($name, $environment = NULL){ } else { $pattern = variable_get('devshop_project_environment_url_pattern', "@environment.@project.@hostname"); - return strtr($pattern, array( + + $replacements = array( '@environment' => $environment, '@project' => $name, '@hostname' => $_SERVER['SERVER_NAME'], - )); + ); + + if (is_null($environment)){ + $replacements['@environment.'] = ''; + } + return strtr($pattern, $replacements); } } diff --git a/devshop_projects/inc/create/step-1.inc b/devshop_projects/inc/create/step-1.inc index 4745e169a..2bc8f0627 100644 --- a/devshop_projects/inc/create/step-1.inc +++ b/devshop_projects/inc/create/step-1.inc @@ -62,7 +62,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { '#type' => variable_get('devshop_projects_allow_custom_code_path', FALSE)? 'textfield': 'value', '#title' => t('Code path'), '#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project. There must not be a file or directory at this path.'), - '#required' => TRUE, + '#required' => variable_get('devshop_projects_allow_custom_code_path', FALSE), '#size' => 40, '#default_value' => $project->code_path, '#maxlength' => 255, @@ -76,7 +76,7 @@ function devshop_project_create_step_git(&$form, &$form_state) { '#type' => variable_get('devshop_projects_allow_custom_base_url', FALSE)? 'textfield': 'value', '#title' => t('Base URL'), '#description' => t('All sites will be under a subdomain of this domain.'), - '#required' => TRUE, + '#required' => variable_get('devshop_projects_allow_custom_base_url', FALSE), '#size' => 40, '#default_value' => $project->base_url, '#maxlength' => 255, @@ -146,12 +146,12 @@ function devshop_project_create_step_git_validate(&$form, &$form_state) { // If custom code paths are not allowed, set the value here using the project name. if (!variable_get('devshop_projects_allow_custom_code_path', FALSE)){ - form_set_value($form['code_path'], $form_state['values']['code_path'] . '/' . $project_name, $form_state); + form_set_value($form['code_path'], variable_get('devshop_project_base_path', '/var/aegir/projects') . '/' . $project_name, $form_state); } // If custom base URLs are not allowed, set the value here using the project name. - if (!variable_get('devshop_projects_allow_custom_code_path', FALSE)){ - form_set_value($form['base_url'], $form_state['values']['base_url'] . '/' . $project_name, $form_state); + if (!variable_get('devshop_projects_allow_custom_base_url', FALSE)){ + form_set_value($form['base_url'], devshop_projects_url($project_name), $form_state); } } @@ -160,6 +160,7 @@ function devshop_project_create_step_git_validate(&$form, &$form_state) { */ function devshop_project_create_step_git_submit(&$from, &$form_state) { global $user; +dsm($form_state); $project = &$form_state['project']; From 080c7c9afa01b56c187295da7774d4f1cd094a35 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 11:46:01 -0400 Subject: [PATCH 0975/3476] removing dsm --- devshop_projects/inc/create/step-1.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/create/step-1.inc b/devshop_projects/inc/create/step-1.inc index 2bc8f0627..aa519199f 100644 --- a/devshop_projects/inc/create/step-1.inc +++ b/devshop_projects/inc/create/step-1.inc @@ -160,7 +160,6 @@ function devshop_project_create_step_git_validate(&$form, &$form_state) { */ function devshop_project_create_step_git_submit(&$from, &$form_state) { global $user; -dsm($form_state); $project = &$form_state['project']; From 31e8614254478ecf24d347f8516489fa20b32a93 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 13:50:17 -0400 Subject: [PATCH 0976/3476] Adding pull settings to form. --- devshop_projects/inc/forms.inc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index faaf2108c..44d6f0aaa 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -209,6 +209,29 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#description' => t('Do not pull code to the server on commit & push.'), ); + $form['environment']['settings']['pull'] = array( + '#type' => 'fieldset', + '#title' => t('Automatic Code Pull'), + '#description' => t('When code is automatically pulled to this environment, do the following:'), + ); + $form['environment']['settings']['pull']['pull_revert'] = array( + '#type' => 'checkbox', + '#title' => t('Revert all features.'), + '#default_value' => $environment->settings->pull['pull_revert'], + '#description' => t(''), + ); + + $form['environment']['settings']['pull']['pull_update'] = array( + '#type' => 'checkbox', + '#title' => t('Run database updates.'), + '#default_value' => $environment->settings->pull['pull_update'], + ); + $form['environment']['settings']['pull']['pull_cache'] = array( + '#type' => 'checkbox', + '#title' => t('Clear all caches.'), + '#default_value' => $environment->settings->pull['pull_cache'], + ); + // Add our own submit handler $form['buttons']['submit']['#submit'][] = 'devshop_projects_environment_settings_submit'; } From c0076348fa0f248537a4ebf85f8e07c5be7b0eaa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 14:09:13 -0400 Subject: [PATCH 0977/3476] Adding last comit. --- devshop_projects/inc/nodes.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 5d547c7c3..dcba05083 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -97,6 +97,10 @@ function devshop_projects_load($node) { 'name' => $environment->db_server ); + // Last commit + $path = $project->code_path . '/' . $environment->name; + $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar
%s' HEAD^...HEAD"); + // Save to project environments collection. $environments[$environment->name] = $environment; } From d06a823c7750904881396008e96bb2fc31b0c6aa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 14:11:50 -0400 Subject: [PATCH 0978/3476] Removing comment text for now. --- devshop_projects/inc/nodes.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index dcba05083..693507cf9 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -99,7 +99,8 @@ function devshop_projects_load($node) { // Last commit $path = $project->code_path . '/' . $environment->name; - $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar
%s' HEAD^...HEAD"); +// $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar
%s' HEAD^...HEAD"); + $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar' HEAD^...HEAD"); // Save to project environments collection. $environments[$environment->name] = $environment; From be050de3294d933d8a4d8870d3fc278bbca55a69 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 14:30:45 -0400 Subject: [PATCH 0979/3476] Fixing multiple commits showing for merges. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 693507cf9..9960d7951 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -100,7 +100,7 @@ function devshop_projects_load($node) { // Last commit $path = $project->code_path . '/' . $environment->name; // $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar
%s' HEAD^...HEAD"); - $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar' HEAD^...HEAD"); + $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar' --max-count=1"); // Save to project environments collection. $environments[$environment->name] = $environment; From c66adc8bfdb2bd3c981a8b030b477968bec607ab Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 14:37:18 -0400 Subject: [PATCH 0980/3476] Cloning in progress message. --- devshop_projects/inc/nodes.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 9960d7951..62d6b886f 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -102,6 +102,10 @@ function devshop_projects_load($node) { // $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar
%s' HEAD^...HEAD"); $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar' --max-count=1"); + if (empty($environment->git_current)){ + $environment->git_current = t('Cloning in progress...'); + } + // Save to project environments collection. $environments[$environment->name] = $environment; } From 68ec8f1239969224c5568c317a28582995946f72 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 22:10:53 -0400 Subject: [PATCH 0981/3476] Using table class. --- devshop_projects/inc/ui.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 46a17851e..0c19923a2 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -85,8 +85,8 @@ HTML; if (empty($data['header'])){ $data['header'] = $header; } - - $output = theme('table', $data['header'], $data['rows'], array('class' => 'hosting-table')); + + $output = theme('table', $data['header'], $data['rows'], array('class' => 'table')); } return $output; } From 3d334e9756e1794afbdf2d17e5e2a265f823a292 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 22:14:50 -0400 Subject: [PATCH 0982/3476] Adding missing permission. --- devshop_log/devshop_log.module | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index 949963dfb..f8ce81d2c 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -11,6 +11,7 @@ function devshop_log_perm() { return array( 'view git commit logs', + 'access project logs', ); } From 1c04edf16cadd2e16bb9c8ec517a895f9c22a241 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Sep 2014 22:45:54 -0400 Subject: [PATCH 0983/3476] Adding a block for the project nav to make it easy to put on multiple pages. --- devshop_projects/devshop_projects.module | 33 +++++++++ devshop_projects/inc/theme.inc | 88 ++++++++++++++++++++++++ devshop_projects/project-nav.tpl.php | 86 +++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 devshop_projects/project-nav.tpl.php diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index ba8bfae52..9a9b04244 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -157,6 +157,39 @@ function devshop_projects_menu() { } +/** + * Implements hook_block() + * + * Provides three blocks: + * - DevShop Tasks + * - Created by ThinkDrop + * - Powered by Aegir + */ +function devshop_projects_block($op = 'list', $delta = 0, $edit = NULL) { + switch ($op) { + case 'list': + $blocks['project_nav'] = array( + 'info' => t('DevShop Project Nav'), + 'weight' => '-10', + 'cache' => BLOCK_NO_CACHE, + 'status' => 1, + 'region' => 'pre_content', + ); + return $blocks; + break; + + case 'view': + if ($delta == 'project_nav'){ + $node = menu_get_object('node'); + if ($node->type == 'project') { + $block['subject'] = ''; + $block['content'] = theme('devshop_project_nav', $node); + } + } + return $block; + } +} + /** * Environment settings page menu callback. * diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 064b42e8e..0f91289f3 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -15,6 +15,12 @@ function devshop_projects_theme(){ 'form' => NULL, ), ), + 'devshop_project_nav' => array( + 'arguments' => array( + 'node' => NULL, + ), + 'template' => 'project-nav', + ), ); } @@ -86,3 +92,85 @@ function devshop_projects_preprocess_page(&$vars){ $vars['tabs2'] = theme('item_list', $links, '', 'ul', array('class' => 'tabs secondary')); } } + +/** + * Preprocess for project_nav.tpl.php + */ +function template_preprocess_devshop_project_nav(&$vars){ + + global $user; + $node = $vars['node']; + $project = $vars['node']->project; + + // @TODO: Detect other web URLs for other git hosts. + if (strpos($project->git_url, 'github.com') !== FALSE) { + $url = str_replace('git@github.com:', 'http://github.com/', $project->git_url); + $vars['github_url'] = $url; + } + + // Generate branches/tags lists + $vars['branches_count'] = count($project->settings->git['branches']); + $vars['tags_count'] = count($project->settings->git['tags']); + $vars['branches_items'] = array(); + $vars['branches_icon'] = 'code-fork'; + + if ($vars['branches_count'] == 0){ + // If branches are 0 and last verifying is queued... + if ($node->verify->task_status == HOSTING_TASK_PROCESSING || $node->verify->task_status == HOSTING_TASK_QUEUED) { + $vars['branches_show_label'] = TRUE; + $vars['branches_label'] = t('Refreshing...'); + $vars['branches_class'] = 'btn-warning'; + $vars['branches_icon'] = 'gear fa-spin'; + $vars['branches_items'][] = l(t('View task log'), 'node/' . $node->verify->nid); + + } + // If branches are 0 and last verifying failed... + elseif ($node->verify->task_status == HOSTING_TASK_ERROR) { + $vars['branches_show_label'] = TRUE; + $vars['branches_label'] = t('Error'); + $vars['branches_class'] = 'btn-danger'; + $vars['branches_items'][] = t('There was a problem refreshing branches and tags.'); + $vars['branches_items'][] = l(t('View task log'), 'node/' . $node->verify->nid); + $vars['branches_items'][] = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); + } + // If branches are 0 and last verifying has completed... This should never happen, because the task would error out. + elseif ($node->verify->task_status == HOSTING_TASK_SUCCESS) { + $vars['branches_show_label'] = TRUE; + $vars['branches_label'] = t('No branches found!'); + $vars['branches_items'][] = l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link'), 'query' => array('token' => drupal_get_token($user->uid)))); + } + } + // If there are branches... build the branch items + else { + $vars['branches_show_label'] = FALSE; + $vars['branches_label'] = format_plural($vars['branches_count'], t('1 Branch'), t('!count Branches', array('!count' => $vars['branches_count']))); + + foreach ($project->settings->git['branches'] as $branch){ + $href = isset($vars['github_url'])? $vars['github_url'] . '/tree/' . $branch: '#'; + $vars['branches_items'][] = " $branch "; + } + } + + if ($vars['tags_count']){ +//
  • + + $vars['branches_label'] .= ' & ' . format_plural($vars['tags_count'], t('1 Tag'), t('!count Tags', array('!count' => $vars['tags_count']))); + + + foreach ($project->settings->git['tags'] as $branch){ + $href = isset($vars['github_url'])? $vars['github_url'] . '/tree/' . $branch: '#'; + $vars['branches_items'][] = " $branch "; + $vars['git_refs'][] = $branch; + } + } + + $vars['dashboard_link'] = l(' ' . t('Dashboard'), "node/$node->nid", array('html' => TRUE)); + + if (node_access('update', $node)){ + $vars['settings_link'] = l(' ' . t('Settings'), "node/$node->nid/edit", array('html' => TRUE)); + } + + if (user_access('access project logs')){ + $vars['logs_link'] = l(' ' . t('Logs'), "node/$node->nid/logs", array('html' => TRUE)); + } +} \ No newline at end of file diff --git a/devshop_projects/project-nav.tpl.php b/devshop_projects/project-nav.tpl.php new file mode 100644 index 000000000..cef0dcf55 --- /dev/null +++ b/devshop_projects/project-nav.tpl.php @@ -0,0 +1,86 @@ + + + + From d4515eaec14dcc800aafa8cbde05155cad1e58a9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Sep 2014 21:21:03 -0400 Subject: [PATCH 0984/3476] Fixing a notice on the project create form. --- devshop_projects/inc/forms.inc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 44d6f0aaa..de970dfdd 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -388,8 +388,13 @@ function devshop_projects_form(&$node) { // Live Environment $environments = array_keys($project->environments); - $environments_options = array_combine($environments, $environments); - $environments_options[''] = t('No live environment'); + if (empty($environments)){ + $environments_options= array(); + } + else { + $environments_options = array_combine($environments, $environments); + $environments_options[''] = t('No live environment'); + } $form['project']['settings']['live']['live_environment'] = array( '#type' => 'select', From 4960ee27f08ace2796b26d0f72d864e78cb9ea97 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 5 Oct 2014 10:06:06 -0400 Subject: [PATCH 0985/3476] Reformatting code. Not sure how it got this way. --- devshop_projects/inc/create/step-4.inc | 259 +++++++++++++------------ 1 file changed, 135 insertions(+), 124 deletions(-) diff --git a/devshop_projects/inc/create/step-4.inc b/devshop_projects/inc/create/step-4.inc index 0ad59fcbf..b7a17c6ae 100644 --- a/devshop_projects/inc/create/step-4.inc +++ b/devshop_projects/inc/create/step-4.inc @@ -1,180 +1,191 @@ project_nid); + $project = &$form_state['project']; + $project_node = node_load($project->project_nid); -$profiles = array(); -$available_profiles = array(); -$completed = TRUE; + $profiles = array(); + $available_profiles = array(); + $completed = TRUE; -$form['nid'] = array( -'#type' => 'value', -'#value' => $project_node->nid, -); + $form['nid'] = array( + '#type' => 'value', + '#value' => $project_node->nid, + ); // Display the platforms -$rows = array(); -$header = array(t('Name'), t('Branch'), t('Version'), t('Install Profiles'), t('Status')); -$all_tasks_queued = TRUE; -$all_tasks_succeeded = TRUE; - -foreach ($project_node->project->environments as $name => $environment){ + $rows = array(); + $header = array( + t('Name'), + t('Branch'), + t('Version'), + t('Install Profiles'), + t('Status') + ); + $all_tasks_queued = TRUE; + $all_tasks_succeeded = TRUE; + + foreach ($project_node->project->environments as $name => $environment) { // Get platform and latest verify task. -$platform_nid = $environment->platform; -$platform = node_load($platform_nid); -$task = hosting_get_most_recent_task($platform_nid, 'verify'); + $platform_nid = $environment->platform; + $platform = node_load($platform_nid); + $task = hosting_get_most_recent_task($platform_nid, 'verify'); // Build a table. -$row = array(); -$row['name'] = $name; -$row['branch'] = $environment->git_ref; -$row['version'] = $task->task_status == HOSTING_TASK_SUCCESS? $platform->release->version: t('...'); + $row = array(); + $row['name'] = $name; + $row['branch'] = $environment->git_ref; + $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS ? $platform->release->version : t('...'); // If platform verified successfully: -if ($task->task_status == HOSTING_TASK_SUCCESS) { + if ($task->task_status == HOSTING_TASK_SUCCESS) { // It's not really ready until we get a version. -if (empty($platform->release->version)){ -$completed = FALSE; -$row['version'] = '...'; -$row['profiles'] = '...'; -} + if (empty($platform->release->version)) { + $completed = FALSE; + $row['version'] = '...'; + $row['profiles'] = '...'; + } // Collect install profiles -$profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); -if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { -$profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); -$row['profiles'] = implode(', ', $profiles[$name]); -} else { -$profiles[$name] = array(); -} + $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); + + dsm($profiles_shortnames); + if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { + $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); + $row['profiles'] = implode(', ', $profiles[$name]); + } + else { + $profiles[$name] = array(); + } // If no tasks have failed, save available profiles -if ($all_tasks_succeeded){ -if (empty($available_profiles)){ -$available_profiles = $profiles[$name]; -} else { -$available_profiles = array_intersect_key($available_profiles, $profiles[$name]); -} -} -} + if ($all_tasks_succeeded) { + if (empty($available_profiles)) { + $available_profiles = $profiles[$name]; + } + else { + $available_profiles = array_intersect_key($available_profiles, $profiles[$name]); + } + } + } // If platform verification failed: -elseif ($task->task_status == HOSTING_TASK_ERROR) { -$completed = TRUE; -$all_tasks_succeeded = FALSE; -$available_profiles = array(); + elseif ($task->task_status == HOSTING_TASK_ERROR) { + $completed = TRUE; + $all_tasks_succeeded = FALSE; + $available_profiles = array(); -$error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); + $error = db_result(db_query("SELECT message FROM {hosting_task_log} WHERE nid = %d AND type = '%s' ORDER BY vid, lid", $task->nid, 'error')); -$row['version'] = array( -'data' => t('Platform verification failed: %error', array('%error' => $error)), -'colspan' => 2, -); + $row['version'] = array( + 'data' => t('Platform verification failed: %error', array('%error' => $error)), + 'colspan' => 2, + ); // @TODO: Get task log error message -} + } // If platform is still processing: -elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { -$completed = FALSE; -$row['version'] = '...'; -$row['profiles'] = '...'; -} + elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { + $completed = FALSE; + $row['version'] = '...'; + $row['profiles'] = '...'; + } // If a single task is not queued, $all_tasks_queued == FALSE -if ($task->task_status != HOSTING_TASK_QUEUED){ -$all_tasks_queued = FALSE; -} + if ($task->task_status != HOSTING_TASK_QUEUED) { + $all_tasks_queued = FALSE; + } // Add hosting task status. -$row['status'] = _hosting_parse_error_code($task->task_status); + $row['status'] = _hosting_parse_error_code($task->task_status); // Store rows for display -$rows[] = $row; -} // end foreach platform + $rows[] = $row; + } // end foreach platform // Output our table. -$form['platforms'] = array( -'#type' => 'markup', -'#value' => theme('table', $header, $rows), -); + $form['platforms'] = array( + '#type' => 'markup', + '#value' => theme('table', $header, $rows), + ); // Not completed means show all tasks are not completed (or errored) -if (!$completed){ -$project->no_finish = TRUE; -$note = '

    ' . t('Please wait while we clone your repo and verify your drupal code.') . '

    '; - -$form['help'] = array( -'#type' => 'markup', -'#value' => $note, -); - -devshop_form_reloader($form, 'platform'); -return $form; -} + if (!$completed) { + $project->no_finish = TRUE; + $note = '

    ' . t('Please wait while we clone your repo and verify your drupal code.') . '

    '; + + $form['help'] = array( + '#type' => 'markup', + '#value' => $note, + ); + + devshop_form_reloader($form, 'platform'); + return $form; + } // If no available profiles: -elseif (count($available_profiles) == 0) { -$project->no_finish = TRUE; -$note = '

    ' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

    '; - -$form['error'] = array( -'#type' => 'markup', -'#value' => $note, -); -return $form; -} else { -$project->no_finish = FALSE; + elseif (count($available_profiles) == 0) { + $project->no_finish = TRUE; + $note = '

    ' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

    '; + + $form['error'] = array( + '#type' => 'markup', + '#value' => $note, + ); + return $form; + } + else { + $project->no_finish = FALSE; // Install Profile // Sensible default? // Lets go with standard for now... we can update later. -if (isset($available_profiles['standard'])) { -$default_profile = 'standard'; -} + if (isset($available_profiles['standard'])) { + $default_profile = 'standard'; + } // If 'drupal' profile exists, it is likely drupal6! -elseif (isset($available_profiles['drupal'])) { -$default_profile = 'drupal'; -} + elseif (isset($available_profiles['drupal'])) { + $default_profile = 'drupal'; + } -$form['install_profile'] = array( -'#type' => 'radios', -'#options' => $available_profiles, -'#title' => t('Project Install Profile'), -'#required' => 1, -'#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), -'#default_value' => $default_profile, -); -} + $form['install_profile'] = array( + '#type' => 'radios', + '#options' => $available_profiles, + '#title' => t('Project Install Profile'), + '#required' => 1, + '#description' => t('All sites in your project must use the same installation profile, and it must exist in all branches. Choose the installation profile for this project.'), + '#default_value' => $default_profile, + ); + } } /** -* STEP 4: Validate -*/ + * STEP 4: Validate + */ function devshop_project_create_step_sites_validate(&$from, &$form_state) { -$project = &$form_state['project']; + $project = &$form_state['project']; -if (empty($form_state['values']['install_profile'])){ -form_set_error('install_profile', t('You must choose an install profile')); -} + if (empty($form_state['values']['install_profile'])) { + form_set_error('install_profile', t('You must choose an install profile')); + } } /** -* STEP 4: Submit -* -* Save install profile to the project object (project and site node creation -* happens on wizard finish.) -*/ + * STEP 4: Submit + * + * Save install profile to the project object (project and site node creation + * happens on wizard finish.) + */ function devshop_project_create_step_sites_submit(&$from, &$form_state) { -$project = &$form_state['project']; -$project->install_profile = $form_state['values']['install_profile']; + $project = &$form_state['project']; + $project->install_profile = $form_state['values']['install_profile']; } From cdf5b89aa5d1ef85ac55f669fe5c117d1ab0469f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 5 Oct 2014 10:08:26 -0400 Subject: [PATCH 0986/3476] Indenting comments. --- devshop_projects/inc/create/step-4.inc | 41 +++++++++++++------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/devshop_projects/inc/create/step-4.inc b/devshop_projects/inc/create/step-4.inc index b7a17c6ae..0f8f3c5a9 100644 --- a/devshop_projects/inc/create/step-4.inc +++ b/devshop_projects/inc/create/step-4.inc @@ -21,7 +21,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { '#value' => $project_node->nid, ); -// Display the platforms + // Display the platforms $rows = array(); $header = array( t('Name'), @@ -35,31 +35,30 @@ function devshop_project_create_step_sites(&$form, &$form_state) { foreach ($project_node->project->environments as $name => $environment) { -// Get platform and latest verify task. + // Get platform and latest verify task. $platform_nid = $environment->platform; $platform = node_load($platform_nid); $task = hosting_get_most_recent_task($platform_nid, 'verify'); -// Build a table. + // Build a table. $row = array(); $row['name'] = $name; $row['branch'] = $environment->git_ref; $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS ? $platform->release->version : t('...'); -// If platform verified successfully: + // If platform verified successfully: if ($task->task_status == HOSTING_TASK_SUCCESS) { -// It's not really ready until we get a version. + // It's not really ready until we get a version. if (empty($platform->release->version)) { $completed = FALSE; $row['version'] = '...'; $row['profiles'] = '...'; } -// Collect install profiles + // Collect install profiles $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); - dsm($profiles_shortnames); if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) { $profiles[$name] = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid)); $row['profiles'] = implode(', ', $profiles[$name]); @@ -68,7 +67,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $profiles[$name] = array(); } -// If no tasks have failed, save available profiles + // If no tasks have failed, save available profiles if ($all_tasks_succeeded) { if (empty($available_profiles)) { $available_profiles = $profiles[$name]; @@ -78,7 +77,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { } } } -// If platform verification failed: + // If platform verification failed: elseif ($task->task_status == HOSTING_TASK_ERROR) { $completed = TRUE; $all_tasks_succeeded = FALSE; @@ -91,35 +90,35 @@ function devshop_project_create_step_sites(&$form, &$form_state) { 'colspan' => 2, ); -// @TODO: Get task log error message + // @TODO: Get task log error message } -// If platform is still processing: + // If platform is still processing: elseif ($task->task_status == HOSTING_TASK_PROCESSING || $task->task_status == HOSTING_TASK_QUEUED) { $completed = FALSE; $row['version'] = '...'; $row['profiles'] = '...'; } -// If a single task is not queued, $all_tasks_queued == FALSE + // If a single task is not queued, $all_tasks_queued == FALSE if ($task->task_status != HOSTING_TASK_QUEUED) { $all_tasks_queued = FALSE; } -// Add hosting task status. + // Add hosting task status. $row['status'] = _hosting_parse_error_code($task->task_status); -// Store rows for display + // Store rows for display $rows[] = $row; } // end foreach platform -// Output our table. + // Output our table. $form['platforms'] = array( '#type' => 'markup', '#value' => theme('table', $header, $rows), ); -// Not completed means show all tasks are not completed (or errored) + // Not completed means show all tasks are not completed (or errored) if (!$completed) { $project->no_finish = TRUE; $note = '

    ' . t('Please wait while we clone your repo and verify your drupal code.') . '

    '; @@ -132,7 +131,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { devshop_form_reloader($form, 'platform'); return $form; } -// If no available profiles: + // If no available profiles: elseif (count($available_profiles) == 0) { $project->no_finish = TRUE; $note = '

    ' . t('No common profile was found in all of your branches. Please check your source code and try again. You must !link to change what environment tracks each branch.', array('!link' => l(t('Go Back'), 'hosting/projects/add/environments'))) . '

    '; @@ -146,13 +145,13 @@ function devshop_project_create_step_sites(&$form, &$form_state) { else { $project->no_finish = FALSE; -// Install Profile -// Sensible default? -// Lets go with standard for now... we can update later. + // Install Profile + // Sensible default? + // Lets go with standard for now... we can update later. if (isset($available_profiles['standard'])) { $default_profile = 'standard'; } -// If 'drupal' profile exists, it is likely drupal6! + // If 'drupal' profile exists, it is likely drupal6! elseif (isset($available_profiles['drupal'])) { $default_profile = 'drupal'; } From c4879aa9b86249ad8e9c4f44d2c1db14e9341de1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 5 Oct 2014 10:18:32 -0400 Subject: [PATCH 0987/3476] Fixing create wizard: new Task status was throwing it off. --- devshop_projects/inc/create/step-4.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create/step-4.inc b/devshop_projects/inc/create/step-4.inc index 0f8f3c5a9..3e0a0321b 100644 --- a/devshop_projects/inc/create/step-4.inc +++ b/devshop_projects/inc/create/step-4.inc @@ -44,10 +44,10 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $row = array(); $row['name'] = $name; $row['branch'] = $environment->git_ref; - $row['version'] = $task->task_status == HOSTING_TASK_SUCCESS ? $platform->release->version : t('...'); + $row['version'] = t('...'); // If platform verified successfully: - if ($task->task_status == HOSTING_TASK_SUCCESS) { + if ($task->task_status == HOSTING_TASK_SUCCESS || $task->task_status == HOSTING_TASK_WARNING) { // It's not really ready until we get a version. if (empty($platform->release->version)) { @@ -55,6 +55,9 @@ function devshop_project_create_step_sites(&$form, &$form_state) { $row['version'] = '...'; $row['profiles'] = '...'; } + else { + $row['version'] = $platform->release->version; + } // Collect install profiles $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name'); From 4ba20e3e35dda2d7aa1b4866589264e3cbbab57b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 15:12:43 -0500 Subject: [PATCH 0988/3476] Set module weight higher than views. --- devshop_hosting.install | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_hosting.install b/devshop_hosting.install index 5bbc88bcc..36fa322aa 100644 --- a/devshop_hosting.install +++ b/devshop_hosting.install @@ -8,4 +8,15 @@ function devshop_hosting_install(){ variable_set('site_frontpage', 'devshop'); drupal_set_message(t('Site frontpage set to devshop.')); + + db_query('UPDATE {system} SET weight = 11 WHERE name = "devshop_hosting"'); } + +/** + * Set weight of this module higher than views. + */ +function devshop_hosting_update_7001() { + $ret = array(); + $ret[] = update_sql('UPDATE {system} SET weight = 11 WHERE name = "devshop_hosting"'); + return $ret; +} \ No newline at end of file From 761b84d20c40ad3574bb1a99fee6bff7ccae1ea1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 15:12:56 -0500 Subject: [PATCH 0989/3476] remove clients menu and try to rearrange primary links. --- devshop_hosting.module | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_hosting.module b/devshop_hosting.module index 5af96104b..491da8f04 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -59,6 +59,7 @@ function devshop_hosting_menu_alter(&$items){ $items['user/password']['type'] = MENU_CALLBACK; unset($items['hosting/sites']); unset($items['hosting/platforms']); + unset($items['hosting/clients']); $items['hosting/servers/add'] = $items['node/add/server']; $items['hosting/servers/add']['title'] = t('Add new Server'); @@ -72,6 +73,11 @@ function devshop_hosting_menu_alter(&$items){ ); $items['node/%hosting_site_node/goto_site']['page callback'] = 'devshop_hosting_site_goto'; + + // Order primary links + $items['hosting/projects']['weight'] = 1; + $items['hosting/servers']['weight'] = 2; + $items['logout']['weight'] = 3; } /** From dae40836abbbcc787dacdd3c78bf3a67927e9ddf Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 15:15:59 -0500 Subject: [PATCH 0990/3476] No need to do this, the weights are already what we want. They are just being ignored. --- devshop_hosting.module | 4 ---- 1 file changed, 4 deletions(-) diff --git a/devshop_hosting.module b/devshop_hosting.module index 491da8f04..5031a104d 100644 --- a/devshop_hosting.module +++ b/devshop_hosting.module @@ -74,10 +74,6 @@ function devshop_hosting_menu_alter(&$items){ $items['node/%hosting_site_node/goto_site']['page callback'] = 'devshop_hosting_site_goto'; - // Order primary links - $items['hosting/projects']['weight'] = 1; - $items['hosting/servers']['weight'] = 2; - $items['logout']['weight'] = 3; } /** From 9698137636c791e5491cce93511e2306b56ff7fe Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 16:36:07 -0500 Subject: [PATCH 0991/3476] New display for URLs. --- devshop_projects/inc/nodes.inc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 503deae77..326ce6b70 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -55,9 +55,12 @@ function devshop_projects_load($node) { p.status as platform_status, http.title as web_server, db.title as db_server, - n.title as project_name + n.title as project_name, + s.vid, + sn.title as default_domain FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_site} s ON e.site = s.nid + LEFT JOIN {node} sn ON s.vid = sn.vid LEFT JOIN {hosting_platform} p ON e.platform = p.nid LEFT JOIN {node} http ON p.web_server = http.nid LEFT JOIN {node} db ON s.db_server = db.nid @@ -76,8 +79,9 @@ function devshop_projects_load($node) { $environment->settings = (object) unserialize($environment->settings); // Environments URL: - // @TODO: Use aegir generated link - $environment->url = 'http://' . $environment->name . '.' . $project->name . '.' . $_SERVER['HTTP_HOST']; + // @TODO: Check for http or https. + // @TODO: Load a login URL if available. + $environment->url = 'http://' . $environment->default_domain; $environment->git_ref_type = $project->settings->git['refs'][$environment->git_ref]; // Environment version @@ -106,6 +110,10 @@ function devshop_projects_load($node) { $environment->git_current = t('Cloning in progress...'); } + // Get Domain aliases + $environment->domains = hosting_alias_get_aliases($environment); + array_unshift($environment->domains, $environment->default_domain); + // Save to project environments collection. $environments[$environment->name] = $environment; } From 032867812c693fcee59dbebf149d63c556d27f10 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 20:16:29 -0500 Subject: [PATCH 0992/3476] Improving descriptions of live domain settings. Adding note about missing functionality. --- devshop_projects/inc/forms.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index de970dfdd..bc0931de4 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -375,6 +375,7 @@ function devshop_projects_form(&$node) { $form['project']['settings']['live'] = array( '#type' => 'fieldset', '#title' => t('Domain Name Settings'), + '#description' => 'NOTE: Automatic domain setup is not yet functional. Edit domains in the Environment settings.' ); // Live Domain @@ -399,7 +400,7 @@ function devshop_projects_form(&$node) { $form['project']['settings']['live']['live_environment'] = array( '#type' => 'select', '#title' => t('Live environment'), - '#description' => t('The environment for the live website.'), + '#description' => t('The environment for the live website. The Live Domain will be saved to the live environment automatically.'), '#options' => $environments_options, '#default_value' => $project->settings->live['live_environment'], ); @@ -407,8 +408,8 @@ function devshop_projects_form(&$node) { // Use www $form['project']['settings']['live']['live_domain_www'] = array( '#type' => 'checkbox', - '#title' => t('Add www as a ServerAlias for Live Environment'), - '#description' => t('Access the site at http://livedomain.com and http://www.livedomain.com'), + '#title' => t('Add a Domain Alias with "www" to the Live Environment.'), + '#description' => t('Access the live site at http://example.com and http://www.example.com.'), '#default_value' => $project->settings->live['live_domain_www'], ); From 80b035365383ac96858ed1756f712bd81076eda6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 20:59:38 -0500 Subject: [PATCH 0993/3476] Removing commented code. --- devshop_projects/inc/nodes.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 326ce6b70..7d02dd7bd 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -238,9 +238,6 @@ function devshop_projects_update($node) { // Save Environment records. if (!empty($project->environments )) { - // Delete existing environment records - // db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $node->nid); - // Save each environment foreach ($project->environments as $name => $environment){ // Ensure correct data types From 3c82b9cdeae6cb7e7a4c85b7707aadc96204c98e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 21:51:51 -0500 Subject: [PATCH 0994/3476] On site create, save domain aliases. --- devshop_projects/inc/nodes.inc | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 7d02dd7bd..ecfcee235 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -305,4 +305,46 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $additions['environment'] = $project_node->project->environments[$result->name]; return $additions; } + + // When creating a site, look for project live domain setting, and + // "environment subdomains" setting to save domain aliases. + if ($op == 'insert' && $node->type == 'site') { + + $result = db_fetch_object(db_query(' + SELECT p.settings, e.name AS environment FROM {hosting_devshop_project} p + LEFT JOIN {hosting_devshop_project_environment} e ON p.nid = e.project_nid + WHERE e.site = %d + ', $node->nid)); + + $settings = unserialize($result->settings); + $environment = $result->environment; + + // Bail out if no environment name found. + if (empty($result->name)) { + return; + } + + $aliases = array(); + + // If site is the live site for an environment + if ($settings->live->live_environment == $result->name) { + // Save alias + $aliases[] = $settings->live->live_environment; + + // If "www" alias is enabled + $aliases[] = 'www.' . $settings->live->live_environment; + } + + // If environment subdomains are enabled... + if ($settings->live->environment_aliases) { + $aliases[] = $environment . "." . $settings->live->live_environment; + } + } + + // Save aliases if the are allowed. + foreach ($aliases as $alias) { + if (hosting_domain_allowed($alias, array('nid' => $node->nid)) && $alias != $node->title) { + db_query("INSERT INTO {hosting_site_alias} (vid, nid, alias, automatic, redirection) VALUES (%d, %d, '%s', %d, '%s')", $node->vid, $node->nid, $alias, HOSTING_ALIAS_CUSTOM, ''); + } + } } From 4ecdadd5adb881c47315db9148392b8b96f18133 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 21:53:59 -0500 Subject: [PATCH 0995/3476] On site insert, the site can never be the live environment, because it doesn't exist yet, it can not be set as the live environment. --- devshop_projects/inc/nodes.inc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index ecfcee235..208d888f3 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -326,15 +326,6 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $aliases = array(); - // If site is the live site for an environment - if ($settings->live->live_environment == $result->name) { - // Save alias - $aliases[] = $settings->live->live_environment; - - // If "www" alias is enabled - $aliases[] = 'www.' . $settings->live->live_environment; - } - // If environment subdomains are enabled... if ($settings->live->environment_aliases) { $aliases[] = $environment . "." . $settings->live->live_environment; From f673346bc2eb4caef05e1bece5eb85383508eb88 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 22:24:28 -0500 Subject: [PATCH 0996/3476] Removing hook_devshop_project_settings! --- devshop_log/devshop_log.module | 24 -------------- devshop_projects/devshop_projects.api.php | 28 ---------------- devshop_projects/devshop_projects.module | 39 +---------------------- devshop_projects/inc/create/step-3.inc | 37 +++++++++++---------- 4 files changed, 22 insertions(+), 106 deletions(-) diff --git a/devshop_log/devshop_log.module b/devshop_log/devshop_log.module index f8ce81d2c..30df2824f 100644 --- a/devshop_log/devshop_log.module +++ b/devshop_log/devshop_log.module @@ -15,30 +15,6 @@ function devshop_log_perm() { ); } -/** - * Implements hook_devshop_project_settings() - * - * Exposes hosting_logs settings to project settings. - */ -function devshop_log_devshop_project_settings($project_node = NULL){ - if (module_exists('hosting_logs')){ - return array( - 'logs_enabled' => array( - '#title' => '', - '#node_type' => 'site', - '#type' => 'hidden', - '#value' => TRUE, - ), - 'logs_available' => array( - '#title' => t('Visible Error Logs'), - '#node_type' => 'site', - '#type' => 'checkbox', - '#description' => t('Make error logs available at http://mysite/error.log'), - ), - ); - } -} - /** * Implementation of hook_menu() */ diff --git a/devshop_projects/devshop_projects.api.php b/devshop_projects/devshop_projects.api.php index 89db5eef2..52ec1f285 100644 --- a/devshop_projects/devshop_projects.api.php +++ b/devshop_projects/devshop_projects.api.php @@ -5,31 +5,3 @@ * Example functions for interacting with devshop. */ - /** - * Implementation of hook_devshop_project_settings() - * - * Provides a settings form for every environment within a project settings page. - * This allows you to save any property to site or platform nodes automatically. - * By using the #node_type property, the chosen value will be saved to the - * right node of the "environment", either site or platform. - * - * @param $project_node - * - * To use this hook, simply provide an array where the keys match a property - * of the site or platform node, to be saved when the project's settings form - * is submitted. - * - * - * - */ -function hook_devshop_project_settings($node = NULL){ - $branch_options = array_combine((array) $node->project->git->branches, (array) $node->project->git->branches); - return array( - 'git_branch' => array( - '#title' => t('Git Branch'), - '#node_type' => 'platform', - '#type' => 'select', - '#options' => $branch_options, - ), - ); -} diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9a9b04244..de3e5511d 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -203,44 +203,6 @@ function devshop_project_environment_settings_page($project_node, $environment){ return drupal_get_form($site_node->type . '_node_form', $site_node); } - -/** - * Implements hook_devshop_project_settings - */ -function devshop_projects_devshop_project_settings($node = NULL){ - $project = $node->project; - - $settings = array(); - - $http_servers = hosting_get_servers('http'); - if (count($http_servers)) { - $settings['web_server'] = array( - '#title' => t('Web server'), - '#node_type' => 'platform', - '#type' => 'select', - '#options' => $http_servers, - ); - } - - $db_servers = hosting_get_servers('db'); - if (count($db_servers)) { - $settings['db_server'] = array( - '#title' => t('Database server'), - '#node_type' => 'site', - '#type' => 'select', - '#options' => $db_servers, - ); - } - - $settings['production_mode'] = array( - '#title' => t('Production Mode'), - '#type' => 'checkbox', - '#default_value' => 0, - '#description' => t('Prevent overwriting of data, disabling, and other interruptive actions.'), - ); - return $settings; -} - /** * Page Callback for hosting/projects/add */ @@ -255,6 +217,7 @@ function devshop_projects_add($step = NULL){ } return devshop_projects_create_wizard($step); } + /** * Replacement for hosting_task_confirm_form() * diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index 2bc384fe9..9eb6eeefe 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -35,7 +35,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { $path = drupal_get_path('module', 'devshop_projects') . '/inc/environments.js'; drupal_add_js($path); - $settings = module_invoke_all('devshop_project_settings', $project_node); $form['nid'] = array( '#type' => 'value', '#value' => $project_node->nid, @@ -97,16 +96,29 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#default_value' => $environment->git_ref, ); - // Environment settings - // Add environment settings form elements - foreach ($settings as $setting_id => $setting) { - $form['project']['environments'][$name]['settings'][$setting_id] = $setting; - $form['project']['environments'][$name]['settings'][$setting_id]['#default_value'] = $environment->settings->{$setting_id}; - $form['project']['environments'][$name]['settings'][$setting_id]['#attributes']['title'] = $setting['#description']; - $form['project']['environments'][$name]['settings'][$setting_id]['#description'] = ''; + // HTTP Server select. + $http_servers = hosting_get_servers('http'); + if (count($http_servers)) { + $form['project']['environments'][$name]['settings']['web_server'] = array( + '#title' => t('Web server'), + '#node_type' => 'platform', + '#type' => 'select', + '#options' => $http_servers, + ); } -//Now add button. + // DB Server select. + $db_servers = hosting_get_servers('db'); + if (count($db_servers)) { + $form['project']['environments'][$name]['settings']['db_server'] = array( + '#title' => t('Database server'), + '#node_type' => 'site', + '#type' => 'select', + '#options' => $db_servers, + ); + } + + // Add Environment $form['add_environment'] = array( '#type' => 'submit', '#value' => t('Add environment'), @@ -173,8 +185,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { $project = $form_state['project']; $project_node = node_load($project->project_nid); - $settings = module_invoke_all('devshop_project_settings', $project_node); - // Create these platforms, if they don't exist yet foreach ($form_state['values']['project']['environments'] as $name => $environment) { @@ -205,11 +215,6 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { // Other attributes $platform->web_server = $environment->settings['web_server']; - foreach ($settings as $setting_name => $element) { - if ($element['#node_type'] == 'platform') { - $platform->{$setting_name} = $environment->settings[$setting_name]; - } - } $platform_node = _devshop_projects_node_create('platform', $platform); } $environment->platform = $platform_node->nid; From 9fe35add49afb91213a8821bf3cabc91ff00f385 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 22:55:21 -0500 Subject: [PATCH 0997/3476] Adding code to handle updating all the domains for a project when the live domain settings changes. --- devshop_projects/inc/nodes.inc | 106 ++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 29 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 208d888f3..54e62db2c 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -236,39 +236,73 @@ function devshop_projects_update($node) { // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); - // Save Environment records. - if (!empty($project->environments )) { - // Save each environment - foreach ($project->environments as $name => $environment){ - // Ensure correct data types - $environment = (object) $environment; - $environment->name = $name; - $environment->settings = (array) $environment->settings; + // Update the project environments domain aliases based on the live settings. + devshop_project_update_domains($node); +} - $info = new stdClass(); - $info->project_nid = $node->nid; - $info->name = $name; - $info->git_ref = $environment->git_ref; - $info->site = $environment->site; - $info->platform = $environment->platform; +/** + * Function for updating a project's alias + */ +function devshop_project_update_domains($project_node) { + $project = $project_node->project; + $old_project = $project_node->old->project; - // Remove primary settings from settings array before saving. - // @TODO: These values shouldn't show up anymore. Once tested, remove. - unset($environment->settings['git_ref']); - unset($environment->settings['site']); - unset($environment->settings['platform']); - $info->settings = serialize($environment->settings); + // If there is a live environment, AND live domain was entered: + if ($project->settings->live['live_environment'] && $project->settings->live['live_domain']) { + + // ...and if the Live Domain changed: + if ($project->settings->live['live_domain'] != $old_project->settings->live['live_domain']) { - // Detect existing environment record - if (db_result(db_query('SELECT name FROM {hosting_devshop_project_environment} WHERE project_nid = %d AND name = "%s"', $node->nid, $environment->name))) { - $update = array('project_nid', 'name'); + // Save live domain alias to the live environment + $live_environment = $project->settings->live['live_environment']; + $live_site_node = node_load($project->environment[$live_environment]->site); + devshop_hosting_save_domain($project->settings->live['live_domain'], $live_site_node); + + // Save live domain alias with www prefix. + if ($project->settings->live['live_domain_www']) { + devshop_hosting_save_domain('www.' . $project->settings->live['live_domain'], $live_site_node); } + + // Delete all the old live domain aliases for all environments, and save new ones. + $project_node_loaded = node_load($project_node->nid); + foreach ($project_node_loaded->project['environments'] as $name => $environment) { + $site_node = node_load($project->environment[$live_environment]->site); + + devshop_hosting_delete_domain($name . "." . $project->settings->live['live_domain'], $site_node); + devshop_hosting_save_domain($name . "." . $project->settings->live['live_domain'], $site_node); + } + } + + // ... or if the Live Environment changed. + if ($project->settings->live['live_environment'] != $old_project->settings->live['live_environment']) { + $live_environment = $project->settings->live['live_environment']; + $live_site_node = node_load($project->environment[$live_environment]->site); + + $old_live_environment = $old_project->settings->live['live_environment']; + $old_live_site_node = node_load($project->environment[$old_live_environment]->site); + + // If the live domain also changed, delete the old domain from the old environment). + if ($project->settings->live['live_domain'] != $old_project->settings->live['live_domain']) { + devshop_hosting_delete_domain($old_project->settings->live['live_domain'], $old_live_site_node); + + // If project had www aliases, delete that alias as well. + if ($old_project->settings->live['live_domain_www']) { + devshop_hosting_delete_domain("www." . $old_project->settings->live['live_domain'], $old_live_site_node); + } + } + // ... if the live domain did not change, delete the current live domain alias. else { - $update = array(); + devshop_hosting_delete_domain($project->settings->live['live_domain'], $live_site_node); + if ($old_project->settings->live['live_domain_www']) { + devshop_hosting_delete_domain("www." . $old_project->settings->live['live_domain'], $live_site_node); + } } - // Save environment record. - drupal_write_record('hosting_devshop_project_environment', $info, $update); + // Save the domain aliases to the new live environment + devshop_hosting_save_domain($project->settings->live['live_domain'], $live_site_node); + if ($project->settings->live['live_domain_www']) { + devshop_hosting_save_domain("www." . $project->settings->live['live_domain'], $live_site_node); + } } } } @@ -334,8 +368,22 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { // Save aliases if the are allowed. foreach ($aliases as $alias) { - if (hosting_domain_allowed($alias, array('nid' => $node->nid)) && $alias != $node->title) { - db_query("INSERT INTO {hosting_site_alias} (vid, nid, alias, automatic, redirection) VALUES (%d, %d, '%s', %d, '%s')", $node->vid, $node->nid, $alias, HOSTING_ALIAS_CUSTOM, ''); - } + devshop_hosting_save_domain($alias, $node); + } +} + +/** + * Helper for saving a site domain for a site, includes the allow check. + */ +function devshop_hosting_save_domain($alias, $site) { + if (hosting_domain_allowed($alias, array('nid' => $site->nid)) && $alias != $site->title) { + db_query("INSERT INTO {hosting_site_alias} (vid, nid, alias, automatic, redirection) VALUES (%d, %d, '%s', %d, '%s')", $site->vid, $site->nid, $alias, HOSTING_ALIAS_CUSTOM, ''); } } + +/** + * Helper for deleting a site domain for a site, includes the allow check. + */ +function devshop_hosting_delete_domain($alias, $site) { + db_query("DELETE FROM {hosting_site_alias} WHERE vid = %d AND alias = '%s';", $site->vid, $alias); +} From 18951ce9c10b18320776f4d5936a06a4004bd46a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 22:58:30 -0500 Subject: [PATCH 0998/3476] Wrong position of foreach. --- devshop_projects/inc/nodes.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 54e62db2c..73b2fbaca 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -364,11 +364,11 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { if ($settings->live->environment_aliases) { $aliases[] = $environment . "." . $settings->live->live_environment; } - } - // Save aliases if the are allowed. - foreach ($aliases as $alias) { - devshop_hosting_save_domain($alias, $node); + // Save aliases if the are allowed. + foreach ($aliases as $alias) { + devshop_hosting_save_domain($alias, $node); + } } } From 19dbd7f98d8504acf3b129a43dcdc65d8a132ab8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 23 Nov 2014 23:12:36 -0500 Subject: [PATCH 0999/3476] Form label cleanup. --- devshop_projects/inc/create/step-3.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index 9eb6eeefe..c448e6c93 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -90,7 +90,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#value' => $environment->platform, ); $form['project']['environments'][$name]['git_ref'] = array( - '#title' => t('Git Branch/Tag'), '#type' => 'select', '#options' => devshop_projects_git_ref_options($project), '#default_value' => $environment->git_ref, @@ -101,7 +100,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { if (count($http_servers)) { $form['project']['environments'][$name]['settings']['web_server'] = array( '#title' => t('Web server'), - '#node_type' => 'platform', '#type' => 'select', '#options' => $http_servers, ); @@ -112,7 +110,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { if (count($db_servers)) { $form['project']['environments'][$name]['settings']['db_server'] = array( '#title' => t('Database server'), - '#node_type' => 'site', '#type' => 'select', '#options' => $db_servers, ); From 1fa07c77e6df4a9531c277ee792aef1d5ad41af2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 00:26:58 -0500 Subject: [PATCH 1000/3476] Refactoring create project environments form to work without hook_devshop_project_settings(); --- devshop_projects/inc/create/create.inc | 19 ---- devshop_projects/inc/create/step-3.inc | 116 ++++++++++++++----------- devshop_projects/inc/create/step-4.inc | 1 - devshop_projects/inc/nodes.inc | 6 +- 4 files changed, 70 insertions(+), 72 deletions(-) diff --git a/devshop_projects/inc/create/create.inc b/devshop_projects/inc/create/create.inc index bdad7c77a..4629b550f 100644 --- a/devshop_projects/inc/create/create.inc +++ b/devshop_projects/inc/create/create.inc @@ -270,22 +270,3 @@ function devshop_form_reloader(&$form, $type = 'platform'){ drupal_add_js($settings, 'setting'); drupal_add_js(drupal_get_path('module','devshop_projects') . '/inc/reload.js'); } - -/** - * Functionality for add a new environment. - */ -function devshop_projects_create_wizard_add_new_environment($form, &$form_state) { - - // Load project node - $project_node = node_load($form_state['values']['nid']); - $environments = $form_state['values']['project']['environments']; - unset($environments['NEW']); - - // Set environments and no_verify - $project_node->project->environments = $environments; - $project_node->no_verify = TRUE; - node_save($project_node); - - // Go back to the same page. - drupal_goto('hosting/projects/add/environments'); -} diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index c448e6c93..f5ec88457 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -76,7 +76,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#maxlength' => 64, '#attributes' => array( 'placeholder' => t('name'), - 'autofocus' => 'autofocus', ), '#field_prefix' => 'http://', '#field_suffix' => "." . $project->base_url, @@ -102,6 +101,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#title' => t('Web server'), '#type' => 'select', '#options' => $http_servers, + '#default_value' => $environment->settings->http_server, ); } @@ -112,6 +112,7 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#title' => t('Database server'), '#type' => 'select', '#options' => $db_servers, + '#default_value' => $environment->settings->db_server, ); } @@ -120,11 +121,12 @@ function devshop_project_create_step_environments(&$form, &$form_state) { '#type' => 'submit', '#value' => t('Add environment'), '#name' => 'add_environment', - '#submit' => array('devshop_projects_create_wizard_add_new_environment'), + '#submit' => array('devshop_projects_save_environments'), '#prefix' => '
    ', '#suffix' => '
    ', ); } + $form['project']['environments']['NEW']['name']['#attributes']['autofocus'] = 'autofocus'; } @@ -136,26 +138,9 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) $values = &$form_state['values']; -// Changes NEW environment data to $title - if ($values['project']['environments']['NEW']['name']) { - $new_env = $values['project']['environments']['NEW']['name']; - $new_env_settings = $values['project']['environments']['NEW']; - $values['project']['environments'][$new_env] = $new_env_settings; - -// Create the next NEW environment. Unset makes sure its always last. - unset($values['project']['environments']['NEW']); - -// If "add environment" button clicked, add another row. - if ($form_state['clicked_button']['#name'] == 'add_environment') { - $values['project']['environments']['NEW'] = array(); - } - } else { - unset($values['project']['environments']['NEW']); - } - -// Check environment titles + // Check environment titles foreach ($values['project']['environments'] as $env => $env_settings) { -// Check for illegal chars + // Check for illegal chars if ($env != 'NEW' && !empty($env_settings['name'])) { if (!preg_match('!^[a-z0-9_]+$!', $env_settings['name'])) { $form_item = 'environments][' . $env . '][name'; @@ -164,7 +149,7 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) } } -// Reject if empty + // Reject if empty if (count($values['project']['environments']) < 1) { if ($form_state['clicked_button']['#name'] == 'add_environment') { form_set_error('project][environments][NEW][name', t('Name this environment before you add another.')); @@ -174,18 +159,50 @@ function devshop_project_create_step_environments_validate(&$form, &$form_state) } } +/** + * Functionality for add a new environment. + */ +function devshop_projects_save_environments($form, &$form_state) { + + $environments = $form_state['values']['project']['environments']; + + if (!empty($environments['NEW']['name'])) { + $new_environment_name = $environments['NEW']['name']; + $environments[$new_environment_name] = $environments['NEW']; + } + unset($environments['NEW']); + + // Delete all environments + db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d', $form_state['values']['nid']); + + // Create all environments + foreach ($environments as $environment) { + $name = trim($environment['name']); + if (!empty($name)) { + dsm(db_query('INSERT INTO {hosting_devshop_project_environment} (project_nid, name, git_ref, platform, settings) VALUES (%d, "%s", "%s", %d, "%s");', $form_state['values']['nid'], $environment['name'], $environment['git_ref'], $environment['platform'], serialize($environment['settings'])), 'insert ok?'); + } + } + + // Go back to the same page. + if ($form_state['clicked_button']['#name'] == 'add_environment') { + drupal_goto('hosting/projects/add/environments'); + } +} + /** * STEP 3: SUBMIT */ function devshop_project_create_step_environments_submit(&$form, &$form_state) { + + // Save all environments. + devshop_projects_save_environments($form, $form_state); + // Get project and reset properties.. - $project = $form_state['project']; - $project_node = node_load($project->project_nid); + $project_node = node_load($form_state['values']['nid'], NULL, TRUE); + $project = $project_node->project; // Create these platforms, if they don't exist yet - foreach ($form_state['values']['project']['environments'] as $name => $environment) { - - $environment = (object) $environment; + foreach ($project->environments as $name => $environment) { // If platform exists, it's because user went back in the wizard. $platform_nid = $project->environments[$name]->platform; @@ -194,6 +211,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { if ($platform_nid) { // @TODO: Apply settings and re-save platform. hosting_add_task($platform_nid, 'verify'); + $environment->platform = $platform_nid; } // If platform hasn't been created yet, do so now! @@ -211,33 +229,29 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } // Other attributes - $platform->web_server = $environment->settings['web_server']; + $platform->web_server = $environment->settings->web_server; $platform_node = _devshop_projects_node_create('platform', $platform); + $environment->platform = $platform_node->nid; } - $environment->platform = $platform_node->nid; - $project->environments[$name] = (object) $environment; - } - - // Somehow, NEW environment still appears! - unset($project_node->project->environments['NEW']); - - // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($project_node->project->environments, $form_state['values']['project']['environments']); - foreach ($removed_environments as $environment_name => $settings) { - // @TODO: Determine what to do here based on task status... - // if verify task hasn't even run yet (and has never run) we can just delete - // the platform node. - - // Create 'delete' task for the removed platform - $platform_nid = $settings['platform']; - if ($platform_nid) { - hosting_add_task($platform_nid, 'delete'); - } + // Update environment with our platform + db_query('UPDATE {hosting_devshop_project_environment} SET platform = %d WHERE project_nid = %d AND name = %d', $environment->platform, $form_state['values']['nid'], $environment->name); } - // Save the project node but don't verify. This triggers environment saving. - $project_node->project = $project; - $project_node->no_verify = TRUE; - node_save($project_node); + // For all removed platforms, trigger a delete task + $removed_environments = array_diff_key($project->environments, $form_state['values']['project']['environments']); + +// dsm($removed_environments, 'removed_envs'); +// +// foreach ($removed_environments as $environment_name => $settings) { +// // @TODO: Determine what to do here based on task status... +// // if verify task hasn't even run yet (and has never run) we can just delete +// // the platform node. +// +// // Create 'delete' task for the removed platform +// $platform_nid = $settings['platform']; +// if ($platform_nid) { +// hosting_add_task($platform_nid, 'delete'); +// } +// } } \ No newline at end of file diff --git a/devshop_projects/inc/create/step-4.inc b/devshop_projects/inc/create/step-4.inc index 3e0a0321b..53db307a3 100644 --- a/devshop_projects/inc/create/step-4.inc +++ b/devshop_projects/inc/create/step-4.inc @@ -32,7 +32,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { ); $all_tasks_queued = TRUE; $all_tasks_succeeded = TRUE; - foreach ($project_node->project->environments as $name => $environment) { // Get platform and latest verify task. diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 73b2fbaca..300a4b4b2 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -81,7 +81,11 @@ function devshop_projects_load($node) { // Environments URL: // @TODO: Check for http or https. // @TODO: Load a login URL if available. - $environment->url = 'http://' . $environment->default_domain; + if ($environment->default_domain) { + $environment->url = 'http://' . $environment->default_domain; + } else { + $environment->url = ''; + } $environment->git_ref_type = $project->settings->git['refs'][$environment->git_ref]; // Environment version From 73a6ab4ee97bab527ff7cbf9cf460de02712f1b4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 00:28:42 -0500 Subject: [PATCH 1001/3476] Fixing label on create environments form. --- devshop_projects/inc/create/step-3.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index f5ec88457..153fa969c 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -70,7 +70,6 @@ function devshop_project_create_step_environments(&$form, &$form_state) { // Environment properties $form['project']['environments'][$name]['name'] = array( '#type' => 'textfield', - '#title' => t('Environment Name'), '#default_value' => $env_title, '#size' => 6, '#maxlength' => 64, From 005c554c2ced08c552d37b2e8e60d2dac954e1be Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 00:35:38 -0500 Subject: [PATCH 1002/3476] Always load project from node. --- devshop_projects/inc/create/step-4.inc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/create/step-4.inc b/devshop_projects/inc/create/step-4.inc index 53db307a3..202f9af4a 100644 --- a/devshop_projects/inc/create/step-4.inc +++ b/devshop_projects/inc/create/step-4.inc @@ -9,8 +9,8 @@ * STEP 4: Form */ function devshop_project_create_step_sites(&$form, &$form_state) { - $project = &$form_state['project']; - $project_node = node_load($project->project_nid); + $project_node = node_load($form_state['project']->project_nid); + $project = $project_node->project; $profiles = array(); $available_profiles = array(); @@ -32,7 +32,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { ); $all_tasks_queued = TRUE; $all_tasks_succeeded = TRUE; - foreach ($project_node->project->environments as $name => $environment) { + foreach ($project->environments as $name => $environment) { // Get platform and latest verify task. $platform_nid = $environment->platform; @@ -173,8 +173,6 @@ function devshop_project_create_step_sites(&$form, &$form_state) { * STEP 4: Validate */ function devshop_project_create_step_sites_validate(&$from, &$form_state) { - $project = &$form_state['project']; - if (empty($form_state['values']['install_profile'])) { form_set_error('install_profile', t('You must choose an install profile')); } From 111804d42321915f0bea45d82b69fa813f03a335 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 00:36:36 -0500 Subject: [PATCH 1003/3476] removing dsm --- devshop_projects/inc/create/step-3.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index 153fa969c..4fc29fc04 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -178,7 +178,7 @@ function devshop_projects_save_environments($form, &$form_state) { foreach ($environments as $environment) { $name = trim($environment['name']); if (!empty($name)) { - dsm(db_query('INSERT INTO {hosting_devshop_project_environment} (project_nid, name, git_ref, platform, settings) VALUES (%d, "%s", "%s", %d, "%s");', $form_state['values']['nid'], $environment['name'], $environment['git_ref'], $environment['platform'], serialize($environment['settings'])), 'insert ok?'); + db_query('INSERT INTO {hosting_devshop_project_environment} (project_nid, name, git_ref, platform, settings) VALUES (%d, "%s", "%s", %d, "%s");', $form_state['values']['nid'], $environment['name'], $environment['git_ref'], $environment['platform'], serialize($environment['settings'])); } } From 1aa50e1544f57712def28eda14e32a5f1693124b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 00:51:50 -0500 Subject: [PATCH 1004/3476] Fixing name update hook. --- devshop_projects/inc/create/step-3.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index 4fc29fc04..9e91005a1 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -204,7 +204,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { foreach ($project->environments as $name => $environment) { // If platform exists, it's because user went back in the wizard. - $platform_nid = $project->environments[$name]->platform; + $platform_nid = $environment->platform; // If the platform already exists, update settings and verify it again. if ($platform_nid) { @@ -234,7 +234,7 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } // Update environment with our platform - db_query('UPDATE {hosting_devshop_project_environment} SET platform = %d WHERE project_nid = %d AND name = %d', $environment->platform, $form_state['values']['nid'], $environment->name); + db_query('UPDATE {hosting_devshop_project_environment} SET platform = %d WHERE project_nid = %d AND name = "%s"', $environment->platform, $form_state['values']['nid'], $environment->name); } // For all removed platforms, trigger a delete task From 863fe6ba24d7cf49e797898d8c12696bac8fe5fd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 00:54:37 -0500 Subject: [PATCH 1005/3476] Adding comment about removing environments. --- devshop_projects/inc/create/step-3.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/create/step-3.inc b/devshop_projects/inc/create/step-3.inc index 9e91005a1..27f220769 100644 --- a/devshop_projects/inc/create/step-3.inc +++ b/devshop_projects/inc/create/step-3.inc @@ -238,9 +238,8 @@ function devshop_project_create_step_environments_submit(&$form, &$form_state) { } // For all removed platforms, trigger a delete task - $removed_environments = array_diff_key($project->environments, $form_state['values']['project']['environments']); - -// dsm($removed_environments, 'removed_envs'); + // @TODO: Get this working again. +// $removed_environments = array_diff_key($project->environments, $form_state['values']['project']['environments']); // // foreach ($removed_environments as $environment_name => $settings) { // // @TODO: Determine what to do here based on task status... From 411b41230f0ae6fe61a94fb6990ddf899eda418e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 01:01:29 -0500 Subject: [PATCH 1006/3476] Just update the environment record, don't save the whole project. Saving a project does not save the environments anymore. --- devshop_projects/devshop_projects.module | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index de3e5511d..6645d29a7 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -351,12 +351,8 @@ function devshop_projects_create_site($project, $platform_node, $environment_nam node_save($node); } - // Once we have the nid, save the environment object. - $project->environments[$environment_name]->site = $node->nid; - $project_node = node_load($project->nid); - $project_node->project = $project; - $project_node->no_verify = TRUE; - node_save($project_node); + // Update environment with our site + db_query('UPDATE {hosting_devshop_project_environment} SET site = %d WHERE project_nid = %d AND name = "%s"', $node->nid, $project->nid, $environment_name); return $node; } From 4fd967f2f7e1a1b971eee2bf9de02de57a072074 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 01:30:18 -0500 Subject: [PATCH 1007/3476] Ensure bad environments with no names are loaded. --- devshop_projects/inc/nodes.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 300a4b4b2..30823d628 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -65,7 +65,8 @@ function devshop_projects_load($node) { LEFT JOIN {node} http ON p.web_server = http.nid LEFT JOIN {node} db ON s.db_server = db.nid LEFT JOIN {node} n ON e.project_nid = n.nid - WHERE project_nid = %d + WHERE project_nid = %d AND + e.name != '' ORDER BY name; ", $node->nid); $environments = array(); while ($environment = db_fetch_object($query)) { From 5aa673e4db588d0f22c1827bd02624bf24869e6b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 01:30:45 -0500 Subject: [PATCH 1008/3476] Directly insert environment record instead of saving project. --- devshop_projects/tasks/create.inc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 20efb670a..839809da9 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -205,6 +205,7 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ $source_environment = $project_node->project->environments[$fork_source]; $environment = new stdClass(); + $environment->name = $environment_name; $environment->git_ref = $branch; $environment->platform = $platform->nid; @@ -219,6 +220,7 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ else { // Next, add the environment record. $environment = new stdClass(); + $environment->name = $environment_name; $environment->git_ref = $branch; $environment->platform = $platform->nid; @@ -226,9 +228,6 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ $environment->settings = $settings; } - $project->environments[$environment_name] = $environment; + db_query('INSERT INTO {hosting_devshop_project_environment} (project_nid, name, git_ref, platform, settings) VALUES (%d, "%s", "%s", %d, "%s");', $project_node->nid, $environment->name, $environment->git_ref, $environment->platform, serialize($environment->settings)); - // Save the project node. - $project_node->project = $project; - node_save($project_node); } From 7a0ffbc2ce5cf5d522529eb2ded4caa8eaf1986f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 24 Nov 2014 11:31:28 -0500 Subject: [PATCH 1009/3476] Changing "production mode" to "locked" --- devshop_projects/inc/forms.inc | 6 +++--- devshop_projects/inc/ui.inc | 29 +++-------------------------- devshop_projects/tasks/sync.inc | 6 +++--- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index bc0931de4..936ee5d95 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -195,10 +195,10 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#title' => t('Environment Settings'), '#tree' => true, ); - $form['environment']['settings']['production_mode'] = array( + $form['environment']['settings']['locked'] = array( '#type' => 'checkbox', - '#title' => t('Production Mode'), - '#default_value' => $environment->settings->production_mode, + '#title' => t('Locked'), + '#default_value' => $environment->settings->locked, '#description' => t('Prevent devshop users from overwriting the database.'), ); diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 0c19923a2..1c51cefd4 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -180,7 +180,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // Environments $rows = array(); - $production_rows = array(); foreach ($project->environments as $env => $environment) { $site_installed = false; @@ -317,7 +316,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { // The actions we want $site_actions = array('flush_cache', 'login_reset', 'backup'); - if (!$environment->settings->production_mode){ + if (!$environment->settings->locked){ $site_actions[] = 'restore'; } @@ -328,7 +327,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { if ($environment->site_status == HOSTING_SITE_DISABLED){ $site_actions[] = 'enable'; $site_actions[] = 'delete'; - } elseif (!$environment->settings->production_mode){ + } elseif (!$environment->settings->locked){ $site_actions[] = 'disable'; } } @@ -358,13 +357,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $node->environment_actions[$environment->name] = $actions; $row[] = theme('ctools_dropdown', t('Actions'), $actions); - - if ($environment->settings->production_mode){ - $production_rows[] = $row; - } - else { - $rows[] = $row; - } + $rows[] = $row; // Environment Tasks if ($environment->site) { @@ -397,22 +390,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $header = array(); $table = theme('table', $header, $rows, array('class' => 'environments-table')); - // Show production environments separately. - if (count($production_rows)){ - $node->content['production_environments'] = array( - '#type' => 'fieldset', - '#title' => t('Production'), - '#weight' => 11, - '#attributes' => array( - 'class' => 'project-environments', - ), - ); - $node->content['production_environments']['table'] = array( - '#type' => 'item', - '#value' => theme('table', $header, $production_rows, array('class' => 'environments-table')), - ); - } - $node->content['environments'] = array( '#type' => 'fieldset', '#title' => t('Environments'), diff --git a/devshop_projects/tasks/sync.inc b/devshop_projects/tasks/sync.inc index 3e6827157..5b449e44c 100644 --- a/devshop_projects/tasks/sync.inc +++ b/devshop_projects/tasks/sync.inc @@ -23,15 +23,15 @@ function hosting_task_devshop_sync_form($node) { $form['destination']['#prefix'] = '
    '; $form['destination']['#suffix'] = '
    '; - // Look for any production mode sites. + // Look for any locked sites. foreach ($project->environments as $environment) { - if ($environment->settings->production_mode){ + if ($environment->settings->locked){ // Remove live site from destination options. unset($form['destination']['#options'][$environment->name]); $source_options = $form['source']['#options']; unset($source_options[$environment->name]); - $form['source']['#options'] = array_merge(array($environment->name => $environment->name . " (Production Mode)"), $source_options); + $form['source']['#options'] = array_merge(array($environment->name => $environment->name . " (Locked)"), $source_options); $form['source']['#default_value'] = $environment->name; } } From b970188bab29ae8853b4adfe73a55feba0da8568 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:12:11 -0500 Subject: [PATCH 1010/3476] Improving wording on forms for live domain settings, removing "www" option. --- devshop_projects/inc/forms.inc | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 936ee5d95..f28cb1e2c 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -378,15 +378,6 @@ function devshop_projects_form(&$node) { '#description' => 'NOTE: Automatic domain setup is not yet functional. Edit domains in the Environment settings.' ); - // Live Domain - $form['project']['settings']['live']['live_domain'] = array( - '#type' => 'textfield', - '#title' => t('Live domain'), - '#description' => t('The live domain for this project. Do not include "www".'), - '#size' => 40, - '#default_value' => $node->project->settings->live['live_domain'], - ); - // Live Environment $environments = array_keys($project->environments); if (empty($environments)){ @@ -400,24 +391,25 @@ function devshop_projects_form(&$node) { $form['project']['settings']['live']['live_environment'] = array( '#type' => 'select', '#title' => t('Live environment'), - '#description' => t('The environment for the live website. The Live Domain will be saved to the live environment automatically.'), + '#description' => t('The environment for the live website. This locks the live environment from overwrites and disabling, and allows syncing to other environments.'), '#options' => $environments_options, '#default_value' => $project->settings->live['live_environment'], ); - // Use www - $form['project']['settings']['live']['live_domain_www'] = array( - '#type' => 'checkbox', - '#title' => t('Add a Domain Alias with "www" to the Live Environment.'), - '#description' => t('Access the live site at http://example.com and http://www.example.com.'), - '#default_value' => $project->settings->live['live_domain_www'], + // Live Domain + $form['project']['settings']['live']['live_domain'] = array( + '#type' => 'textfield', + '#title' => t('Live Domain'), + '#description' => t('The live domain for this project. Do not include "www". Used only for links. You must still add the Domain to your live environment manually.'), + '#size' => 40, + '#default_value' => $node->project->settings->live['live_domain'], ); // Use aliases $form['project']['settings']['live']['environment_aliases'] = array( '#type' => 'checkbox', - '#title' => t('Environment Subdomains'), - '#description' => t('Create subdomains under the live domain for all environments.'), + '#title' => t('Create subdomains under Live Domain for new environments.'), + '#description' => t('When new environments are created, automatically add a domain name such as http://ENVIRONMENT.LIVEDOMAIN.com. Does not affect existing environments. Does not remove domains when disabled.'), '#default_value' => $project->settings->live['environment_aliases'], ); From adbc415e10315c642b989181dd467b206dedd61f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:13:35 -0500 Subject: [PATCH 1011/3476] Commenting out domain saving settings as they are not needed anymore. Live Domain settings only affect new environments. --- devshop_projects/inc/nodes.inc | 138 ++++++++++++++++----------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 30823d628..d9ec421df 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -240,77 +240,77 @@ function devshop_projects_update($node) { // Write project record. drupal_write_record('hosting_devshop_project', $info, 'nid'); - - // Update the project environments domain aliases based on the live settings. - devshop_project_update_domains($node); +// +// // Update the project environments domain aliases based on the live settings. +// devshop_project_update_domains($node); } -/** - * Function for updating a project's alias - */ -function devshop_project_update_domains($project_node) { - $project = $project_node->project; - $old_project = $project_node->old->project; - - // If there is a live environment, AND live domain was entered: - if ($project->settings->live['live_environment'] && $project->settings->live['live_domain']) { - - // ...and if the Live Domain changed: - if ($project->settings->live['live_domain'] != $old_project->settings->live['live_domain']) { - - // Save live domain alias to the live environment - $live_environment = $project->settings->live['live_environment']; - $live_site_node = node_load($project->environment[$live_environment]->site); - devshop_hosting_save_domain($project->settings->live['live_domain'], $live_site_node); - - // Save live domain alias with www prefix. - if ($project->settings->live['live_domain_www']) { - devshop_hosting_save_domain('www.' . $project->settings->live['live_domain'], $live_site_node); - } - - // Delete all the old live domain aliases for all environments, and save new ones. - $project_node_loaded = node_load($project_node->nid); - foreach ($project_node_loaded->project['environments'] as $name => $environment) { - $site_node = node_load($project->environment[$live_environment]->site); - - devshop_hosting_delete_domain($name . "." . $project->settings->live['live_domain'], $site_node); - devshop_hosting_save_domain($name . "." . $project->settings->live['live_domain'], $site_node); - } - } - - // ... or if the Live Environment changed. - if ($project->settings->live['live_environment'] != $old_project->settings->live['live_environment']) { - $live_environment = $project->settings->live['live_environment']; - $live_site_node = node_load($project->environment[$live_environment]->site); - - $old_live_environment = $old_project->settings->live['live_environment']; - $old_live_site_node = node_load($project->environment[$old_live_environment]->site); - - // If the live domain also changed, delete the old domain from the old environment). - if ($project->settings->live['live_domain'] != $old_project->settings->live['live_domain']) { - devshop_hosting_delete_domain($old_project->settings->live['live_domain'], $old_live_site_node); - - // If project had www aliases, delete that alias as well. - if ($old_project->settings->live['live_domain_www']) { - devshop_hosting_delete_domain("www." . $old_project->settings->live['live_domain'], $old_live_site_node); - } - } - // ... if the live domain did not change, delete the current live domain alias. - else { - devshop_hosting_delete_domain($project->settings->live['live_domain'], $live_site_node); - if ($old_project->settings->live['live_domain_www']) { - devshop_hosting_delete_domain("www." . $old_project->settings->live['live_domain'], $live_site_node); - } - } - - // Save the domain aliases to the new live environment - devshop_hosting_save_domain($project->settings->live['live_domain'], $live_site_node); - if ($project->settings->live['live_domain_www']) { - devshop_hosting_save_domain("www." . $project->settings->live['live_domain'], $live_site_node); - } - } - } -} +///** +// * Function for updating a project's alias +// */ +//function devshop_project_update_domains($project_node) { +// $project = $project_node->project; +// $old_project = $project_node->old->project; +// +// // If there is a live environment, AND live domain was entered: +// if ($project->settings->live['live_environment'] && $project->settings->live['live_domain']) { +// +// // ...and if the Live Domain changed: +// if ($project->settings->live['live_domain'] != $old_project->settings->live['live_domain']) { +// +// // Save live domain alias to the live environment +// $live_environment = $project->settings->live['live_environment']; +// $live_site_node = node_load($project->environment[$live_environment]->site); +// devshop_hosting_save_domain($project->settings->live['live_domain'], $live_site_node); +// +// // Save live domain alias with www prefix. +// if ($project->settings->live['live_domain_www']) { +// devshop_hosting_save_domain('www.' . $project->settings->live['live_domain'], $live_site_node); +// } +// +// // Delete all the old live domain aliases for all environments, and save new ones. +// $project_node_loaded = node_load($project_node->nid); +// foreach ($project_node_loaded->project['environments'] as $name => $environment) { +// $site_node = node_load($project->environment[$live_environment]->site); +// +// devshop_hosting_delete_domain($name . "." . $project->settings->live['live_domain'], $site_node); +// devshop_hosting_save_domain($name . "." . $project->settings->live['live_domain'], $site_node); +// } +// } +// +// // ... or if the Live Environment changed. +// if ($project->settings->live['live_environment'] != $old_project->settings->live['live_environment']) { +// $live_environment = $project->settings->live['live_environment']; +// $live_site_node = node_load($project->environment[$live_environment]->site); +// +// $old_live_environment = $old_project->settings->live['live_environment']; +// $old_live_site_node = node_load($project->environment[$old_live_environment]->site); +// +// // If the live domain also changed, delete the old domain from the old environment). +// if ($project->settings->live['live_domain'] != $old_project->settings->live['live_domain']) { +// devshop_hosting_delete_domain($old_project->settings->live['live_domain'], $old_live_site_node); +// +// // If project had www aliases, delete that alias as well. +// if ($old_project->settings->live['live_domain_www']) { +// devshop_hosting_delete_domain("www." . $old_project->settings->live['live_domain'], $old_live_site_node); +// } +// } +// // ... if the live domain did not change, delete the current live domain alias. +// else { +// devshop_hosting_delete_domain($project->settings->live['live_domain'], $live_site_node); +// if ($old_project->settings->live['live_domain_www']) { +// devshop_hosting_delete_domain("www." . $old_project->settings->live['live_domain'], $live_site_node); +// } +// } +// +// // Save the domain aliases to the new live environment +// devshop_hosting_save_domain($project->settings->live['live_domain'], $live_site_node); +// if ($project->settings->live['live_domain_www']) { +// devshop_hosting_save_domain("www." . $project->settings->live['live_domain'], $live_site_node); +// } +// } +// } +//} /** * Implementation of hook_delete(). From 051c96e4148b7b11674b868d4deaa9548976a20c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:15:31 -0500 Subject: [PATCH 1012/3476] Removing mention of not including www. --- devshop_projects/inc/forms.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index f28cb1e2c..b1c44aed5 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -400,7 +400,7 @@ function devshop_projects_form(&$node) { $form['project']['settings']['live']['live_domain'] = array( '#type' => 'textfield', '#title' => t('Live Domain'), - '#description' => t('The live domain for this project. Do not include "www". Used only for links. You must still add the Domain to your live environment manually.'), + '#description' => t('The live domain for this project. Used only for links. You must still add the Domain to your live environment manually.'), '#size' => 40, '#default_value' => $node->project->settings->live['live_domain'], ); From a9b232a43e6cfd2c0d2b1ff87970f1aa1dfa93af Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:36:24 -0500 Subject: [PATCH 1013/3476] Better wording for environment aliases form. --- devshop_projects/inc/forms.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index b1c44aed5..c0267efc7 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -400,7 +400,7 @@ function devshop_projects_form(&$node) { $form['project']['settings']['live']['live_domain'] = array( '#type' => 'textfield', '#title' => t('Live Domain'), - '#description' => t('The live domain for this project. Used only for links. You must still add the Domain to your live environment manually.'), + '#description' => t('The live domain for this project. Used only for links and when creating subdomain aliases for other environments. You must still add the Domain to your live environment manually.'), '#size' => 40, '#default_value' => $node->project->settings->live['live_domain'], ); @@ -408,7 +408,7 @@ function devshop_projects_form(&$node) { // Use aliases $form['project']['settings']['live']['environment_aliases'] = array( '#type' => 'checkbox', - '#title' => t('Create subdomains under Live Domain for new environments.'), + '#title' => t('For new environments, create subdomains under Live Domain.'), '#description' => t('When new environments are created, automatically add a domain name such as http://ENVIRONMENT.LIVEDOMAIN.com. Does not affect existing environments. Does not remove domains when disabled.'), '#default_value' => $project->settings->live['environment_aliases'], ); From 444c7da2b890685241f54bd9f3af17330910a47b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:36:47 -0500 Subject: [PATCH 1014/3476] Add "Additional Domain" display to create new environment form. --- devshop_projects/tasks/create.inc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 839809da9..4d7fef216 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -87,6 +87,15 @@ function hosting_task_devshop_create_form($node) { '#maxlength' => 64, ); + if ($project->settings->live['environment_aliases'] && $node->project->settings->live['live_domain']) { + $form['environment_alias'] = array( + '#title' => t('Additional Domain'), + '#type' => 'item', + '#description' => t('Your environment will get an additional Domain.'), + '#value' => 'http://ENVIRONMENTNAME.' . $node->project->settings->live['live_domain'], + ); + } + // @TODO: I don't think this is needed. hosting_task_devshop_create_form_submit() looks for nid, these values end up in $form_state['values']['paramters']; $form['project_nid'] = array( '#type' => 'value', From 3b024ec378a17c36a6beed9b7354677f2ca19fd8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:37:15 -0500 Subject: [PATCH 1015/3476] When creating the site node, add an alias if the project is configured that way. --- devshop_projects/devshop_projects.module | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6645d29a7..d06552955 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -346,6 +346,11 @@ function devshop_projects_create_site($project, $platform_node, $environment_nam // @TODO: Improve site language handling? $node->site_language = !empty($user->language)? $user->language: 'en'; + // Subdomain alias, if configured. + if ($project->settings->live['environment_aliases'] && !empty($project->settings->live['live_domain'])) { + $node->aliases = array($environment_name . '.' . $project->settings->live['live_domain']); + } + // Save the site node if ($node = node_submit($node)) { node_save($node); From 98f5d9a5f8a6aab96783378b9e03a416ea3c946b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:37:42 -0500 Subject: [PATCH 1016/3476] Removing note about automatic domains not working. --- devshop_projects/inc/forms.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c0267efc7..1eede42f2 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -375,7 +375,6 @@ function devshop_projects_form(&$node) { $form['project']['settings']['live'] = array( '#type' => 'fieldset', '#title' => t('Domain Name Settings'), - '#description' => 'NOTE: Automatic domain setup is not yet functional. Edit domains in the Environment settings.' ); // Live Environment From 104d14abe249f95e748be94e22b8979676137f1a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:42:24 -0500 Subject: [PATCH 1017/3476] Simplify button css --- devshop.css | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/devshop.css b/devshop.css index 7c7becc6d..a277ab8e1 100644 --- a/devshop.css +++ b/devshop.css @@ -55,12 +55,9 @@ textarea#rsa { .form-submit { margin-top: 10px; } -#edit-return, #edit-next { +#edit-cancel { float: right; - font-size: 2.0em; - height: 2.0em; } - .command input { font-family: Courier New, monospace; width: 100%; From cb9ea85b5f8fcf9cd7d5ae33cd9310466839ba15 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:49:19 -0500 Subject: [PATCH 1018/3476] Classing up the project wizard buttons. --- devshop_projects/inc/forms.inc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 1eede42f2..56a57eeb4 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -41,6 +41,17 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ if ($form_state['project']->no_next){ unset($form['buttons']['next']); } + $form['buttons']['next']['#attributes'] = + $form['buttons']['return']['#attributes'] = array( + 'class' => 'btn btn-success', + ); + $form['buttons']['previous']['#attributes'] = array( + 'class' => 'btn btn-default', + ); + $form['buttons']['cancel']['#attributes'] = array( + 'class' => 'btn btn-link', + ); + } // Hosting Task Forms From b8b901f033919b54c284c350d925d69ebcd3a3f9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:51:34 -0500 Subject: [PATCH 1019/3476] Add table class to wizard. --- devshop_projects/inc/theme.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 0f91289f3..609149054 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -70,7 +70,7 @@ function theme_devshop_projects_create_settings_form($form) { } $rows[] = $row; } - $output = theme('table', $header, $rows, array('id' => 'project-settings-table')); + $output = theme('table', $header, $rows, array('id' => 'project-settings-table', 'class' => 'table')); $output .= '

    '. t('Create as many new environments as you would like. For example: "dev", "test", and "live". You can create more later on if needed.') .'

    '; return $output; From fac97154742e567d748a6f94e79a5e16bf392820 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 12:52:28 -0500 Subject: [PATCH 1020/3476] Classing up the tables. --- devshop_projects/inc/create/step-4.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/create/step-4.inc b/devshop_projects/inc/create/step-4.inc index 202f9af4a..69a5e2d0c 100644 --- a/devshop_projects/inc/create/step-4.inc +++ b/devshop_projects/inc/create/step-4.inc @@ -117,7 +117,7 @@ function devshop_project_create_step_sites(&$form, &$form_state) { // Output our table. $form['platforms'] = array( '#type' => 'markup', - '#value' => theme('table', $header, $rows), + '#value' => theme('table', $header, $rows, array('class' => 'table')), ); // Not completed means show all tasks are not completed (or errored) From a861462ad03f40ff1559a197bf123ccb0306fd94 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 13:28:43 -0500 Subject: [PATCH 1021/3476] Create live subdomain aliases on clone and fork. --- devshop_projects/devshop_projects.drush.inc | 10 +++++ devshop_projects/inc/nodes.inc | 47 --------------------- devshop_projects/tasks/create.inc | 2 +- 3 files changed, 11 insertions(+), 48 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 9473f0fa9..bced1d874 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -307,9 +307,19 @@ function devshop_projects_post_hosting_import_task($task, $data) { $platform = node_load($site->platform); if ($platform->environment->name) { + + // Save environment record. drush_log('[DEVSHOP] Project & Environment found:' . $platform->project->name . '.' . $platform->environment->name); $return = db_query('UPDATE {hosting_devshop_project_environment} SET site = %d WHERE project_nid = %d AND name = "%s"', $site->nid, $platform->project->nid, $platform->environment->name); drush_log('[DEVSHOP] Site saved to environment.'); + + // Save aliases if needed. + if ($platform->project->settings->live['environment_aliases'] && !empty($platform->project->settings->live['live_domain'])) { + $domain = $platform->environment->name . '.' . $platform->project->settings->live['live_domain']; + $site->aliases = array($domain); + drush_log('[DEVSHOP] Saving domain to the environment:' . $domain); + node_save($site); + } } } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index d9ec421df..fb2a609f9 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -344,51 +344,4 @@ function devshop_projects_nodeapi(&$node, $op, $a3 = null) { $additions['environment'] = $project_node->project->environments[$result->name]; return $additions; } - - // When creating a site, look for project live domain setting, and - // "environment subdomains" setting to save domain aliases. - if ($op == 'insert' && $node->type == 'site') { - - $result = db_fetch_object(db_query(' - SELECT p.settings, e.name AS environment FROM {hosting_devshop_project} p - LEFT JOIN {hosting_devshop_project_environment} e ON p.nid = e.project_nid - WHERE e.site = %d - ', $node->nid)); - - $settings = unserialize($result->settings); - $environment = $result->environment; - - // Bail out if no environment name found. - if (empty($result->name)) { - return; - } - - $aliases = array(); - - // If environment subdomains are enabled... - if ($settings->live->environment_aliases) { - $aliases[] = $environment . "." . $settings->live->live_environment; - } - - // Save aliases if the are allowed. - foreach ($aliases as $alias) { - devshop_hosting_save_domain($alias, $node); - } - } -} - -/** - * Helper for saving a site domain for a site, includes the allow check. - */ -function devshop_hosting_save_domain($alias, $site) { - if (hosting_domain_allowed($alias, array('nid' => $site->nid)) && $alias != $site->title) { - db_query("INSERT INTO {hosting_site_alias} (vid, nid, alias, automatic, redirection) VALUES (%d, %d, '%s', %d, '%s')", $site->vid, $site->nid, $alias, HOSTING_ALIAS_CUSTOM, ''); - } -} - -/** - * Helper for deleting a site domain for a site, includes the allow check. - */ -function devshop_hosting_delete_domain($alias, $site) { - db_query("DELETE FROM {hosting_site_alias} WHERE vid = %d AND alias = '%s';", $site->vid, $alias); } diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 4d7fef216..7c77aaf30 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -87,7 +87,7 @@ function hosting_task_devshop_create_form($node) { '#maxlength' => 64, ); - if ($project->settings->live['environment_aliases'] && $node->project->settings->live['live_domain']) { + if ($project->settings->live['environment_aliases'] && !empty($node->project->settings->live['live_domain'])) { $form['environment_alias'] = array( '#title' => t('Additional Domain'), '#type' => 'item', From a71597a966639862b84962b7e13b2da370944d03 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Dec 2014 13:34:29 -0500 Subject: [PATCH 1022/3476] Make live subdomain the primary URL for an environment. --- devshop_projects/inc/nodes.inc | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index fb2a609f9..be670f4b4 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -79,14 +79,6 @@ function devshop_projects_load($node) { // Save to environments array $environment->settings = (object) unserialize($environment->settings); - // Environments URL: - // @TODO: Check for http or https. - // @TODO: Load a login URL if available. - if ($environment->default_domain) { - $environment->url = 'http://' . $environment->default_domain; - } else { - $environment->url = ''; - } $environment->git_ref_type = $project->settings->git['refs'][$environment->git_ref]; // Environment version @@ -115,10 +107,28 @@ function devshop_projects_load($node) { $environment->git_current = t('Cloning in progress...'); } + // Environments URL + // @TODO: Check for http or https. + // @TODO: Load a login URL if available. + if ($environment->default_domain) { + $environment->url = 'http://' . $environment->default_domain; + } else { + $environment->url = ''; + } + // Get Domain aliases $environment->domains = hosting_alias_get_aliases($environment); array_unshift($environment->domains, $environment->default_domain); + // Make the live domain the primary + if ($project->settings->live['environment_aliases'] && !empty($project->settings->live['live_domain'])) { + foreach ($environment->domains as $domain) { + if (strpos($domain, $project->settings->live['live_domain']) !== FALSE){ + $environment->url = 'http://' . $domain; + } + } + } + // Save to project environments collection. $environments[$environment->name] = $environment; } From fa646e4f09d00d6643069e9cc1808267e4abab1c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 16:32:13 -0500 Subject: [PATCH 1023/3476] Shifting deploy task to sites. --- devshop_projects/devshop_projects.module | 23 ++++++---- devshop_projects/tasks/deploy.inc | 58 ++++++++---------------- devshop_projects/tasks/tasks.inc | 2 +- 3 files changed, 36 insertions(+), 47 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index d06552955..9e37dd559 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -292,14 +292,12 @@ function devshop_hosting_task_menu_access($node, $task) { } if (user_access("create " . $task . " task")) { - if ($node->type == 'project') { - // If Commit Features task, make sure features module is present - if ($task == 'devshop-commit'){ - return _devshop_projects_project_has_module($node, 'features'); - } else { - return TRUE; - } + // If Commit Features task, make sure features module is present + if ($task == 'devshop-commit'){ + return _devshop_projects_project_has_module($node, 'features'); + } else { + return TRUE; } } } @@ -365,7 +363,7 @@ function devshop_projects_create_site($project, $platform_node, $environment_nam /** * Helper to get select #options for git ref. */ -function devshop_projects_git_ref_options($project){ +function devshop_projects_git_ref_options($project, $current_ref = ''){ // Build branch options if (is_array($project->settings->git['branches']) && !empty($project->settings->git['branches'])) { @@ -384,6 +382,15 @@ function devshop_projects_git_ref_options($project){ $options = array(t('No branches or tags! Re-validate the project.')); } + // Set "current" label. + if (isset($options['Branches'][$current_ref])) { + $options['Branches'][$current_ref] .= ' (' . t('current') . ')'; + } + elseif (isset($options['Tags'][$current_ref])) { + $options['Tags'][$current_ref] .= ' (' . t('current') . ')'; + } + + return $options; } diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 467808f03..7a7aa8f5e 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -13,53 +13,35 @@ */ function hosting_task_devshop_deploy_form($node) { - global $user; - $project = $node->project; - $branch_options = devshop_projects_git_ref_options($project); - $form = array(); - $form['git_ref'] = array( - '#title' => t('Git Branch or Tag'), - '#description' => t('Choose the branch or tag to deploy to this environment.'), - '#type' => 'select', - '#options' => $branch_options, - '#required' => TRUE, - '#default_value' => arg(3) == 'ref'? arg(4): '', - ); - - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the environment to deploy the chosen git branch or tag to.'), 'environment', 'Environment', 'radios'); - - $form['environment']['#default_value'] = isset($_GET['environment'])? $_GET['environment']: ''; - - // Detect pre-selected environments - if ($_GET['environment']) { - - // @TODO: Check that Git ref exists! - // @TODO: Display current branch! - // @TODO: Display timestamps for the last commit in the two Git Refs! - - $form['environment']['#type'] = 'value'; - $form['environment']['#value'] = $_GET['environment']; - - $form['git_ref']['#type'] = 'value'; - $form['git_ref']['#value'] = $_GET['git_ref']; + $project = $node->project; + $environment = $node->environment; - $form['environment_label'] = array( - '#type' => 'item', - '#title' => t('Environment'), - '#value' => $_GET['environment'], - '#description' => t('The environment to deploy code to.'), - '#prefix' => '
    ', - '#suffix' => '
    ', + // Detect pre-selected git ref. + if (!empty($_GET['git_ref'])) { + $form['git_ref'] = array( + '#type' => 'value', + '#value' => $_GET['git_ref'], ); $form['git_ref_label'] = array( '#type' => 'item', '#title' => t('Git Reference'), '#value' => $_GET['git_ref'], '#description' => t("The Git branch or tag to deploy."), - '#prefix' => '
    ', - '#suffix' => '
    ', + ); + } + else { + $branch_options = devshop_projects_git_ref_options($project, $environment->git_ref); + $default_value = !empty($_GET['git_ref'])? $_GET['git_ref']: $environment->git_ref; + + $form['git_ref'] = array( + '#title' => t('Git Branch or Tag'), + '#description' => t('Choose the branch or tag to deploy to this environment.'), + '#type' => 'select', + '#options' => $branch_options, + '#required' => TRUE, + '#default_value' => $default_value, ); } diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index a4d704680..453124b88 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -48,7 +48,7 @@ function devshop_projects_hosting_tasks() { 'dialog' => TRUE, 'hidden' => TRUE, ); - $tasks['project']['devshop-deploy'] = array( + $tasks['site']['devshop-deploy'] = array( 'title' => t('Deploy'), 'description' => t('Deploy a tag or branch to an environment, and (optionally) run update.php, clear cache, and revert features.'), 'access callback' => 'devshop_hosting_task_menu_access', From 573e4ca812325fddfc7e2a80510abde8ede16a77 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 16:55:25 -0500 Subject: [PATCH 1024/3476] Fixing refs array. --- devshop_projects/inc/nodes.inc | 12 ++++++++++-- devshop_projects/tasks/deploy.inc | 9 ++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index be670f4b4..4577e2aab 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -46,6 +46,13 @@ function devshop_projects_load($node) { $project->name = $name; $project->settings = (object) unserialize($project->settings); + // Create "refs" array to determine ref type. + foreach ($project->settings->git as $type => $refs) { + foreach ($refs as $ref) { + $project->settings->git['refs'][$ref] = $type == 'branches'? 'branch': 'tag'; + } + } + // Load Environments // @TODO: Remove environments where the site has been deleted. $query = db_query(" @@ -76,12 +83,13 @@ function devshop_projects_load($node) { continue; } - // Save to environments array + // Unserialize environment settings. $environment->settings = (object) unserialize($environment->settings); + // Get the current git ref type. $environment->git_ref_type = $project->settings->git['refs'][$environment->git_ref]; - // Environment version + // Environment Drupal version. $iid = db_result(db_query("SELECT iid FROM {hosting_package_instance} i left join {hosting_package} p on p.nid=i.package_id WHERE p.package_type='platform' AND i.rid=%d", $environment->platform)); $release = hosting_package_instance_load($iid); $environment->version = $release->version; diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 7a7aa8f5e..3a229936b 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -20,15 +20,18 @@ function hosting_task_devshop_deploy_form($node) { // Detect pre-selected git ref. if (!empty($_GET['git_ref'])) { + + $ref_type = $project->settings->git['refs'][$_GET['git_ref']]; + $form['git_ref'] = array( '#type' => 'value', '#value' => $_GET['git_ref'], ); $form['git_ref_label'] = array( '#type' => 'item', - '#title' => t('Git Reference'), - '#value' => $_GET['git_ref'], - '#description' => t("The Git branch or tag to deploy."), + '#title' => t('Git @ref', array('@ref' => $ref_type)), + '#value' => ' ' . $_GET['git_ref'], + '#description' => t("This git reference will be checked out in this environment."), ); } else { From 2404a89554627962ca1e6c05d62b9acfef20a56a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 17:28:24 -0500 Subject: [PATCH 1025/3476] Cleaning up the deploy form. --- devshop_projects/inc/forms.inc | 7 +++++++ devshop_projects/tasks/deploy.inc | 12 +++++++++++- devshop_projects/tasks/tasks.inc | 1 - 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 56a57eeb4..c2c00bb61 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -105,6 +105,13 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } break; + + // Alter title of deploy task. + case 'devshop-deploy': + $node = node_load($form['nid']['#value']); + drupal_set_title(t('Deploy code to Environment "@env"', array('@env' => $node->environment->name))); + $form['actions']['cancel']['#value'] = l(t('Cancel'), "node/{$node->project->nid}"); + break; } } diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 3a229936b..786f7fb31 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -18,10 +18,20 @@ function hosting_task_devshop_deploy_form($node) { $project = $node->project; $environment = $node->environment; + $current_ref_type_class = $environment->git_ref_type == 'tag'? 'tag': 'code-fork'; + + $form['environment'] = array( + '#type' => 'item', + '#title' => t('Environment'), + '#value' => l($environment->default_domain, 'http://' . $environment->default_domain, array('attributes' => array('target' => '_blank'))) . "   " . $environment->git_ref, + '#description' => t('The environment URL and current git reference.'), + ); + // Detect pre-selected git ref. if (!empty($_GET['git_ref'])) { $ref_type = $project->settings->git['refs'][$_GET['git_ref']]; + $ref_type_class = $ref_type == 'tag'? 'tag': 'code-fork'; $form['git_ref'] = array( '#type' => 'value', @@ -30,7 +40,7 @@ function hosting_task_devshop_deploy_form($node) { $form['git_ref_label'] = array( '#type' => 'item', '#title' => t('Git @ref', array('@ref' => $ref_type)), - '#value' => ' ' . $_GET['git_ref'], + '#value' => " " . $_GET['git_ref'], '#description' => t("This git reference will be checked out in this environment."), ); } diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index 453124b88..0858020e3 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -50,7 +50,6 @@ function devshop_projects_hosting_tasks() { ); $tasks['site']['devshop-deploy'] = array( 'title' => t('Deploy'), - 'description' => t('Deploy a tag or branch to an environment, and (optionally) run update.php, clear cache, and revert features.'), 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); From d3db0291747506a358d69219d8f6c5befde14c88 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 18:20:58 -0500 Subject: [PATCH 1026/3476] Adding hook_hosting_site_context_options() to set environment name, project name, and git_ref. --- devshop_projects/devshop_projects.drush.inc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index bced1d874..9624e2bff 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -355,6 +355,20 @@ function devshop_projects_hosting_project_context_options(&$task) { } } +/** + * Implements hook_hosting_site_context_options() + * + * Save environment name, project name, and git ref to site aliases. + */ +function devshop_projects_hosting_site_context_options(&$task) { + + if (isset($task->ref->environment)) { + $task->context_options['environment'] = $task->ref->environment->name; + $task->context_options['project'] = $task->ref->environment->project_name; + $task->context_options['git_ref'] = $task->ref->environment->git_ref; + } +} + /** * Implements hook_drush_context_import() * From fc25dfa89001c17538d0bac74c473a5a2e001e2a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 21:52:15 -0500 Subject: [PATCH 1027/3476] "deploy" updated to work with sites. --- devshop_projects/devshop_projects.drush.inc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 9624e2bff..6506110d9 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -39,9 +39,13 @@ function drush_devshop_projects_pre_hosting_task() { } // Deploy - if ($task->ref->type == 'project' && $task->task_type == 'devshop-deploy') { + if ($task->ref->type == 'site' && $task->task_type == 'devshop-deploy') { $task->options['git_ref'] = $task->task_args['git_ref']; - $task->options['environment'] = $task->task_args['environment']; + $task->options['git_ref_type'] = $task->task_args['git_ref_type']; + $task->options['environment'] = $task->ref->environment->name; + $task->options['settings'] = $task->ref->environment->settings; + + // @TODO: Convert this to something more robust. $task->options['update'] = $task->task_args['update']; $task->options['revert'] = !empty($task->task_args['revert']); $task->options['cache'] = $task->task_args['cache']; From 4744396827920d6ecfa521c5b12e0df6194ba6e5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 21:55:23 -0500 Subject: [PATCH 1028/3476] Breaking out drush stuff into includes. --- devshop_projects/devshop_projects.drush.inc | 119 +------------------- devshop_projects/drush/contexts.inc | 118 +++++++++++++++++++ 2 files changed, 120 insertions(+), 117 deletions(-) create mode 100644 devshop_projects/drush/contexts.inc diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 6506110d9..c864d3a91 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -4,6 +4,8 @@ * Connects DevShop front-end to provision backend. */ +include 'drush/contexts.inc'; + /** * Implements drush_HOOK_pre_COMMAND() * @@ -328,123 +330,6 @@ function devshop_projects_post_hosting_import_task($task, $data) { } } -/** - * Implements hook_hosting_project_context_options() - * - * This transfers data from the node to thes aegir context object (the alias!) - * For project entities. This is where we find the branches and tags on the remote. - */ -function devshop_projects_hosting_project_context_options(&$task) { - - - $branches = getBranchesAndTags($task->ref->project->git_url); - - // If something went wrong connecting to the git repo, don't wipe out our branches. - if (!empty($branches['branches'])){ - $task->ref->project->settings->git['branches'] = $branches['branches']; - $task->ref->project->settings->git['tags'] = $branches['tags']; - $task->ref->project->settings->git['refs'] = $branches['refs']; - - // Save the project node now that we have branches and tags. - // Don't verify again, this is the verification process. - $task->ref->no_verify = TRUE; - node_save($task->ref); - } - - // Save project object to drush alias (aegir context). - if (isset($task->ref->project)) { - $task->context_options['server'] = '@server_master'; - $task->context_options['project_name'] = $task->ref->title; - $task->context_options['project'] = $task->ref->project; - } -} - -/** - * Implements hook_hosting_site_context_options() - * - * Save environment name, project name, and git ref to site aliases. - */ -function devshop_projects_hosting_site_context_options(&$task) { - - if (isset($task->ref->environment)) { - $task->context_options['environment'] = $task->ref->environment->name; - $task->context_options['project'] = $task->ref->environment->project_name; - $task->context_options['git_ref'] = $task->ref->environment->git_ref; - } -} - -/** - * Implements hook_drush_context_import() - * - * This allows project nodes to be created from contexts (aliases) - */ -function devshop_projects_drush_context_import($context, &$node) { - if ($context->type == 'project') { - $node->title = $context->project_name; - $node->type = 'project'; - $node->project = $context->project; - } -} - -/** - * Helpfer for getting branches and tags from a git URL - */ -function getBranchesAndTags($git_url = NULL){ - if (is_null($git_url)){ - $git_url = drush_get_option('git_url'); - } - $command = "git ls-remote {$git_url}"; - drush_log('[DEVSHOP] running '.$command, 'ok'); - - if (drush_shell_exec($command)){ - $exec_output = drush_shell_exec_output(); - } else { - $exec_output = drush_shell_exec_output(); - drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error retrieving remote information: ') . implode("\n", $exec_output), 'error'); - return; - } - - - // Check for Permission Denied - // @TODO: Provide link to the Public key for the server. - if ('Permission denied' == substr($exec_output[0], 0, 17)){ - drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error:'). implode("\n", $exec_output), 'error'); - return; - } - - // If remote list is empty, something else went wrong. - if (count($exec_output) == 1 && empty($exec_output[0])){ - drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('Something went wrong. Check the git URL and try again.'), 'error'); - return; - } - - // Build tag and branch list - $branches = array(); - $tags = array(); - $refs = array(); - - foreach ($exec_output AS $line_string){ - - // @TODO: Would love some regex love here - // Example remote line: - // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 - $line = trim(substr($line_string, 40)); - $ref = explode("/", $line); - $git_ref = array_pop($ref); - - if ($ref[1] == 'heads') { - $branches[] = $git_ref; - $refs[$git_ref] = 'branch'; - } else if ($ref[1] == 'tags') { - $tags[] = $git_ref; - $refs[$git_ref] = 'tag'; - } - - } - drush_log(dt('[DEVSHOP] Found !b branches and !t tags.', array('!b' => count($branches), '!t' => count($tags), )), 'ok'); - return array('branches' => $branches, 'tags' => $tags, 'refs' => $refs); -} - /** * Utility for execute git commands. */ diff --git a/devshop_projects/drush/contexts.inc b/devshop_projects/drush/contexts.inc new file mode 100644 index 000000000..e40805db9 --- /dev/null +++ b/devshop_projects/drush/contexts.inc @@ -0,0 +1,118 @@ +ref->project->git_url); + + // If something went wrong connecting to the git repo, don't wipe out our branches. + if (!empty($branches['branches'])){ + $task->ref->project->settings->git['branches'] = $branches['branches']; + $task->ref->project->settings->git['tags'] = $branches['tags']; + $task->ref->project->settings->git['refs'] = $branches['refs']; + + // Save the project node now that we have branches and tags. + // Don't verify again, this is the verification process. + $task->ref->no_verify = TRUE; + node_save($task->ref); + } + + // Save project object to drush alias (aegir context). + if (isset($task->ref->project)) { + $task->context_options['server'] = '@server_master'; + $task->context_options['project_name'] = $task->ref->title; + $task->context_options['project'] = $task->ref->project; + } +} + +/** + * Implements hook_hosting_site_context_options() + * + * Save environment name, project name, and git ref to site aliases. + */ +function devshop_projects_hosting_site_context_options(&$task) { + + if (isset($task->ref->environment)) { + $task->context_options['environment'] = $task->ref->environment->name; + $task->context_options['project'] = $task->ref->environment->project_name; + $task->context_options['git_ref'] = $task->ref->environment->git_ref; + } +} + +/** + * Implements hook_drush_context_import() + * + * This allows project nodes to be created from contexts (aliases) + */ +function devshop_projects_drush_context_import($context, &$node) { + if ($context->type == 'project') { + $node->title = $context->project_name; + $node->type = 'project'; + $node->project = $context->project; + } +} + +/** + * Helpfer for getting branches and tags from a git URL + */ +function getBranchesAndTags($git_url = NULL){ + if (is_null($git_url)){ + $git_url = drush_get_option('git_url'); + } + $command = "git ls-remote {$git_url}"; + drush_log('[DEVSHOP] running '.$command, 'ok'); + + if (drush_shell_exec($command)){ + $exec_output = drush_shell_exec_output(); + } else { + $exec_output = drush_shell_exec_output(); + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error retrieving remote information: ') . implode("\n", $exec_output), 'error'); + return; + } + + + // Check for Permission Denied + // @TODO: Provide link to the Public key for the server. + if ('Permission denied' == substr($exec_output[0], 0, 17)){ + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('[DEVSHOP] Error:'). implode("\n", $exec_output), 'error'); + return; + } + + // If remote list is empty, something else went wrong. + if (count($exec_output) == 1 && empty($exec_output[0])){ + drush_set_error(DRUSH_FRAMEWORK_ERROR, dt('Something went wrong. Check the git URL and try again.'), 'error'); + return; + } + + // Build tag and branch list + $branches = array(); + $tags = array(); + $refs = array(); + + foreach ($exec_output AS $line_string){ + + // @TODO: Would love some regex love here + // Example remote line: + // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/3-00 + $line = trim(substr($line_string, 40)); + $ref = explode("/", $line); + $git_ref = array_pop($ref); + + if ($ref[1] == 'heads') { + $branches[] = $git_ref; + $refs[$git_ref] = 'branch'; + } else if ($ref[1] == 'tags') { + $tags[] = $git_ref; + $refs[$git_ref] = 'tag'; + } + + } + drush_log(dt('[DEVSHOP] Found !b branches and !t tags.', array('!b' => count($branches), '!t' => count($tags), )), 'ok'); + return array('branches' => $branches, 'tags' => $tags, 'refs' => $refs); +} \ No newline at end of file From ee7496e5fe7c2007c1f4c83bcca57113be1f7242 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 22:00:45 -0500 Subject: [PATCH 1029/3476] Fixing post deploy hook to use new site info. --- devshop_projects/devshop_projects.drush.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index c864d3a91..d2d43172a 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -220,15 +220,16 @@ function devshop_projects_post_hosting_delete_task($task, $data) { function devshop_projects_post_hosting_devshop_deploy_task($task, $data) { // Save the deployed git ref to the environment record. - drush_log('[DEVSHOP] Environment Deployed. Saving record.'); + // Doing this post deploy ensures it was actually checked out. + drush_log('[DEVSHOP] Environment Deployed. Saving new git ref to project settings.'); - $environment = $task->task_args['environment']; + $environment = $task->ref->environment->name; $git_ref = $task->task_args['git_ref']; $return = db_query('UPDATE {hosting_devshop_project_environment} SET git_ref = "%s" WHERE project_nid = %d AND name = "%s"', $git_ref, $task->ref->nid, $environment); if ($return) { - drush_log('[DEVSHOP] New git ref saved to environment..'); + drush_log('[DEVSHOP] New git ref saved to environment.'); } else { return drush_set_error('[DEVSHOP] Environment update failed!'); From da505ac814bbd9559ccf8a0a4ed9c16b3505c509 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 22:00:54 -0500 Subject: [PATCH 1030/3476] comments --- devshop_projects/drush/contexts.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/drush/contexts.inc b/devshop_projects/drush/contexts.inc index e40805db9..e4a417dbe 100644 --- a/devshop_projects/drush/contexts.inc +++ b/devshop_projects/drush/contexts.inc @@ -34,6 +34,7 @@ function devshop_projects_hosting_project_context_options(&$task) { /** * Implements hook_hosting_site_context_options() * + * Runs on verify task. Passes data to the drush alias. * Save environment name, project name, and git ref to site aliases. */ function devshop_projects_hosting_site_context_options(&$task) { From d2f2b087aec56ea83468405ad44b8737b8afe5c8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 22:15:57 -0500 Subject: [PATCH 1031/3476] Pass argument, not an option. --- devshop_projects/devshop_projects.drush.inc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index d2d43172a..886170608 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -42,10 +42,7 @@ function drush_devshop_projects_pre_hosting_task() { // Deploy if ($task->ref->type == 'site' && $task->task_type == 'devshop-deploy') { - $task->options['git_ref'] = $task->task_args['git_ref']; - $task->options['git_ref_type'] = $task->task_args['git_ref_type']; - $task->options['environment'] = $task->ref->environment->name; - $task->options['settings'] = $task->ref->environment->settings; + $task->args['git_ref'] = $task->task_args['git_ref']; // @TODO: Convert this to something more robust. $task->options['update'] = $task->task_args['update']; From 6c6b26357cc3c5528296989095bc86b84c3891d5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 22:20:32 -0500 Subject: [PATCH 1032/3476] Fixing the environment record update. --- devshop_projects/devshop_projects.drush.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 886170608..7e335d29b 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -220,13 +220,12 @@ function devshop_projects_post_hosting_devshop_deploy_task($task, $data) { // Doing this post deploy ensures it was actually checked out. drush_log('[DEVSHOP] Environment Deployed. Saving new git ref to project settings.'); - $environment = $task->ref->environment->name; $git_ref = $task->task_args['git_ref']; - $return = db_query('UPDATE {hosting_devshop_project_environment} SET git_ref = "%s" WHERE project_nid = %d AND name = "%s"', $git_ref, $task->ref->nid, $environment); + $return = db_query('UPDATE {hosting_devshop_project_environment} SET git_ref = "%s" WHERE project_nid = %d AND name = "%s"', $git_ref, $task->ref->environment->project_nid, $task->ref->environment->name); if ($return) { - drush_log('[DEVSHOP] New git ref saved to environment.'); + drush_log("[DEVSHOP] Git reference {$git_ref} saved to {$task->ref->environment->name} environment record."); } else { return drush_set_error('[DEVSHOP] Environment update failed!'); From 77970a127911bd209d79ed55473c89cbc48e498a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 22:25:08 -0500 Subject: [PATCH 1033/3476] Fixing extra items in git['refs'] --- devshop_projects/inc/nodes.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 4577e2aab..f23824292 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -47,9 +47,10 @@ function devshop_projects_load($node) { $project->settings = (object) unserialize($project->settings); // Create "refs" array to determine ref type. + $project->settings->git['refs'] = array(); foreach ($project->settings->git as $type => $refs) { foreach ($refs as $ref) { - $project->settings->git['refs'][$ref] = $type == 'branches'? 'branch': 'tag'; + $project->settings->git['refs'][$ref] = ($type == 'branches')? 'branch': 'tag'; } } From 147b7e3e65d39d4fd30c51bf771426da30292218 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 22:31:37 -0500 Subject: [PATCH 1034/3476] Adding redirect back to project from deploy task form. --- devshop_projects/inc/forms.inc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c2c00bb61..b182eb91c 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -111,6 +111,7 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ $node = node_load($form['nid']['#value']); drupal_set_title(t('Deploy code to Environment "@env"', array('@env' => $node->environment->name))); $form['actions']['cancel']['#value'] = l(t('Cancel'), "node/{$node->project->nid}"); + $form['#submit'][] = 'devshop_deploy_redirect_to_project'; break; } } @@ -255,6 +256,21 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } } +/** + * Generic form submit handler for tasks confirmation + * + * This handler gets called after any task has been confirmed by the user. It + * will inject a new task in the queue and redirect the user to the + * originating node. + * + * @see hosting_add_task() + */ +function devshop_deploy_redirect_to_project($form, &$form_state) { + $values = $form_state['values']; + $node = node_load($values['nid']); + $form_state['redirect'] = 'node/' . $node->environment->project_nid; +} + /** * Submit handler for site/environment settings page. */ From ca63b42642f9d7c84ba02ac52f29cedbe7488ce9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 6 Dec 2014 22:57:55 -0500 Subject: [PATCH 1035/3476] Queuing up a verification of the platform. --- devshop_projects/devshop_projects.drush.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 7e335d29b..67386f868 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -230,6 +230,9 @@ function devshop_projects_post_hosting_devshop_deploy_task($task, $data) { else { return drush_set_error('[DEVSHOP] Environment update failed!'); } + + // Queue up a verification of the platform. + hosting_add_task($task->ref->environment->platform, 'verify'); } /** From 52078973da27d3eca48eba294b196ca0b85209ea Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 7 Dec 2014 00:39:51 -0500 Subject: [PATCH 1036/3476] Load the project block on every node/% page that is a project. --- devshop_projects/devshop_projects.module | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 9e37dd559..0f71785e5 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -159,11 +159,6 @@ function devshop_projects_menu() { /** * Implements hook_block() - * - * Provides three blocks: - * - DevShop Tasks - * - Created by ThinkDrop - * - Powered by Aegir */ function devshop_projects_block($op = 'list', $delta = 0, $edit = NULL) { switch ($op) { @@ -180,7 +175,7 @@ function devshop_projects_block($op = 'list', $delta = 0, $edit = NULL) { case 'view': if ($delta == 'project_nav'){ - $node = menu_get_object('node'); + $node = node_load(arg(1)); if ($node->type == 'project') { $block['subject'] = ''; $block['content'] = theme('devshop_project_nav', $node); From 5fa4976c72bd7fa8dc694392756329c1a82d09a2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 7 Dec 2014 14:23:59 -0500 Subject: [PATCH 1037/3476] Moving pull settings to devshop projects codebase. It is required stuff. --- devshop_projects/inc/forms.inc | 101 +++++++++++++++++++ devshop_pull/devshop_pull.module | 166 +++++++++++++------------------ 2 files changed, 170 insertions(+), 97 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index b182eb91c..140fa1961 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -446,6 +446,107 @@ function devshop_projects_form(&$node) { '#default_value' => $project->settings->live['environment_aliases'], ); + // Pull Code Method + $form['project']['settings']['deploy'] = array( + '#type' => 'fieldset', + '#title' => t('Deploy Settings'), + '#description' => t('Configure how code is delivered to the servers. Post Deploy hooks are configured per environment.'), + ); + + $form['project']['settings']['deploy']['method'] = array( + '#title' => 'Deploy Code Method', + '#type' => 'radios', + '#description' => t('Choose the method used to deploy code to the server.'), + '#default_value' => $project->settings->deploy['method'], + ); + + $form['project']['settings']['deploy']['method'][] = array( + '#type' => 'radio', + '#title' => t('Commit Webhook'), + '#description' => t('Recommended. Pull code when a webhook is invoked from your git repository host.'), + '#return_value' => 'webhook', + '#default_value' => $project->settings->pull['method'] == 'webhook', + '#parents' => array('method'), + '#spawned' => TRUE, + ); + + // Add pull queue option, if it is enabled. + $queues = hosting_get_queues(); + if ($queues['pull']['enabled']) { + $form['project']['settings']['deploy']['method'][] = array( + '#type' => 'radio', + '#title' => t('Pull on Queue'), + '#description' => t('Pull code on a regular basis. Use if you cannot setup a commit webhook.'), + '#return_value' => 'queue', + '#default_value' => $project->settings->deploy['method'] == 'queue', + '#parents' => array('method'), + '#spawned' => TRUE, + '#disabled' => !$queues['pull']['enabled'], + ); + } + + if (!$queues['pull']['enabled'] && user_access('administer hosting queues')){ + $form['project']['settings']['deploy']['queue_admin'] = array( + '#value' => t('The !link is disabled. Enable it to allow projects to pull code on a regular basis.', array( + '!link' => l(t('Pull Queue'), 'admin/hosting/queues'), + )), + '#prefix' => '

    ', + '#suffix' => '

    ', + ); + } + + $form['project']['settings']['deploy']['method'][] = array( + '#type' => 'radio', + '#title' => t('Manual Pull Only'), + '#description' => t('Pull Code to servers manually via devshop or drush only.', array( + '!link' => l(t('Hosting Queues'), 'admin/hosting/queues'), + )), + '#return_value' => 'manual', + '#default_value' => $project->settings->deploy['method'] == 'manual', + '#parents' => array('method'), + '#spawned' => TRUE, + ); + + // @TODO: is there a better way to save certain values? We lose data without these. + $form['project']['settings']['pull']['last_pull'] = array( + '#type' => 'value', + '#value' => $node->project->settings->pull['last_pull'], + ); + $form['project']['settings']['pull']['last_pull_status'] = array( + '#type' => 'value', + '#value' => $node->project->settings->pull['last_pull_status'], + ); + $form['project']['settings']['pull']['last_pull_ip'] = array( + '#type' => 'value', + '#value' => $node->project->settings->pull['last_pull_ip'], + ); + + // @TODO: Make this more abstract and extensible. + $is_github = (strpos($node->project->git_url, 'github.com') !== FALSE); + + //All settings git pull in project page + $form['project']['settings']['github'] = array( + '#type' => 'fieldset', + '#title' => t('GitHub Integration'), + '#access' => $is_github, + ); + + // Pull Requests create environments? + $form['project']['settings']['github']['pull_request_environments'] = array( + '#type' => 'checkbox', + '#title' => t('Create Environments for Pull Requests'), + '#default_value' => $node->project->settings->github['pull_request_environments'], + '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), + '#access' => $is_github, + ); + + // Delete Pull Request environments? + $form['project']['settings']['github']['pull_request_environments_delete'] = array( + '#type' => 'checkbox', + '#title' => t('Delete Pull Request Environments'), + '#default_value' => $node->project->settings->github['pull_request_environments_delete'], + '#description' => t('When Pull Requests are closed, delete the environment.'), + ); return $form; } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 10e2e1b6d..f03271172 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -74,103 +74,75 @@ function devshop_pull_hosting_queues() { return $items; } -/** - * Implements hook_form_alter(). - */ -function devshop_pull_form_alter(&$form, &$form_state, $form_id) { - - // On Projects, add "Choose Pull Method" - if ($form_id == 'project_node_form') { - // Get node - $node = $form['#node']; - - //All settings git pull in project page - $form['project']['settings']['pull'] = array( - '#type' => 'fieldset', - '#title' => t('Git Settings'), - ); - - $form['project']['settings']['pull']['pull_enabled'] = array( - '#title' => 'Pull on Commit', - '#type' => 'checkbox', - '#description' => t('Run a "Pull Code" task when a commit notification is received. All environments set to a branch will have their code updated.'), - '#default_value' => $node->project->settings->pull['pull_enabled'], - '#attributes' => array( - ), - ); - - module_load_include('inc', 'devshop_pull'); - $form['project']['settings']['pull']['pull_url'] = array( - '#type' => 'item', - '#title' => t('Pull Trigger URL'), - '#value' => _devshop_pull_callback_url($node), - '#description' => t('Configure your repo to hit this URL when it receives a commit.'), - '#prefix' => '
    ', - '#suffix' => '
    ', - ); - $queues = hosting_get_queues(); - $form['project']['settings']['pull']['queue_enabled'] = array( - '#title' => 'Pull on Queue', - '#type' => 'checkbox', - '#description' => t('Run a "Pull Code" task on a regular basis using Aegir Queues.'), - '#default_value' => $node->project->settings->pull['queue_enabled'], - '#access' => $queues['pull']['enabled'], - ); - - // @TODO: is there a better way to save certain values? We lose data without these. - $form['project']['settings']['pull']['last_pull'] = array( - '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull'], - ); - $form['project']['settings']['pull']['last_pull_status'] = array( - '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull_status'], - ); - $form['project']['settings']['pull']['last_pull_ip'] = array( - '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull_ip'], - ); - - // @TODO: Make this more abstract and extensible. - $is_github = (strpos($node->project->git_url, 'github.com') !== FALSE); - - //All settings git pull in project page - $form['project']['settings']['github'] = array( - '#type' => 'fieldset', - '#title' => t('GitHub Integration'), - '#access' => $is_github, - ); - - // Pull Requests create environments? - $form['project']['settings']['github']['pull_request_environments'] = array( - '#type' => 'checkbox', - '#title' => t('Create Environments for Pull Requests'), - '#default_value' => $node->project->settings->github['pull_request_environments'], - '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), - '#access' => $is_github, - ); - - // Delete Pull Request environments? - $form['project']['settings']['github']['pull_request_environments_delete'] = array( - '#type' => 'checkbox', - '#title' => t('Delete Pull Request Environments'), - '#default_value' => $node->project->settings->github['pull_request_environments_delete'], - '#description' => t('When Pull Requests are closed, delete the environment.'), - ); - - } - - // "Environment Settings" form. - if ($form_id == 'site_node_form' && isset($form['#node']->project)) { - $form['environment']['settings']['pull_disabled'] = array( - '#title' => t('Disable Pull on Commit'), - '#node_type' => 'platform', - '#type' => 'checkbox', - '#description' => t('Turn off automatic code updates for this environment.'), - '#default_value' => $form['#node']->environment->settings->pull_disabled, - ); - } -} +///** +// * Implements hook_form_alter(). +// */ +//function devshop_pull_form_alter(&$form, &$form_state, $form_id) { +// +// // On Projects, add "Choose Pull Method" +// if ($form_id == 'project_node_form') { +// // Get node +// $node = $form['#node']; + +// +// module_load_include('inc', 'devshop_pull'); +// $form['project']['settings']['pull']['pull_url'] = array( +// '#type' => 'item', +// '#title' => t('Pull Trigger URL'), +// '#value' => _devshop_pull_callback_url($node), +// '#description' => t('Configure your repo to hit this URL when it receives a commit.'), +// '#prefix' => '
    ', +// '#suffix' => '
    ', +// ); +// $queues = hosting_get_queues(); +// $form['project']['settings']['pull']['queue_enabled'] = array( +// '#title' => 'Pull on Queue', +// '#type' => 'checkbox', +// '#description' => t('Run a "Pull Code" task on a regular basis using Aegir Queues.'), +// '#default_value' => $node->project->settings->pull['queue_enabled'], +// '#access' => $queues['pull']['enabled'], +//// ); +// +// // @TODO: Make this more abstract and extensible. +// $is_github = (strpos($node->project->git_url, 'github.com') !== FALSE); +// +// //All settings git pull in project page +// $form['project']['settings']['github'] = array( +// '#type' => 'fieldset', +// '#title' => t('GitHub Integration'), +// '#access' => $is_github, +// ); +// +// // Pull Requests create environments? +// $form['project']['settings']['github']['pull_request_environments'] = array( +// '#type' => 'checkbox', +// '#title' => t('Create Environments for Pull Requests'), +// '#default_value' => $node->project->settings->github['pull_request_environments'], +// '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), +// '#access' => $is_github, +// ); +// +// // Delete Pull Request environments? +// $form['project']['settings']['github']['pull_request_environments_delete'] = array( +// '#type' => 'checkbox', +// '#title' => t('Delete Pull Request Environments'), +// '#default_value' => $node->project->settings->github['pull_request_environments_delete'], +// '#description' => t('When Pull Requests are closed, delete the environment.'), +// ); + +// } +// +// // "Environment Settings" form. +// if ($form_id == 'site_node_form' && isset($form['#node']->project)) { +// $form['environment']['settings']['pull_disabled'] = array( +// '#title' => t('Disable Pull on Commit'), +// '#node_type' => 'platform', +// '#type' => 'checkbox', +// '#description' => t('Turn off automatic code updates for this environment.'), +// '#default_value' => $form['#node']->environment->settings->pull_disabled, +// ); +// } +//} /** * Implements hook_nodeapi() From 1c03b446c06e6c8bc97ce090206fd0f9a5763c95 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 7 Dec 2014 14:23:59 -0500 Subject: [PATCH 1038/3476] Moving pull settings to devshop projects codebase, changing it to be called "Deploy Code Method" --- devshop_projects/inc/forms.inc | 101 +++++++++++++++++++ devshop_pull/devshop_pull.module | 166 +++++++++++++------------------ 2 files changed, 170 insertions(+), 97 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index b182eb91c..140fa1961 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -446,6 +446,107 @@ function devshop_projects_form(&$node) { '#default_value' => $project->settings->live['environment_aliases'], ); + // Pull Code Method + $form['project']['settings']['deploy'] = array( + '#type' => 'fieldset', + '#title' => t('Deploy Settings'), + '#description' => t('Configure how code is delivered to the servers. Post Deploy hooks are configured per environment.'), + ); + + $form['project']['settings']['deploy']['method'] = array( + '#title' => 'Deploy Code Method', + '#type' => 'radios', + '#description' => t('Choose the method used to deploy code to the server.'), + '#default_value' => $project->settings->deploy['method'], + ); + + $form['project']['settings']['deploy']['method'][] = array( + '#type' => 'radio', + '#title' => t('Commit Webhook'), + '#description' => t('Recommended. Pull code when a webhook is invoked from your git repository host.'), + '#return_value' => 'webhook', + '#default_value' => $project->settings->pull['method'] == 'webhook', + '#parents' => array('method'), + '#spawned' => TRUE, + ); + + // Add pull queue option, if it is enabled. + $queues = hosting_get_queues(); + if ($queues['pull']['enabled']) { + $form['project']['settings']['deploy']['method'][] = array( + '#type' => 'radio', + '#title' => t('Pull on Queue'), + '#description' => t('Pull code on a regular basis. Use if you cannot setup a commit webhook.'), + '#return_value' => 'queue', + '#default_value' => $project->settings->deploy['method'] == 'queue', + '#parents' => array('method'), + '#spawned' => TRUE, + '#disabled' => !$queues['pull']['enabled'], + ); + } + + if (!$queues['pull']['enabled'] && user_access('administer hosting queues')){ + $form['project']['settings']['deploy']['queue_admin'] = array( + '#value' => t('The !link is disabled. Enable it to allow projects to pull code on a regular basis.', array( + '!link' => l(t('Pull Queue'), 'admin/hosting/queues'), + )), + '#prefix' => '

    ', + '#suffix' => '

    ', + ); + } + + $form['project']['settings']['deploy']['method'][] = array( + '#type' => 'radio', + '#title' => t('Manual Pull Only'), + '#description' => t('Pull Code to servers manually via devshop or drush only.', array( + '!link' => l(t('Hosting Queues'), 'admin/hosting/queues'), + )), + '#return_value' => 'manual', + '#default_value' => $project->settings->deploy['method'] == 'manual', + '#parents' => array('method'), + '#spawned' => TRUE, + ); + + // @TODO: is there a better way to save certain values? We lose data without these. + $form['project']['settings']['pull']['last_pull'] = array( + '#type' => 'value', + '#value' => $node->project->settings->pull['last_pull'], + ); + $form['project']['settings']['pull']['last_pull_status'] = array( + '#type' => 'value', + '#value' => $node->project->settings->pull['last_pull_status'], + ); + $form['project']['settings']['pull']['last_pull_ip'] = array( + '#type' => 'value', + '#value' => $node->project->settings->pull['last_pull_ip'], + ); + + // @TODO: Make this more abstract and extensible. + $is_github = (strpos($node->project->git_url, 'github.com') !== FALSE); + + //All settings git pull in project page + $form['project']['settings']['github'] = array( + '#type' => 'fieldset', + '#title' => t('GitHub Integration'), + '#access' => $is_github, + ); + + // Pull Requests create environments? + $form['project']['settings']['github']['pull_request_environments'] = array( + '#type' => 'checkbox', + '#title' => t('Create Environments for Pull Requests'), + '#default_value' => $node->project->settings->github['pull_request_environments'], + '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), + '#access' => $is_github, + ); + + // Delete Pull Request environments? + $form['project']['settings']['github']['pull_request_environments_delete'] = array( + '#type' => 'checkbox', + '#title' => t('Delete Pull Request Environments'), + '#default_value' => $node->project->settings->github['pull_request_environments_delete'], + '#description' => t('When Pull Requests are closed, delete the environment.'), + ); return $form; } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 10e2e1b6d..f03271172 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -74,103 +74,75 @@ function devshop_pull_hosting_queues() { return $items; } -/** - * Implements hook_form_alter(). - */ -function devshop_pull_form_alter(&$form, &$form_state, $form_id) { - - // On Projects, add "Choose Pull Method" - if ($form_id == 'project_node_form') { - // Get node - $node = $form['#node']; - - //All settings git pull in project page - $form['project']['settings']['pull'] = array( - '#type' => 'fieldset', - '#title' => t('Git Settings'), - ); - - $form['project']['settings']['pull']['pull_enabled'] = array( - '#title' => 'Pull on Commit', - '#type' => 'checkbox', - '#description' => t('Run a "Pull Code" task when a commit notification is received. All environments set to a branch will have their code updated.'), - '#default_value' => $node->project->settings->pull['pull_enabled'], - '#attributes' => array( - ), - ); - - module_load_include('inc', 'devshop_pull'); - $form['project']['settings']['pull']['pull_url'] = array( - '#type' => 'item', - '#title' => t('Pull Trigger URL'), - '#value' => _devshop_pull_callback_url($node), - '#description' => t('Configure your repo to hit this URL when it receives a commit.'), - '#prefix' => '
    ', - '#suffix' => '
    ', - ); - $queues = hosting_get_queues(); - $form['project']['settings']['pull']['queue_enabled'] = array( - '#title' => 'Pull on Queue', - '#type' => 'checkbox', - '#description' => t('Run a "Pull Code" task on a regular basis using Aegir Queues.'), - '#default_value' => $node->project->settings->pull['queue_enabled'], - '#access' => $queues['pull']['enabled'], - ); - - // @TODO: is there a better way to save certain values? We lose data without these. - $form['project']['settings']['pull']['last_pull'] = array( - '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull'], - ); - $form['project']['settings']['pull']['last_pull_status'] = array( - '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull_status'], - ); - $form['project']['settings']['pull']['last_pull_ip'] = array( - '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull_ip'], - ); - - // @TODO: Make this more abstract and extensible. - $is_github = (strpos($node->project->git_url, 'github.com') !== FALSE); - - //All settings git pull in project page - $form['project']['settings']['github'] = array( - '#type' => 'fieldset', - '#title' => t('GitHub Integration'), - '#access' => $is_github, - ); - - // Pull Requests create environments? - $form['project']['settings']['github']['pull_request_environments'] = array( - '#type' => 'checkbox', - '#title' => t('Create Environments for Pull Requests'), - '#default_value' => $node->project->settings->github['pull_request_environments'], - '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), - '#access' => $is_github, - ); - - // Delete Pull Request environments? - $form['project']['settings']['github']['pull_request_environments_delete'] = array( - '#type' => 'checkbox', - '#title' => t('Delete Pull Request Environments'), - '#default_value' => $node->project->settings->github['pull_request_environments_delete'], - '#description' => t('When Pull Requests are closed, delete the environment.'), - ); - - } - - // "Environment Settings" form. - if ($form_id == 'site_node_form' && isset($form['#node']->project)) { - $form['environment']['settings']['pull_disabled'] = array( - '#title' => t('Disable Pull on Commit'), - '#node_type' => 'platform', - '#type' => 'checkbox', - '#description' => t('Turn off automatic code updates for this environment.'), - '#default_value' => $form['#node']->environment->settings->pull_disabled, - ); - } -} +///** +// * Implements hook_form_alter(). +// */ +//function devshop_pull_form_alter(&$form, &$form_state, $form_id) { +// +// // On Projects, add "Choose Pull Method" +// if ($form_id == 'project_node_form') { +// // Get node +// $node = $form['#node']; + +// +// module_load_include('inc', 'devshop_pull'); +// $form['project']['settings']['pull']['pull_url'] = array( +// '#type' => 'item', +// '#title' => t('Pull Trigger URL'), +// '#value' => _devshop_pull_callback_url($node), +// '#description' => t('Configure your repo to hit this URL when it receives a commit.'), +// '#prefix' => '
    ', +// '#suffix' => '
    ', +// ); +// $queues = hosting_get_queues(); +// $form['project']['settings']['pull']['queue_enabled'] = array( +// '#title' => 'Pull on Queue', +// '#type' => 'checkbox', +// '#description' => t('Run a "Pull Code" task on a regular basis using Aegir Queues.'), +// '#default_value' => $node->project->settings->pull['queue_enabled'], +// '#access' => $queues['pull']['enabled'], +//// ); +// +// // @TODO: Make this more abstract and extensible. +// $is_github = (strpos($node->project->git_url, 'github.com') !== FALSE); +// +// //All settings git pull in project page +// $form['project']['settings']['github'] = array( +// '#type' => 'fieldset', +// '#title' => t('GitHub Integration'), +// '#access' => $is_github, +// ); +// +// // Pull Requests create environments? +// $form['project']['settings']['github']['pull_request_environments'] = array( +// '#type' => 'checkbox', +// '#title' => t('Create Environments for Pull Requests'), +// '#default_value' => $node->project->settings->github['pull_request_environments'], +// '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'), +// '#access' => $is_github, +// ); +// +// // Delete Pull Request environments? +// $form['project']['settings']['github']['pull_request_environments_delete'] = array( +// '#type' => 'checkbox', +// '#title' => t('Delete Pull Request Environments'), +// '#default_value' => $node->project->settings->github['pull_request_environments_delete'], +// '#description' => t('When Pull Requests are closed, delete the environment.'), +// ); + +// } +// +// // "Environment Settings" form. +// if ($form_id == 'site_node_form' && isset($form['#node']->project)) { +// $form['environment']['settings']['pull_disabled'] = array( +// '#title' => t('Disable Pull on Commit'), +// '#node_type' => 'platform', +// '#type' => 'checkbox', +// '#description' => t('Turn off automatic code updates for this environment.'), +// '#default_value' => $form['#node']->environment->settings->pull_disabled, +// ); +// } +//} /** * Implements hook_nodeapi() From dba254d35e73f1b67711cc7d850832b7a1ee3be4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 7 Dec 2014 16:00:40 -0500 Subject: [PATCH 1039/3476] Improving project deploy settings. --- devshop_projects/inc/forms.inc | 60 +++++++++++++++------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 140fa1961..ecb69d4cc 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -460,34 +460,40 @@ function devshop_projects_form(&$node) { '#default_value' => $project->settings->deploy['method'], ); - $form['project']['settings']['deploy']['method'][] = array( - '#type' => 'radio', - '#title' => t('Commit Webhook'), - '#description' => t('Recommended. Pull code when a webhook is invoked from your git repository host.'), - '#return_value' => 'webhook', - '#default_value' => $project->settings->pull['method'] == 'webhook', - '#parents' => array('method'), - '#spawned' => TRUE, - ); + // Commit Webhook + $form['project']['settings']['deploy']['method']['#options']['webhook'] = t('Immediate Deployment'); + $form['project']['settings']['deploy']['method']['#options']['webhook'] .= '
    ' . t('Recommended. Deploy code as it is delivered to your repository.') . ' ' . t('Requires setting up a webhook with your git repository host.') . '' . '
    '; - // Add pull queue option, if it is enabled. + // Queue $queues = hosting_get_queues(); if ($queues['pull']['enabled']) { - $form['project']['settings']['deploy']['method'][] = array( - '#type' => 'radio', - '#title' => t('Pull on Queue'), - '#description' => t('Pull code on a regular basis. Use if you cannot setup a commit webhook.'), - '#return_value' => 'queue', - '#default_value' => $project->settings->deploy['method'] == 'queue', - '#parents' => array('method'), - '#spawned' => TRUE, - '#disabled' => !$queues['pull']['enabled'], - ); + $form['project']['settings']['deploy']['method']['#options']['queue'] = t('Queued Deployment'); + + $form['project']['settings']['deploy']['method']['#options']['queue'] .= '
    '; + $form['project']['settings']['deploy']['method']['#options']['queue'] .= t('Use only if repository webhooks are not available.'); + + + if (user_access('administer hosting queues')) { + $form['project']['settings']['deploy']['method']['#options']['queue'] .= ' '. l(t("Deploy Queue configured to run every @freq.", array('@freq' => format_interval($queues['pull']['frequency'], 1))), 'admin/hosting/queues'); + } else { + $form['project']['settings']['deploy']['method']['#options']['queue'] .= ' '. t("Deploy Queue configured to run every @freq.", array('@freq' => format_interval($queues['pull']['frequency'], 1))); + + } + + $form['project']['settings']['deploy']['method']['#options']['queue'] .= '
    '; + } + // Manual Pull + $form['project']['settings']['deploy']['method']['#options']['manual'] = t('Manual Deploy'); + $form['project']['settings']['deploy']['method']['#options']['manual'] .= '
    ' . t('Pull Code to servers manually via devshop or drush.'); + + $form['project']['settings']['deploy']['method']['#options']['manual'] .= ' ' . t('Rarely recommended. Branch environments must be manually updated.') . ''. '
    '; + + // Add link to hosting queues admin if the user can access them. if (!$queues['pull']['enabled'] && user_access('administer hosting queues')){ $form['project']['settings']['deploy']['queue_admin'] = array( - '#value' => t('The !link is disabled. Enable it to allow projects to pull code on a regular basis.', array( + '#value' => t('The !link is disabled. Enable it to allow projects to pull code in the queue.', array( '!link' => l(t('Pull Queue'), 'admin/hosting/queues'), )), '#prefix' => '

    ', @@ -495,18 +501,6 @@ function devshop_projects_form(&$node) { ); } - $form['project']['settings']['deploy']['method'][] = array( - '#type' => 'radio', - '#title' => t('Manual Pull Only'), - '#description' => t('Pull Code to servers manually via devshop or drush only.', array( - '!link' => l(t('Hosting Queues'), 'admin/hosting/queues'), - )), - '#return_value' => 'manual', - '#default_value' => $project->settings->deploy['method'] == 'manual', - '#parents' => array('method'), - '#spawned' => TRUE, - ); - // @TODO: is there a better way to save certain values? We lose data without these. $form['project']['settings']['pull']['last_pull'] = array( '#type' => 'value', From 142cc293a3e412cec31ca46281f9b0b673fa8a5a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 15:32:26 -0500 Subject: [PATCH 1040/3476] No longer using devshop_pull_form_alter(); --- devshop_projects/inc/create/step-2.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/create/step-2.inc b/devshop_projects/inc/create/step-2.inc index 8a86ad415..6d586d723 100644 --- a/devshop_projects/inc/create/step-2.inc +++ b/devshop_projects/inc/create/step-2.inc @@ -15,7 +15,6 @@ function devshop_project_create_step_settings(&$form, &$form_state) { $node = node_load($project->project_nid); $form += devshop_projects_form($node); $form['#node'] = $node; - devshop_pull_form_alter($form, $form_state, 'project_node_form'); // Remove "environment" selector. $form['project']['settings']['live']['live_environment']['#type'] = 'value'; From 3d1b39bbdf4dbbeb190c2f6b92369f1b6fb1afd5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 15:53:14 -0500 Subject: [PATCH 1041/3476] Adding "Pull Request Environment" deploy method. --- devshop_projects/inc/forms.inc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index ecb69d4cc..fcb7c732b 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -541,6 +541,18 @@ function devshop_projects_form(&$node) { '#default_value' => $node->project->settings->github['pull_request_environments_delete'], '#description' => t('When Pull Requests are closed, delete the environment.'), ); + + // Pull Request Environment method. + $form['project']['settings']['github']['pull_request_environments_method'] = array( + '#type' => 'radios', + '#title' => t('Pull Request Environment Deploy Method'), + '#default_value' => $node->project->settings->github['pull_request_environments_method'], + '#description' => t('Select the method for creating the pull request environments.'), + '#options' => array( + 'install' => t('Run the install profile %profile', array('%profile' => $project->install_profile)), + 'clone_live' => t('Clone the live environment: %live', array('%live' => $project->settings->live['live_environment'])), + ) + ); return $form; } From 4e72e68baa7dbf2e37e32623c09477138b65b59f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 16:00:47 -0500 Subject: [PATCH 1042/3476] Improving form descriptions. --- devshop_projects/inc/forms.inc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index fcb7c732b..50af534b5 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -469,14 +469,17 @@ function devshop_projects_form(&$node) { if ($queues['pull']['enabled']) { $form['project']['settings']['deploy']['method']['#options']['queue'] = t('Queued Deployment'); + $t = array(); + $t['@freq'] = format_interval($queues['pull']['frequency'], 1); + $form['project']['settings']['deploy']['method']['#options']['queue'] .= '

    '; - $form['project']['settings']['deploy']['method']['#options']['queue'] .= t('Use only if repository webhooks are not available.'); + $form['project']['settings']['deploy']['method']['#options']['queue'] .= t('Pulls code every @freq. Only runs deploy hooks if code has changed. Use only if repository webhooks are not available.', $t); if (user_access('administer hosting queues')) { - $form['project']['settings']['deploy']['method']['#options']['queue'] .= ' '. l(t("Deploy Queue configured to run every @freq.", array('@freq' => format_interval($queues['pull']['frequency'], 1))), 'admin/hosting/queues'); + $form['project']['settings']['deploy']['method']['#options']['queue'] .= ' '. l(t("Deploy Queue configured to run every @freq.", $t), 'admin/hosting/queues'); } else { - $form['project']['settings']['deploy']['method']['#options']['queue'] .= ' '. t("Deploy Queue configured to run every @freq.", array('@freq' => format_interval($queues['pull']['frequency'], 1))); + $form['project']['settings']['deploy']['method']['#options']['queue'] .= ' '. t("Deploy Queue configured to run every @freq.", $t); } From cd5ab13119abdc933e5ce83f9a25263a071d7a7e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 16:16:49 -0500 Subject: [PATCH 1043/3476] Moving the queue out of devshop pull. --- devshop_projects/devshop_projects.module | 1 + devshop_projects/inc/queue.inc | 45 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 devshop_projects/inc/queue.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 0f71785e5..6dac0a03f 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -7,6 +7,7 @@ include_once('inc/forms.inc'); include_once('inc/nodes.inc'); +include_once('inc/queue.inc'); include_once('inc/theme.inc'); include_once('inc/ui.inc'); diff --git a/devshop_projects/inc/queue.inc b/devshop_projects/inc/queue.inc new file mode 100644 index 000000000..5b58fc4e1 --- /dev/null +++ b/devshop_projects/inc/queue.inc @@ -0,0 +1,45 @@ + 'batch', + 'name' => t('Deploy Queue'), + 'description' => t('Runs git pull and deploy hooks on projects configured to do so.'), + 'total_items' => count(devshop_projects_get_deploy_queue_environments()), + 'frequency' => strtotime("1 minute", 0), + 'singular' => t('environment'), + 'plural' => t('environments'), + ); + return $items; +} + +/** + * Get the environments to be pulled in the queue. + * + * @param $limit + * Limit to a maximum of this number of platforms. + * @return + * An array of site nodes that have a pull queue enabled. + * + */ +function devshop_projects_get_deploy_queue_environments($limit = 5) { + + $results = db_query("SELECT nid FROM {hosting_devshop_project} LIMIT %d", $limit); + $environments = array(); + while ($result = db_fetch_object($results)) { + $node = node_load($result->nid); + $project = $node->project; + if ($project->settings->deploy['method'] == 'queue'){ + foreach ($project->environments as $environment) { + if ($environment->git_ref_type == 'branch') { + $environments[] = $environment; + } + } + + } + } + return $environments; +} \ No newline at end of file From 4aa13d965fb145809891eb39ec78b659b733349a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 17:00:24 -0500 Subject: [PATCH 1044/3476] Adding queue command. --- devshop_projects/inc/queue.inc | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/queue.inc b/devshop_projects/inc/queue.inc index 5b58fc4e1..5a63b9814 100644 --- a/devshop_projects/inc/queue.inc +++ b/devshop_projects/inc/queue.inc @@ -25,16 +25,16 @@ function devshop_projects_hosting_queues() { * An array of site nodes that have a pull queue enabled. * */ -function devshop_projects_get_deploy_queue_environments($limit = 5) { +function devshop_projects_get_deploy_queue_environments() { - $results = db_query("SELECT nid FROM {hosting_devshop_project} LIMIT %d", $limit); + $results = db_query("SELECT nid FROM {hosting_devshop_project}"); $environments = array(); while ($result = db_fetch_object($results)) { $node = node_load($result->nid); $project = $node->project; if ($project->settings->deploy['method'] == 'queue'){ foreach ($project->environments as $environment) { - if ($environment->git_ref_type == 'branch') { + if ($environment->git_ref_type == 'branch' && $environment->settings->pull_disabled == 0) { $environments[] = $environment; } } @@ -42,4 +42,19 @@ function devshop_projects_get_deploy_queue_environments($limit = 5) { } } return $environments; -} \ No newline at end of file +} + +/** + * Implements hosting_QUEUE_TYPE_queue(). + */ +function hosting_deploy_queue($count) { + $environments = devshop_projects_get_deploy_queue_environments($count); + foreach ($environments as $environment) { + $args = array(); + $args['git_ref'] = $environment->git_ref; + $args['update'] = $environment->settings->deploy['update']; + $args['revert'] = $environment->settings->deploy['revert']; + $args['cache'] = $environment->settings->deploy['cache']; + hosting_add_task($environment->site, 'devshop-deploy', $args); + } +} From 2dee714a04d408d6b0ef9efad5645066cd869ca8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 17:05:09 -0500 Subject: [PATCH 1045/3476] Changing "pull" environment settings to "deploy" settings. --- devshop_projects/inc/forms.inc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 50af534b5..e7aaf9a44 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -223,32 +223,31 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ $form['environment']['settings']['pull_disabled'] = array( '#type' => 'checkbox', - '#title' => t('Disable Pull on Commit'), + '#title' => t('Disable Deploy on Commit'), '#default_value' => $environment->settings->pull_disabled, '#description' => t('Do not pull code to the server on commit & push.'), ); - $form['environment']['settings']['pull'] = array( + $form['environment']['settings']['deploy'] = array( '#type' => 'fieldset', - '#title' => t('Automatic Code Pull'), - '#description' => t('When code is automatically pulled to this environment, do the following:'), + '#title' => t('Deployment Hooks'), + '#description' => t('When code is deployed to this environment, do the following:'), ); - $form['environment']['settings']['pull']['pull_revert'] = array( + $form['environment']['settings']['deploy']['revert'] = array( '#type' => 'checkbox', '#title' => t('Revert all features.'), - '#default_value' => $environment->settings->pull['pull_revert'], + '#default_value' => $environment->settings->deploy['revert'], '#description' => t(''), ); - - $form['environment']['settings']['pull']['pull_update'] = array( + $form['environment']['settings']['deploy']['update'] = array( '#type' => 'checkbox', '#title' => t('Run database updates.'), - '#default_value' => $environment->settings->pull['pull_update'], + '#default_value' => $environment->settings->deploy['update'], ); - $form['environment']['settings']['pull']['pull_cache'] = array( + $form['environment']['settings']['deploy']['cache'] = array( '#type' => 'checkbox', '#title' => t('Clear all caches.'), - '#default_value' => $environment->settings->pull['pull_cache'], + '#default_value' => $environment->settings->deploy['cache'], ); // Add our own submit handler From ebba7d00dba8ba0cebae6fbd5b201321a34cba0e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 17:30:54 -0500 Subject: [PATCH 1046/3476] Improving "revert" handling when there is no features module. --- devshop_projects/inc/forms.inc | 6 +++++- devshop_projects/tasks/deploy.inc | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e7aaf9a44..3d4a66fc1 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -237,8 +237,12 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#type' => 'checkbox', '#title' => t('Revert all features.'), '#default_value' => $environment->settings->deploy['revert'], - '#description' => t(''), ); + if (!_devshop_projects_site_has_module($environment->site, 'features')){ + $form['environment']['settings']['deploy']['revert']['#description'] .= ' ' . t('Features module is not enabled in this environment.'); + $form['environment']['settings']['deploy']['revert']['#disabled'] = 'disabled'; + } + $form['environment']['settings']['deploy']['update'] = array( '#type' => 'checkbox', '#title' => t('Run database updates.'), diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index 786f7fb31..d9b9e7c69 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -61,20 +61,24 @@ function hosting_task_devshop_deploy_form($node) { $form['update'] = array( '#title' => t('Run update.php after code pull?'), '#type' => 'checkbox', - '#default_value' => 1, + '#default_value' => $node->environment->settings->deploy['update'], ); - if (_devshop_projects_project_has_module($node, 'features')){ - $form['revert'] = array( - '#title' => t('Revert all features after code pull?'), - '#type' => 'checkbox', - '#default_value' => 1, - ); + $form['revert'] = array( + '#title' => t('Revert all features after code pull?'), + '#type' => 'checkbox', + '#default_value' => $node->environment->settings->deploy['revert'], + ); + + if (!_devshop_projects_site_has_module($node->nid, 'features')){ + $form['revert']['#description'] .= ' ' . t('Features module is not enabled in this environment.'); + $form['revert']['#disabled'] = 'disabled'; } + $form['cache'] = array( '#title' => t('Clear cache after code pull?'), '#type' => 'checkbox', - '#default_value' => 1, + '#default_value' => $node->environment->settings->deploy['cache'], ); return $form; From 62a8fcb4af60ec2c71ad2ac4c4a19f821b0d143d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 17:31:55 -0500 Subject: [PATCH 1047/3476] Lets set the deploy queue default to 5 minutes. --- devshop_projects/inc/queue.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/queue.inc b/devshop_projects/inc/queue.inc index 5a63b9814..4555b471c 100644 --- a/devshop_projects/inc/queue.inc +++ b/devshop_projects/inc/queue.inc @@ -9,7 +9,7 @@ function devshop_projects_hosting_queues() { 'name' => t('Deploy Queue'), 'description' => t('Runs git pull and deploy hooks on projects configured to do so.'), 'total_items' => count(devshop_projects_get_deploy_queue_environments()), - 'frequency' => strtotime("1 minute", 0), + 'frequency' => strtotime("5 minutes", 0), 'singular' => t('environment'), 'plural' => t('environments'), ); From 31557af8a3447a729911db2b8c4cad2d145cb325 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 17:33:35 -0500 Subject: [PATCH 1048/3476] Commenting out pull queue code. --- devshop_pull/devshop_pull.module | 126 +++++++++++++++---------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index f03271172..eea42f93d 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -57,22 +57,22 @@ function devshop_pull_menu() { ); return $items; } - -/** - * Implements hook_hosting_queues() - */ -function devshop_pull_hosting_queues() { - $items['pull'] = array( - 'type' => 'batch', - 'name' => t('Pull queue'), - 'description' => t('Run git pull on projects configured to do so.'), - 'total_items' => count(devshop_pull_get_projects(5)), - 'frequency' => strtotime("1 minute", 0), - 'singular' => t('project'), - 'plural' => t('projects'), - ); - return $items; -} +// +///** +// * Implements hook_hosting_queues() +// */ +//function devshop_pull_hosting_queues() { +// $items['pull'] = array( +// 'type' => 'batch', +// 'name' => t('Pull queue'), +// 'description' => t('Run git pull on projects configured to do so.'), +// 'total_items' => count(devshop_pull_get_projects(5)), +// 'frequency' => strtotime("1 minute", 0), +// 'singular' => t('project'), +// 'plural' => t('projects'), +// ); +// return $items; +//} ///** // * Implements hook_form_alter(). @@ -194,50 +194,50 @@ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { } } -/** - * Get a number of platforms that have their pull queue enabled. - * - * @param $limit - * Limit to a maximum of this number of platforms. - * @return - * An array of site nodes that have a pull queue enabled. - * - */ -function devshop_pull_get_projects($limit = 5) { - - $results = db_query("SELECT * FROM {hosting_devshop_project} LIMIT %d", $limit); - - $projects = array(); - while ($result = db_fetch_object($results)) { - $node = node_load($result->nid); - $project = $node->project; - - if ($project->settings->pull['queue_enabled']){ - $projects[] = $project; - } - } - - return $projects; -} - -/** - * Implements hosting_QUEUE_TYPE_queue(). - */ -function hosting_pull_queue($count) { - - $projects = devshop_pull_get_projects($count); - - foreach ($projects as $project) { - $environments_to_pull = array(); - foreach ($project->environments as $environment) { - if (!$environment->settings['pull_disabled']) { - $environments_to_pull[] = $environment->name; - } - } - $args = array(); - $args['environments'] = implode(' ', $environments_to_pull); - hosting_add_task($project->nid, 'devshop-pull', $args); - - module_invoke_all('devshop_pull', $project); - } -} +///** +// * Get a number of platforms that have their pull queue enabled. +// * +// * @param $limit +// * Limit to a maximum of this number of platforms. +// * @return +// * An array of site nodes that have a pull queue enabled. +// * +// */ +//function devshop_pull_get_projects($limit = 5) { +// +// $results = db_query("SELECT * FROM {hosting_devshop_project} LIMIT %d", $limit); +// +// $projects = array(); +// while ($result = db_fetch_object($results)) { +// $node = node_load($result->nid); +// $project = $node->project; +// +// if ($project->settings->pull['queue_enabled']){ +// $projects[] = $project; +// } +// } +// +// return $projects; +//} +// +///** +// * Implements hosting_QUEUE_TYPE_queue(). +// */ +//function hosting_pull_queue($count) { +// +// $projects = devshop_pull_get_projects($count); +// +// foreach ($projects as $project) { +// $environments_to_pull = array(); +// foreach ($project->environments as $environment) { +// if (!$environment->settings['pull_disabled']) { +// $environments_to_pull[] = $environment->name; +// } +// } +// $args = array(); +// $args['environments'] = implode(' ', $environments_to_pull); +// hosting_add_task($project->nid, 'devshop-pull', $args); +// +// module_invoke_all('devshop_pull', $project); +// } +//} From 24aeb20d41359dcb41074009270d8de356ccdfc5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 17:34:49 -0500 Subject: [PATCH 1049/3476] check deploy queue instead of "pull" --- devshop_projects/inc/forms.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 3d4a66fc1..f597d9a95 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -469,11 +469,11 @@ function devshop_projects_form(&$node) { // Queue $queues = hosting_get_queues(); - if ($queues['pull']['enabled']) { + if ($queues['deploy']['enabled']) { $form['project']['settings']['deploy']['method']['#options']['queue'] = t('Queued Deployment'); $t = array(); - $t['@freq'] = format_interval($queues['pull']['frequency'], 1); + $t['@freq'] = format_interval($queues['deploy']['frequency'], 1); $form['project']['settings']['deploy']['method']['#options']['queue'] .= '
    '; $form['project']['settings']['deploy']['method']['#options']['queue'] .= t('Pulls code every @freq. Only runs deploy hooks if code has changed. Use only if repository webhooks are not available.', $t); @@ -497,7 +497,7 @@ function devshop_projects_form(&$node) { $form['project']['settings']['deploy']['method']['#options']['manual'] .= ' ' . t('Rarely recommended. Branch environments must be manually updated.') . ''. '
    '; // Add link to hosting queues admin if the user can access them. - if (!$queues['pull']['enabled'] && user_access('administer hosting queues')){ + if (!$queues['deploy']['enabled'] && user_access('administer hosting queues')){ $form['project']['settings']['deploy']['queue_admin'] = array( '#value' => t('The !link is disabled. Enable it to allow projects to pull code in the queue.', array( '!link' => l(t('Pull Queue'), 'admin/hosting/queues'), From cad6038212fa105be90e562db54720e67b7f59ee Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 17:51:08 -0500 Subject: [PATCH 1050/3476] Pull > Deploy. --- devshop_projects/inc/forms.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index f597d9a95..9a603a73e 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -476,7 +476,7 @@ function devshop_projects_form(&$node) { $t['@freq'] = format_interval($queues['deploy']['frequency'], 1); $form['project']['settings']['deploy']['method']['#options']['queue'] .= '
    '; - $form['project']['settings']['deploy']['method']['#options']['queue'] .= t('Pulls code every @freq. Only runs deploy hooks if code has changed. Use only if repository webhooks are not available.', $t); + $form['project']['settings']['deploy']['method']['#options']['queue'] .= t('Deploy code every @freq. Only runs deploy hooks if code has changed. Use only if repository webhooks are not available.', $t); if (user_access('administer hosting queues')) { @@ -491,10 +491,10 @@ function devshop_projects_form(&$node) { } // Manual Pull - $form['project']['settings']['deploy']['method']['#options']['manual'] = t('Manual Deploy'); - $form['project']['settings']['deploy']['method']['#options']['manual'] .= '
    ' . t('Pull Code to servers manually via devshop or drush.'); + $form['project']['settings']['deploy']['method']['#options']['manual'] = t('Manual Deployment'); + $form['project']['settings']['deploy']['method']['#options']['manual'] .= '
    ' . t('Deploy code to servers manually via devshop or drush.'); - $form['project']['settings']['deploy']['method']['#options']['manual'] .= ' ' . t('Rarely recommended. Branch environments must be manually updated.') . ''. '
    '; + $form['project']['settings']['deploy']['method']['#options']['manual'] .= ' ' . t('Not recommended. All environments must be manually updated.') . ''. '
    '; // Add link to hosting queues admin if the user can access them. if (!$queues['deploy']['enabled'] && user_access('administer hosting queues')){ From 3c16a857d142ca30122966bbdde1d9cff7e9881c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 18:44:08 -0500 Subject: [PATCH 1051/3476] Add webhook URL to project. --- devshop_projects/inc/nodes.inc | 6 ++ devshop_pull/devshop_pull.module | 96 ++++++++++++++++++-------------- 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index f23824292..ab9b88609 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -158,6 +158,12 @@ function devshop_projects_load($node) { $project->name = $node->title; $project->status = $node->status; + // Webhook Status + // @TODO: Create Hosting Webhooks module. + // @TODO: Remove devshop_pull. + module_load_include('inc', 'devshop_pull'); + $project->webhook_url = _devshop_pull_callback_url($node); + // Save project object be available at $node->project. $additions['project'] = $project; diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index eea42f93d..9bc7516eb 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -150,50 +150,62 @@ function devshop_pull_menu() { * @todo Break this out into aegir's subhook hook_nodeapi_OP_TYPE() */ function devshop_pull_nodeapi(&$node, $op, $a3 = null) { - - // PROJECTS - if ($node->type == 'project'){ - - // View Project - if ($op == 'view' && $node->project->settings->pull['pull_enabled']){ - - $pull_data = $node->project->settings->pull; - - module_load_include('inc', 'devshop_pull'); - $url = _devshop_pull_callback_url($node); - $output = ''; - - $status = (int) $pull_data['last_pull_status']; - $node->pull_status = $status; - - // If access denied, provide link to settings page - if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ - $output .= t('Commit notification received %ago from %ip, but that IP is not allowed to trigger tasks. See !link.', array( - '!link' => l(t('DevShop Pull Settings'), '/admin/hosting/devshop/pull'), - '%ip' => $pull_data['last_pull_ip'], - '%ago' => hosting_format_interval($pull_data['last_pull']) - )); - } - // If OK, show how much time has passed. - elseif ($status == DEVSHOP_PULL_STATUS_OK) { - $output = hosting_format_interval($pull_data['last_pull']); - } - // Otherwise, we assume no commit notification recieved. - else { - $output .= t('No commit notifications received. Setup a webhook on your Git host to ping: '); - $output .= strtr(" ", array('!url' => $url)); - } - $node->content['info']['last_pull'] = array( - '#type' => 'item', - '#title' => t('Last Commit'), - '#weight' => 32, - '#value' => $output, - ); - $node->pull_message = $output; - } - } +// +// // PROJECTS +// if ($node->type == 'project') { +// +// // View Project +// if ($op == 'view' && $node->project->settings->pull['pull_enabled']){ +// +// +// module_load_include('inc', 'devshop_pull'); +// $url = _devshop_pull_callback_url($node); +// $output = ''; +// +// } +// } +// } } +// +// +// $pull_data = $node->project->settings->pull; +// +// module_load_include('inc', 'devshop_pull'); +// $url = _devshop_pull_callback_url($node); +// $output = '';_devshop_pull_callback_url +// +// $status = (int) $pull_data['last_pull_status']; +// $node->pull_status = $status; +// +// // If access denied, provide link to settings page +// if ($status == DEVSHOP_PULL_STATUS_ACCESS_DENIED){ +// $output .= t('Commit notification received %ago from %ip, but that IP is not allowed to trigger tasks. See !link.', array( +// '!link' => l(t('DevShop Pull Settings'), '/admin/hosting/devshop/pull'), +// '%ip' => $pull_data['last_pull_ip'], +// '%ago' => hosting_format_interval($pull_data['last_pull']) +// )); +// } +// // If OK, show how much time has passed. +// elseif ($status == DEVSHOP_PULL_STATUS_OK) { +// $output = hosting_format_interval($pull_data['last_pull']); +// } +// // Otherwise, we assume no commit notification recieved. +// else { +// $output .= t('No commit notifications received. Setup a webhook on your Git host to ping: '); +// $output .= strtr(" ", array('!url' => $url)); +// } +// $node->content['info']['last_pull'] = array( +// '#type' => 'item', +// '#title' => t('Last Commit'), +// '#weight' => 32, +// '#value' => $output, +// ); +// $node->pull_message = $output; +// } +// } +//} + ///** // * Get a number of platforms that have their pull queue enabled. // * From e7a42bad8d4ce2bd54a247216d85a811d58df603 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 18:58:57 -0500 Subject: [PATCH 1052/3476] Make "git_provider" a property on projects. --- devshop_projects/inc/nodes.inc | 12 ++++++++++++ devshop_projects/inc/theme.inc | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index ab9b88609..4675d8c4b 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -164,6 +164,18 @@ function devshop_projects_load($node) { module_load_include('inc', 'devshop_pull'); $project->webhook_url = _devshop_pull_callback_url($node); + // Git Repo Host + if (strpos($project->git_url, 'github.com') !== FALSE) { + $project->git_provider = 'github'; + } + else { + $project->git_provider = 'git'; + } + // @TODO: Set git providers for most common here, then add a hook to detect. + +// dsm($project); + + // Save project object be available at $node->project. $additions['project'] = $project; diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 609149054..27e28a470 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -103,7 +103,7 @@ function template_preprocess_devshop_project_nav(&$vars){ $project = $vars['node']->project; // @TODO: Detect other web URLs for other git hosts. - if (strpos($project->git_url, 'github.com') !== FALSE) { + if ($project->git_provider == 'github') { $url = str_replace('git@github.com:', 'http://github.com/', $project->git_url); $vars['github_url'] = $url; } @@ -170,7 +170,7 @@ function template_preprocess_devshop_project_nav(&$vars){ $vars['settings_link'] = l(' ' . t('Settings'), "node/$node->nid/edit", array('html' => TRUE)); } - if (user_access('access project logs')){ - $vars['logs_link'] = l(' ' . t('Logs'), "node/$node->nid/logs", array('html' => TRUE)); - } +// if (user_access('access project logs')){ +// $vars['logs_link'] = l(' ' . t('Logs'), "node/$node->nid/logs", array('html' => TRUE)); +// } } \ No newline at end of file From 14f84ba93a6eaad3a990effc32f4e0b1132ae12e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 19:02:10 -0500 Subject: [PATCH 1053/3476] removing comments. --- devshop_projects/inc/nodes.inc | 3 --- 1 file changed, 3 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 4675d8c4b..cb8418fb5 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -173,9 +173,6 @@ function devshop_projects_load($node) { } // @TODO: Set git providers for most common here, then add a hook to detect. -// dsm($project); - - // Save project object be available at $node->project. $additions['project'] = $project; From b578129399d181ad063ff3ae835ca67e95615f6b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 19:04:55 -0500 Subject: [PATCH 1054/3476] Altering the webhook callback url. --- devshop_pull/devshop_pull.inc | 2 +- devshop_pull/devshop_pull.module | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index c8c455d9a..b56f27e25 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -204,7 +204,7 @@ function devshop_pull_ip_match($ip, $list) { */ function _devshop_pull_callback_url($node) { return url(DEVSHOP_PULL_CALLBACK_URL - . '/' . $node->hosting_name + . '/' . $node->title . '/' . _devshop_pull_hash_create($node), array('absolute' => TRUE)); } diff --git a/devshop_pull/devshop_pull.module b/devshop_pull/devshop_pull.module index 9bc7516eb..4b0a732d3 100644 --- a/devshop_pull/devshop_pull.module +++ b/devshop_pull/devshop_pull.module @@ -25,7 +25,7 @@ define('DEVSHOP_PULL_DEFAULT_ALLOWED_IPS'," "); // The base URL to use for the Post Commit callback. -define('DEVSHOP_PULL_CALLBACK_URL', 'devshop/pull'); +define('DEVSHOP_PULL_CALLBACK_URL', 'webhook'); /** * Implements hook_perm() From 12d085232d784080a23356144d3114331fd34a99 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 20:01:35 -0500 Subject: [PATCH 1055/3476] Converting pull to deploy. --- devshop_projects/inc/forms.inc | 12 ++++---- devshop_pull/devshop_pull.inc | 54 +++++++++++++++++++--------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 9a603a73e..cdfc31f5f 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -508,17 +508,17 @@ function devshop_projects_form(&$node) { } // @TODO: is there a better way to save certain values? We lose data without these. - $form['project']['settings']['pull']['last_pull'] = array( + $form['project']['settings']['deploy']['webhook_'] = array( '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull'], + '#value' => $node->project->settings->deploy['last_webhook'], ); - $form['project']['settings']['pull']['last_pull_status'] = array( + $form['project']['settings']['deploy']['last_webhook_status'] = array( '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull_status'], + '#value' => $node->project->settings->deploy['last_webhook_status'], ); - $form['project']['settings']['pull']['last_pull_ip'] = array( + $form['project']['settings']['deploy']['last_webhook_ip'] = array( '#type' => 'value', - '#value' => $node->project->settings->pull['last_pull_ip'], + '#value' => $node->project->settings->deploy['last_webhook_ip'], ); // @TODO: Make this more abstract and extensible. diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index b56f27e25..efc523c1e 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -7,9 +7,9 @@ function devshop_pull_callback($project_hosting_context, $hash) { // Load the project node & list of allowed IPs - $project_node = hosting_context_load(str_replace('project_', '', $project_hosting_context)); + $project_node = hosting_context_load($project_hosting_context); $project = $project_node->project; - $pull_settings = $project_node->project->settings->pull; + $deploy_settings = $project_node->project->settings->deploy; $allowed_ips = explode("\n", trim(variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); array_filter(array_map('trim', $allowed_ips)); @@ -32,12 +32,12 @@ function devshop_pull_callback($project_hosting_context, $hash) { $status = DEVSHOP_PULL_STATUS_INVALID_CODE; } // Make sure the project has pull callback enabled - elseif (!$pull_settings['pull_enabled']){ - $message = "Project $project is NOT configured to use Pull Code URL callback!"; + if ($deploy_settings['method'] != 'webhook') { + $message = "Project not configured to use webhook deployment."; } // Make sure the client's IP address is on the list else if (!devshop_pull_ip_match(ip_address(), $allowed_ips)) { - $message = ip_address() . " is not authorized to invoke a Pull Code request."; + $message = ip_address() . " is not authorized to invoke a webhook request."; $status = DEVSHOP_PULL_STATUS_ACCESS_DENIED; } // All checks pass! Server is allowed to trigger tasks! @@ -56,11 +56,12 @@ function devshop_pull_callback($project_hosting_context, $hash) { } // Save the project node with last pull info. - $pull_settings['last_pull'] = time(); - $pull_settings['last_pull_status'] = $status; - $pull_settings['last_pull_ip'] = ip_address(); + $deploy_settings['last_webhook'] = time(); + $deploy_settings['last_webhook_status'] = $status; + $deploy_settings['last_webhook_ip'] = ip_address(); - $project_node->project->settings->pull = $pull_settings; + $project_node->project->settings->deploy = $deploy_settings; + $project_node->no_verify; node_save($project_node); @@ -77,19 +78,26 @@ function devshop_pull_callback($project_hosting_context, $hash) { /** * Default action to take on webhook init. */ -function devshop_pull_default_webhook($project, $environments_to_pull){ - // Create the hosting task - // We need to pass environments so we can control what envs to pull based - // on the data coming back from github! - $args = array(); - $args['environments'] = implode(' ', $environments_to_pull); - - // Always clear cache... - // @TODO: Should we make this a setting? "What to do on auto-pull?" - $args['cache'] = 1; - hosting_add_task($project->nid, 'devshop-pull', $args); - - return "Commit notification received! Running 'Pull Code' on $project->title environments " . $args['environments']; +function devshop_pull_default_webhook($project_node){ + $project = $project_node->project; + foreach ($project->environments as $environment) { + + // Respect pull disabled setting. + if ($environment->settings->pull_disabled) { + continue; + } + $environments_to_pull[] = $environment->name; + + $args = array(); + $args['git_ref'] = $environment->git_ref; + $args['update'] = $environment->settings->deploy['update']; + $args['revert'] = $environment->settings->deploy['revert']; + $args['cache'] = $environment->settings->deploy['cache']; + + hosting_add_task($environment->site, 'devshop-deploy', $args); + } + + return "Commit notification received! Running 'Deploy' on $project->title environments " . implode(', ', $environments_to_pull); } /** @@ -213,7 +221,7 @@ function _devshop_pull_callback_url($node) { * Create a security hash code based on the platform node */ function _devshop_pull_hash_create($node) { - return md5($node->hosting_name . $node->nid); + return md5($node->title . $node->nid); } /** From e0271503a15c120809cc1adacf48537a7af04188 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 20:09:28 -0500 Subject: [PATCH 1056/3476] Finishing up webhook display. --- devshop_projects/inc/forms.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index cdfc31f5f..37eecb274 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -508,7 +508,7 @@ function devshop_projects_form(&$node) { } // @TODO: is there a better way to save certain values? We lose data without these. - $form['project']['settings']['deploy']['webhook_'] = array( + $form['project']['settings']['deploy']['last_webhook'] = array( '#type' => 'value', '#value' => $node->project->settings->deploy['last_webhook'], ); From 77eebb94466e5931031173500ae8b3087e5d1fec Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 20:49:22 -0500 Subject: [PATCH 1057/3476] Replace pull task with deploy task on webhook invoke. --- devshop_pull/devshop_pull.inc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index efc523c1e..cd5887714 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -131,14 +131,19 @@ function devshop_pull_github_webhook($project_node){ foreach ($project->environments as $environment_name => $environment) { // Only pull if pull disabled or is tracking a tag. if (!$environment->settings->pull_disabled && !in_array($environment->git_ref, $project->settings->git['tags'])) { - $environments_to_pull[] = $environment_name; + $environments_to_pull[] = $environment->name; + + $args = array(); + $args['git_ref'] = $environment->git_ref; + $args['update'] = $environment->settings->deploy['update']; + $args['revert'] = $environment->settings->deploy['revert']; + $args['cache'] = $environment->settings->deploy['cache']; + + hosting_add_task($environment->site, 'devshop-deploy', $args); } } - // Set arguments and create "Pull" task. - $args['environments'] = implode(' ', $environments_to_pull); - hosting_add_task($project_node->nid, 'devshop-pull', $args); - $message = 'Push Received. Deploying code to environments: ' . $args['environments']; + $message = 'Push Received. Deploying code to environments: ' . implode(', ', $environments_to_pull); break; case 'pull_request': From a901b4f9955bbebeb6b7a244acdd2a9d9793632f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 20:54:33 -0500 Subject: [PATCH 1058/3476] Act on pull request method, cloning from live if specified. --- devshop_pull/devshop_pull.inc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index cd5887714..f8fc57a13 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -162,8 +162,14 @@ function devshop_pull_github_webhook($project_node){ if ($data->action == 'opened'){ $message = "Detected Pull Request creation for $branch \n"; - // @TODO: Allow cloning from "live" (or other) environment. - hosting_create_environment($project_node, $environment_name, $branch); + // If method is "install"... + if ($project->settings->github['pull_request_environments_method'] == 'install') { + hosting_create_environment($project_node, $environment_name, $branch); + } + // Otherwise, it is a clone from live. + else { + hosting_create_environment($project_node, $environment_name, $branch, $project->settings->live['live_environment']); + } $message .= "Environment $environment_name created for $project_node->title \n"; } From 824765e8f5509b6c5ba259373cf16edf7a6c0bcd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 8 Dec 2014 20:56:40 -0500 Subject: [PATCH 1059/3476] removing no_Verify on webhook. That's when we need to check for new branches! --- devshop_pull/devshop_pull.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index f8fc57a13..14e1a55cf 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -61,7 +61,6 @@ function devshop_pull_callback($project_hosting_context, $hash) { $deploy_settings['last_webhook_ip'] = ip_address(); $project_node->project->settings->deploy = $deploy_settings; - $project_node->no_verify; node_save($project_node); From d1ce3d5ebfb6108fdad96068f18b7943efffa82b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 13:51:41 -0500 Subject: [PATCH 1060/3476] When creating a new environment, use git checkout instead of --branch, since git_ref could be a tag. --- devshop_projects/devshop_projects.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 67386f868..4d9da7874 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -137,7 +137,7 @@ function drush_devshop_platform_verify(){ // If a branch is selected, add --branch to the command. // @TODO: Won't the platform ALWAYS have a git_branch? if ($git_ref) { - $command .= " --branch $git_ref"; + $command .= " && git checkout $git_ref"; } } // If the platform has been verified and has a branch and git url From dbd639f2c66d3a8e2383b4ebc1517eae3ff795da Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 15:11:06 -0500 Subject: [PATCH 1061/3476] Fork/Clone refactor start. --- devshop_projects/tasks/create.inc | 74 ++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 7c77aaf30..9b265a888 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -23,8 +23,10 @@ function hosting_task_devshop_create_form($node) { $site_to_fork = arg(4); if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { if (!empty($project->environments[$site->environment->name])){ - - $form['branch_source'] = array( + $form['action'] = array( + '#value' => 'fork' + ); + $form['branch_parent'] = array( '#type' => 'item', '#title' => t('Fork from'), '#value' => t('!env environment on branch !branch', array('!env' => "{$site->environment->name}", '!branch' => "{$site->environment->git_ref}")), @@ -33,7 +35,7 @@ function hosting_task_devshop_create_form($node) { '#type' => 'hidden', '#value' => $site->environment->name, ); - $form['new_branch'] = array( + $form['branch'] = array( '#title' => t('New branch name'), '#description' => t('Pick a name for your new git branch.'), '#type' => 'textfield', @@ -50,6 +52,9 @@ function hosting_task_devshop_create_form($node) { if ($site_to_clone && is_numeric($site_to_clone) && $site = node_load($site_to_clone)) { if (!empty($project->environments[$site->environment->name])){ + $form['action'] = array( + '#value' => 'clone' + ); $form['environment_source_display'] = array( '#type' => 'item', '#title' => t('Clone'), @@ -68,6 +73,9 @@ function hosting_task_devshop_create_form($node) { } } else { + $form['action'] = array( + '#value' => 'create' + ); $form['branch'] = array( '#title' => t('Branch/Tag'), '#description' => t('Choose the git branch or tag you wish to use for this new environment. Note: If you do not see all remote branches, You can !link', array('!link' => l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link modalframe-exclude', 'target' => '_parent'), 'query' => array('token' => drupal_get_token($user->uid)))))), @@ -143,17 +151,27 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ /** * Extra submit function for hosting_task_confirm_form() * - * @see devshop_projects_form_alter(). We had to add the submit hadler there. + * @see devshop_projects_form_alter(). We had to add the submit handler there. */ function hosting_task_devshop_create_form_submit($form, &$form_state) { $project = node_load($form_state['values']['nid']); $environment_name = $form_state['values']['parameters']['environment_name']; - $branch = !empty($form_state['values']['parameters']['new_branch'])? $form_state['values']['parameters']['new_branch']: $form_state['values']['parameters']['branch']; - $environment_source = $form_state['values']['parameters']['environment_source']; $environment_settings = $form_state['values']['parameters']['project']['environment']['settings']; - hosting_create_environment($project, $environment_name, $branch, $environment_source, $environment_settings); + $action = $form_state['values']['parameters']['action']; + $branch = $form_state['values']['parameters']['branch']; + + if ($action == 'clone') { + + } + elseif ($action == 'create') { + } + elseif ($action == 'fork') { + $branch_parent = $form_state['values']['parameters']['branch_parent']; + } + + hosting_create_environment($project, $environment_name, $branch, $form_state['values']['parameters']['environment_source'], $environment_settings); // We are replacing hosting_confirm_form_submit here, so just do what it does, // minus the hosting task creation! @@ -172,21 +190,21 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { * @TODO: Add more options like web server, etc. * @param $project * A full project node (for now. @TODO Allow project name (and/or nid) as parameters. - * @param $environment_name + * @param $new_environment_name * A new name for the environment - * @param $branch - * What git branch to track. - * @param $fork_source - * If desired, the environment to fork off of. (Copy the database and create a new branch from) + * @param $new_environment_branch + * What git branch to track. If FALSE, will not fork. + * @param $source_environment + * If desired, the environment to clone. (Copy the database and create a new branch from) */ -function hosting_create_environment($project_node, $environment_name, $branch, $fork_source = NULL, $settings = NULL) { +function hosting_create_environment($project_node, $new_environment_name, $new_environment_branch = NULL, $source_environment = NULL, $fork_source_environment = NULL, $settings = NULL) { global $user; $project = $project_node->project; // First, create a platform node. Only aegir properties are needed. $platform = new stdClass(); $platform->type = 'platform'; - $platform->title = $project_node->title . '_' . $environment_name; + $platform->title = $project_node->title . '_' . $new_environment_name; $platform->status = 1; $platform->uid = $user->uid; $platform->name = $user->name; @@ -198,10 +216,10 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ // If drupal path, append to code path/environment name if ($project->drupal_path) { - $platform->publish_path = $project->code_path . '/' . $environment_name . '/' . $project->drupal_path; + $platform->publish_path = $project->code_path . '/' . $new_environment_name . '/' . $project->drupal_path; } else { - $platform->publish_path = $project->code_path . '/' . $environment_name; + $platform->publish_path = $project->code_path . '/' . $new_environment_name; } // Save the platform node. @@ -209,12 +227,24 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ node_save($platform); } + // If no new branch specified and fork source is present, set branch to forked environment's branch. + if (empty($new_environment_branch) && $source_environment) { + $branch = $project->environments[$source_environment]->git_ref; + } + // If no new branch and fork source not mentioned, return false. + elseif (empty($new_environment_branch) && empty($source_environment)) { + return FALSE; + } + else { + $branch = $new_environment_branch; + } + // If cloning or forking, check if source environment exists... - if (isset($project_node->project->environments[$fork_source])) { - $source_environment = $project_node->project->environments[$fork_source]; + if (isset($project_node->project->environments[$source_environment])) { + $source_environment = $project_node->project->environments[$source_environment]; $environment = new stdClass(); - $environment->name = $environment_name; + $environment->name = $new_environment_name; $environment->git_ref = $branch; $environment->platform = $platform->nid; @@ -223,13 +253,15 @@ function hosting_create_environment($project_node, $environment_name, $branch, $ // Save Clone/fork specific data to the environment $environment->settings->site_to_clone = $source_environment->site; - $environment->settings->branch_to_fork = $source_environment->git_ref; + if ($action == 'fork') { + $environment->settings->branch_to_fork = $source_environment->git_ref; + } } else { // Next, add the environment record. $environment = new stdClass(); - $environment->name = $environment_name; + $environment->name = $new_environment_name; $environment->git_ref = $branch; $environment->platform = $platform->nid; From fe9b3559ca3733813779344bb6f59c0b97c999fd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 15:13:52 -0500 Subject: [PATCH 1062/3476] Fixing value of action item. --- devshop_projects/tasks/create.inc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 9b265a888..93a5df657 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -24,6 +24,7 @@ function hosting_task_devshop_create_form($node) { if ($site_to_fork && is_numeric($site_to_fork) && $site = node_load($site_to_fork)) { if (!empty($project->environments[$site->environment->name])){ $form['action'] = array( + '#type' => 'value', '#value' => 'fork' ); $form['branch_parent'] = array( @@ -53,6 +54,7 @@ function hosting_task_devshop_create_form($node) { if (!empty($project->environments[$site->environment->name])){ $form['action'] = array( + '#type' => 'value', '#value' => 'clone' ); $form['environment_source_display'] = array( @@ -74,7 +76,8 @@ function hosting_task_devshop_create_form($node) { } else { $form['action'] = array( - '#value' => 'create' + '#type' => 'value', + '#value' => 'create', ); $form['branch'] = array( '#title' => t('Branch/Tag'), @@ -104,11 +107,11 @@ function hosting_task_devshop_create_form($node) { ); } - // @TODO: I don't think this is needed. hosting_task_devshop_create_form_submit() looks for nid, these values end up in $form_state['values']['paramters']; - $form['project_nid'] = array( - '#type' => 'value', - '#value' => $node->nid, - ); +// // @TODO: I don't think this is needed. hosting_task_devshop_create_form_submit() looks for nid, these values end up in $form_state['values']['paramters']; +// $form['project_nid'] = array( +// '#type' => 'value', +// '#value' => $node->nid, +// ); // Load in environment settings // Load project and it's form From 6716c71eca62d5a5795ef8812b87f728c41ee212 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 15:53:26 -0500 Subject: [PATCH 1063/3476] Refactoring create / fork / clone form. --- devshop_projects/tasks/create.inc | 50 ++++++++++++++----------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/devshop_projects/tasks/create.inc b/devshop_projects/tasks/create.inc index 93a5df657..19b3f28a2 100644 --- a/devshop_projects/tasks/create.inc +++ b/devshop_projects/tasks/create.inc @@ -27,16 +27,16 @@ function hosting_task_devshop_create_form($node) { '#type' => 'value', '#value' => 'fork' ); - $form['branch_parent'] = array( + $form['fork_display'] = array( '#type' => 'item', '#title' => t('Fork from'), '#value' => t('!env environment on branch !branch', array('!env' => "{$site->environment->name}", '!branch' => "{$site->environment->git_ref}")), ); $form['environment_source'] = array( - '#type' => 'hidden', + '#type' => 'value', '#value' => $site->environment->name, ); - $form['branch'] = array( + $form['git_ref'] = array( '#title' => t('New branch name'), '#description' => t('Pick a name for your new git branch.'), '#type' => 'textfield', @@ -63,11 +63,11 @@ function hosting_task_devshop_create_form($node) { '#value' => t('!env environment', array('!env' => "{$site->environment->name}")), ); $form['environment_source'] = array( - '#type' => 'hidden', + '#type' => 'value', '#value' => $site->environment->name, ); - $form['branch'] = array( - '#type' => 'hidden', + $form['git_ref'] = array( + '#type' => 'value', '#value' => $site->environment->git_ref, ); } @@ -79,13 +79,17 @@ function hosting_task_devshop_create_form($node) { '#type' => 'value', '#value' => 'create', ); - $form['branch'] = array( + $form['git_ref'] = array( '#title' => t('Branch/Tag'), '#description' => t('Choose the git branch or tag you wish to use for this new environment. Note: If you do not see all remote branches, You can !link', array('!link' => l(t('Refresh branches'), 'node/' . $node->nid . '/project_verify', array('attributes' => array('class' => 'refresh-link modalframe-exclude', 'target' => '_parent'), 'query' => array('token' => drupal_get_token($user->uid)))))), '#type' => 'select', '#options' => $branch_options, '#required' => TRUE, ); + $form['environment_source'] = array( + '#type' => 'value', + '#value' => NULL, + ); } $form['environment_name'] = array( '#title' => t('New Environment Name'), @@ -158,35 +162,25 @@ function hosting_task_devshop_create_form_validate($form, &$form_state){ */ function hosting_task_devshop_create_form_submit($form, &$form_state) { - $project = node_load($form_state['values']['nid']); + $project_node = node_load($form_state['values']['nid']); $environment_name = $form_state['values']['parameters']['environment_name']; - $environment_settings = $form_state['values']['parameters']['project']['environment']['settings']; + $environment_settings = $form_state['values']['parameters']['environment']['settings']; $action = $form_state['values']['parameters']['action']; - $branch = $form_state['values']['parameters']['branch']; - - if ($action == 'clone') { + $git_ref = $form_state['values']['parameters']['git_ref']; + $environment_to_clone = $form_state['values']['parameters']['environment_source']; - } - elseif ($action == 'create') { - } - elseif ($action == 'fork') { - $branch_parent = $form_state['values']['parameters']['branch_parent']; - } - - hosting_create_environment($project, $environment_name, $branch, $form_state['values']['parameters']['environment_source'], $environment_settings); + hosting_create_environment($project_node->project, $environment_name, $git_ref, $environment_to_clone, $environment_settings, $action); // We are replacing hosting_confirm_form_submit here, so just do what it does, // minus the hosting task creation! $values = $form_state['values']; $form_state['redirect'] = 'node/' . $values['nid']; - modalframe_close_dialog(); // Friendly message drupal_set_message(t('Your environment is being created.')); } - /** * API-level function for creating a new environment. * @@ -200,14 +194,14 @@ function hosting_task_devshop_create_form_submit($form, &$form_state) { * @param $source_environment * If desired, the environment to clone. (Copy the database and create a new branch from) */ -function hosting_create_environment($project_node, $new_environment_name, $new_environment_branch = NULL, $source_environment = NULL, $fork_source_environment = NULL, $settings = NULL) { +function hosting_create_environment($project, $new_environment_name, $new_environment_branch = NULL, $source_environment = NULL, $settings = NULL, $action = 'create') { + global $user; - $project = $project_node->project; // First, create a platform node. Only aegir properties are needed. $platform = new stdClass(); $platform->type = 'platform'; - $platform->title = $project_node->title . '_' . $new_environment_name; + $platform->title = $project->name . '_' . $new_environment_name; $platform->status = 1; $platform->uid = $user->uid; $platform->name = $user->name; @@ -243,8 +237,8 @@ function hosting_create_environment($project_node, $new_environment_name, $new_e } // If cloning or forking, check if source environment exists... - if (isset($project_node->project->environments[$source_environment])) { - $source_environment = $project_node->project->environments[$source_environment]; + if (isset($project->environments[$source_environment])) { + $source_environment = $project->environments[$source_environment]; $environment = new stdClass(); $environment->name = $new_environment_name; @@ -272,6 +266,6 @@ function hosting_create_environment($project_node, $new_environment_name, $new_e $environment->settings = $settings; } - db_query('INSERT INTO {hosting_devshop_project_environment} (project_nid, name, git_ref, platform, settings) VALUES (%d, "%s", "%s", %d, "%s");', $project_node->nid, $environment->name, $environment->git_ref, $environment->platform, serialize($environment->settings)); + db_query('INSERT INTO {hosting_devshop_project_environment} (project_nid, name, git_ref, platform, settings) VALUES (%d, "%s", "%s", %d, "%s");', $project->nid, $environment->name, $environment->git_ref, $environment->platform, serialize($environment->settings)); } From eba079500effd68a74b814eb1a547fa8e4ce1e16 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 15:59:36 -0500 Subject: [PATCH 1064/3476] Fixing environment creation commands so you can create a tag environment from scratch. --- devshop_projects/devshop_projects.drush.inc | 55 +++++++++++---------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 4d9da7874..d6352474c 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -115,6 +115,7 @@ function drush_devshop_platform_verify(){ $create_branch = FALSE; $output = ''; + $commands = array(); //Remove drupal_path to clone. if ($drupal_path) { @@ -132,12 +133,12 @@ function drush_devshop_platform_verify(){ drush_log(dt("[DEVSHOP] No Repo found at !root. Cloning from !git", array('!git' => $git_remote, '!root' => $root))); // Build the clone command string - $command = "git clone --recursive $git_remote $root"; + $commands[] = "git clone --recursive $git_remote $root"; // If a branch is selected, add --branch to the command. // @TODO: Won't the platform ALWAYS have a git_branch? if ($git_ref) { - $command .= " && git checkout $git_ref"; + $commands[] = "git checkout $git_ref"; } } // If the platform has been verified and has a branch and git url @@ -148,21 +149,21 @@ function drush_devshop_platform_verify(){ $output .= _devshop_projects_git_execute('git fetch', $root); // Build the command string - $command = "git checkout $git_ref"; + $commands[] = "git checkout $git_ref"; } // Execute clone or checkout. - $output .= _devshop_projects_git_execute($command, $root); + $output .= _devshop_projects_git_execute($commands, $root); // If branch needs to be created (for "fork environment") if ($create_branch) { + $commands = array(); // Checkout the new branch. - $command = "git checkout -b {$platform->environment->git_ref}"; - $output .= _devshop_projects_git_execute($command, $root); + $commands[] = "git checkout -b {$platform->environment->git_ref}"; // Push the branch - $command = "git push -u origin {$platform->environment->git_ref}"; - $output .= _devshop_projects_git_execute($command, $root); + $commands[] = "git push -u origin {$platform->environment->git_ref}"; + $output .= _devshop_projects_git_execute($commands, $root); } return $output; @@ -333,24 +334,28 @@ function devshop_projects_post_hosting_import_task($task, $data) { /** * Utility for execute git commands. */ -function _devshop_projects_git_execute($command, $root) { +function _devshop_projects_git_execute($commands, $root) { // Execute - if (!empty($command)){ - drush_log('[DEVSHOP] Running: ' . $command); - - // @TODO: Create a d()->server->shell_cd_and_exec() function - // server->shell_exec() uses escapeshellcmd(), so we cannot cd_and_exec! - // So instead, this is the code from d()->server->shell_exec - // d()->server->shell_exec($cmd); - if (provision_is_local_host(d()->server->remote_host)) { - drush_shell_cd_and_exec($root, escapeshellcmd($command)); - } - else { - drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); - } + $return = ''; + foreach ($commands as $command) { + if (!empty($command)){ + drush_log('[DEVSHOP] Running: ' . $command); + + // @TODO: Create a d()->server->shell_cd_and_exec() function + // server->shell_exec() uses escapeshellcmd(), so we cannot cd_and_exec! + // So instead, this is the code from d()->server->shell_exec + // d()->server->shell_exec($cmd); + if (provision_is_local_host(d()->server->remote_host)) { + drush_shell_cd_and_exec($root, escapeshellcmd($command)); + } + else { + drush_shell_cd_and_exec($root, 'ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' %s %s', $this->script_user . '@' . d()->server->remote_host, escapeshellcmd($command)); + } - $output = drush_shell_exec_output(); - drush_log('Shell Output: ' . implode("\n", $output) , 'warning'); - return $output; + $output = drush_shell_exec_output(); + drush_log('Shell Output: ' . implode("\n", $output) , 'warning'); + $return .= $output; + } } + return $return; } From 3932facc0ff09a6e47fbab6b1e596a22ee09df18 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 16:04:24 -0500 Subject: [PATCH 1065/3476] Setting page title for clone and fork. --- devshop_projects/inc/forms.inc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 37eecb274..99d6a0a60 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -60,7 +60,16 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ // Create Environment form. case 'devshop-create': - drupal_set_title(t('Create new environment')); + + if (arg(3) == 'clone') { + drupal_set_title(t('Clone environment')); + } + elseif (arg(3) == 'fork') { + drupal_set_title(t('Fork environment')); + } + else { + drupal_set_title(t('Create new environment')); + } $form['#submit'] = array('hosting_task_devshop_create_form_submit'); break; From 7949f553778a269b83d3b34963479af442bd8d02 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 00:04:13 +0000 Subject: [PATCH 1066/3476] dont load deleted platforms as environments --- devshop_projects/inc/nodes.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index cb8418fb5..937eebfb6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -74,8 +74,10 @@ function devshop_projects_load($node) { LEFT JOIN {node} db ON s.db_server = db.nid LEFT JOIN {node} n ON e.project_nid = n.nid WHERE project_nid = %d AND - e.name != '' - ORDER BY name; ", $node->nid); + p.status <> %d AND + s.status <> %d AND + e.name != '' + ORDER BY name; ", $node->nid, HOSTING_PLATFORM_DELETED, HOSTING_SITE_DELETED); $environments = array(); while ($environment = db_fetch_object($query)) { From 96d613ecdcf5dec0999b171312692848da57ab4c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 19:14:28 -0500 Subject: [PATCH 1067/3476] Pass "project", not project_node. --- devshop_pull/devshop_pull.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 14e1a55cf..e68ea337b 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -163,11 +163,11 @@ function devshop_pull_github_webhook($project_node){ // If method is "install"... if ($project->settings->github['pull_request_environments_method'] == 'install') { - hosting_create_environment($project_node, $environment_name, $branch); + hosting_create_environment($project, $environment_name, $branch); } // Otherwise, it is a clone from live. else { - hosting_create_environment($project_node, $environment_name, $branch, $project->settings->live['live_environment']); + hosting_create_environment($project, $environment_name, $branch, $project->settings->live['live_environment']); } $message .= "Environment $environment_name created for $project_node->title \n"; } From 5364c6efe550402bb141f1008483b9b3744de1be Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 00:31:31 +0000 Subject: [PATCH 1068/3476] Revert "dont load deleted platforms as environments" This reverts commit 7949f553778a269b83d3b34963479af442bd8d02. --- devshop_projects/inc/nodes.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 937eebfb6..cb8418fb5 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -74,10 +74,8 @@ function devshop_projects_load($node) { LEFT JOIN {node} db ON s.db_server = db.nid LEFT JOIN {node} n ON e.project_nid = n.nid WHERE project_nid = %d AND - p.status <> %d AND - s.status <> %d AND - e.name != '' - ORDER BY name; ", $node->nid, HOSTING_PLATFORM_DELETED, HOSTING_SITE_DELETED); + e.name != '' + ORDER BY name; ", $node->nid); $environments = array(); while ($environment = db_fetch_object($query)) { From dde3920d056c4eeedf73675eaeaefb377d063384 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 9 Dec 2014 20:09:44 -0500 Subject: [PATCH 1069/3476] Attempt at deleting repo when platform is deleted. --- devshop_projects/devshop_projects.drush.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index d6352474c..45cd3f837 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -199,6 +199,12 @@ function devshop_projects_post_hosting_delete_task($task, $data) { // When a platform is deleted (if it is in a project), delete the environment record. if ($task->ref->type == 'platform' && !empty($task->ref->project)) { db_query('DELETE FROM {hosting_devshop_project_environment} WHERE project_nid = %d AND platform = %d', $task->ref->project->nid, $task->ref->platform); + + // If drupal root is not repo root, delete folder at code_path. + $repo_path = str_replace($task->ref->project->drupal_path, '', $task->ref->publish_path); + if (file_exists($repo_path) ) { + _provision_recursive_delete($repo_path); + } } // When a platform is deleted, if it is the last in the project, From a8f563e8c02b48c2fa6045870cbfb0f890725b01 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 12:39:54 -0500 Subject: [PATCH 1070/3476] Save the entire environment and project objects to drush alias. --- devshop_projects/drush/contexts.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/drush/contexts.inc b/devshop_projects/drush/contexts.inc index e4a417dbe..a3985f518 100644 --- a/devshop_projects/drush/contexts.inc +++ b/devshop_projects/drush/contexts.inc @@ -40,8 +40,8 @@ function devshop_projects_hosting_project_context_options(&$task) { function devshop_projects_hosting_site_context_options(&$task) { if (isset($task->ref->environment)) { - $task->context_options['environment'] = $task->ref->environment->name; - $task->context_options['project'] = $task->ref->environment->project_name; + $task->context_options['environment'] = $task->ref->environment; + $task->context_options['project'] = $task->ref->project; $task->context_options['git_ref'] = $task->ref->environment->git_ref; } } From 35d800cee97350a2f11240c5eb12f57fbbb65241 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 12:49:55 -0500 Subject: [PATCH 1071/3476] Use project name, not the whole project. --- devshop_projects/drush/contexts.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/drush/contexts.inc b/devshop_projects/drush/contexts.inc index a3985f518..7d2307462 100644 --- a/devshop_projects/drush/contexts.inc +++ b/devshop_projects/drush/contexts.inc @@ -41,7 +41,7 @@ function devshop_projects_hosting_site_context_options(&$task) { if (isset($task->ref->environment)) { $task->context_options['environment'] = $task->ref->environment; - $task->context_options['project'] = $task->ref->project; + $task->context_options['project'] = $task->ref->project->name; $task->context_options['git_ref'] = $task->ref->environment->git_ref; } } From ef9ddb67bc4022d7fdfaf5b9a1a187a17bae6c97 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 14:06:53 -0500 Subject: [PATCH 1072/3476] Check for branch when receiving github push notification. --- devshop_pull/devshop_pull.inc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index e68ea337b..4f78c9ff4 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -123,13 +123,15 @@ function devshop_pull_github_webhook($project_node){ break; case 'push': - // @TODO: Limit "Pull" tasks to only run for the branches we have new code for.. + // Limit "Deploy" tasks to only run for the branches we have new code for.. + $git_ref = strtr($data->ref, array('refs/tags/' => '', 'refs/heads/' => '')); // Check for environments set to pull $environments_to_pull = array(); foreach ($project->environments as $environment_name => $environment) { - // Only pull if pull disabled or is tracking a tag. - if (!$environment->settings->pull_disabled && !in_array($environment->git_ref, $project->settings->git['tags'])) { + + // Only pull if deploy is not disabled or if environment is tracking a tag. + if ($git_ref == $environment->git_ref && !$environment->settings->pull_disabled && !in_array($environment->git_ref, $project->settings->git['tags'])) { $environments_to_pull[] = $environment->name; $args = array(); @@ -142,7 +144,7 @@ function devshop_pull_github_webhook($project_node){ } } - $message = 'Push Received. Deploying code to environments: ' . implode(', ', $environments_to_pull); + $message = "Push Received for git ref $git_ref. Deploying code to environments: " . implode(', ', $environments_to_pull); break; case 'pull_request': From b4a3c22d955ca1160754cc3f2ca84beb1f80b759 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 20:02:19 -0500 Subject: [PATCH 1073/3476] Getting basic drush aliases working. --- devshop_projects/devshop_projects.module | 1 + devshop_projects/inc/drush.inc | 28 ++++++++++++++++++++++++ devshop_projects/inc/nodes.inc | 3 +++ devshop_projects/inc/theme.inc | 4 +++- devshop_projects/inc/ui.inc | 4 ++++ devshop_projects/project-nav.tpl.php | 10 +++++---- 6 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 devshop_projects/inc/drush.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 6dac0a03f..46cc27420 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -5,6 +5,7 @@ * Provides Node Type, UI, and tools for DevShop Projects. */ +include_once('inc/drush.inc'); include_once('inc/forms.inc'); include_once('inc/nodes.inc'); include_once('inc/queue.inc'); diff --git a/devshop_projects/inc/drush.inc b/devshop_projects/inc/drush.inc new file mode 100644 index 000000000..094b1bfd8 --- /dev/null +++ b/devshop_projects/inc/drush.inc @@ -0,0 +1,28 @@ +environments as $name => $environment) { + + $remote_user = 'aegir'; + $remote_host = $environment->remote_host; + $root = $environment->root; + $uri = $environment->default_domain; + + $output .= << '$root', + 'uri' => '$uri', + 'remote-user' => '$remote_user', + 'remote-host' => '$remote_host', +); + +PHP; + } + + return $output; +} \ No newline at end of file diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index cb8418fb5..2e7c2edf9 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -61,6 +61,9 @@ function devshop_projects_load($node) { e.*, s.status as site_status, p.status as platform_status, + p.publish_path as root, + http.title as remote_host, + sn.title as uri, http.title as web_server, db.title as db_server, n.title as project_name, diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 27e28a470..2618a609f 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -44,7 +44,7 @@ function theme_devshop_projects_settings_form($form) { $rows[] = $row; } $output = theme('table', $header, $rows, array('id' => 'project-settings-table')); - return $output; + return $output; } /** @@ -173,4 +173,6 @@ function template_preprocess_devshop_project_nav(&$vars){ // if (user_access('access project logs')){ // $vars['logs_link'] = l(' ' . t('Logs'), "node/$node->nid/logs", array('html' => TRUE)); // } + + $vars['drush_aliases'] = devshop_project_aliases($project); } \ No newline at end of file diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 1c51cefd4..c6a9cb902 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -114,6 +114,10 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { '#suffix' => '
    ' ); + $node->content['info'] = array( + '#value' => devshop_project_aliases($node->project), + ); + //$node->content['info']['code_path'] = array( // '#type' => 'item', // '#title' => t('Code path'), diff --git a/devshop_projects/project-nav.tpl.php b/devshop_projects/project-nav.tpl.php index cef0dcf55..8765eade9 100644 --- a/devshop_projects/project-nav.tpl.php +++ b/devshop_projects/project-nav.tpl.php @@ -2,13 +2,15 @@
    + + + + + + - From 26f9085659a8c415ec50b52d2ec352100e7e142d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 21:45:32 -0500 Subject: [PATCH 1077/3476] Track and display last webhook status. --- devshop_pull/devshop_pull.inc | 1 + devshop_pull/devshop_pull.settings.inc | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/devshop_pull/devshop_pull.inc b/devshop_pull/devshop_pull.inc index 4f78c9ff4..f648b0cc8 100644 --- a/devshop_pull/devshop_pull.inc +++ b/devshop_pull/devshop_pull.inc @@ -72,6 +72,7 @@ function devshop_pull_callback($project_hosting_context, $hash) { // Save a variable to help when using the settings page. variable_set('devshop_pull_last_ip', ip_address()); + variable_set('devshop_pull_last_status', $status); } /** diff --git a/devshop_pull/devshop_pull.settings.inc b/devshop_pull/devshop_pull.settings.inc index b84c5e789..75f65391d 100644 --- a/devshop_pull/devshop_pull.settings.inc +++ b/devshop_pull/devshop_pull.settings.inc @@ -21,7 +21,7 @@ function devshop_pull_settings_form() { // we have a few bullet points $items = array(); - $items[] = t('Enter the IP addresses that are allowed to trigger a "Pull Code" task. You may specify address ranges using CIDR notation (e.g. 192.168.1.0/24).'); + $items[] = t('Enter the IP addresses that are allowed to trigger a "Deploy" tasks. You may specify address ranges using CIDR notation (e.g. 192.168.1.0/24).'); $items[] = t('GitHub post-receive callback servers are: %github_ips.', array('%github_ips' => variable_get('devshop_pull_ip_acl', DEVSHOP_PULL_DEFAULT_ALLOWED_IPS))); $items[] = t('Your local computer\'s IP address is %ip. ', array('%ip' => $_SERVER['REMOTE_ADDR'])); @@ -32,6 +32,11 @@ function devshop_pull_settings_form() { } else { $items[] = t('No requests ever detected. If you add the trigger URL for a project to your git repo host, the IP will be logged and displayed here.'); } + + if (variable_get('devshop_pull_last_status', '') == DEVSHOP_PULL_STATUS_ACCESS_DENIED) { + $items[] = t('The last incoming webhook was denied because it is not in this list.'); + } + $form['devshop_pull_ip_acl']['#description'] = theme('item_list', $items); return system_settings_form($form); From 37670c076f4902bf27df0abef25db21caa5105b4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 21:55:26 -0500 Subject: [PATCH 1078/3476] Adding download drush aliases page. --- devshop_projects/devshop_projects.module | 11 +++++++++++ devshop_projects/inc/drush.inc | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 46cc27420..7f53dffeb 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -155,6 +155,17 @@ function devshop_projects_menu() { 'file path' => drupal_get_path('module', 'node'), 'type' => MENU_LOCAL_TASK, ); + // Drush aliases download. + $items['node/%node/aliases'] = array( + 'title' => 'Drush Aliases', + 'page callback' => 'devshop_project_drush_aliases_page', + 'page arguments' => array(1), + 'access callback' => 'node_access', + 'access arguments' => array('update', 1), + 'weight' => 1, + 'file' => 'inc/drush.inc', + 'type' => MENU_CALLBACK, + ); return $items; } diff --git a/devshop_projects/inc/drush.inc b/devshop_projects/inc/drush.inc index d4d77c04d..d931db6cc 100644 --- a/devshop_projects/inc/drush.inc +++ b/devshop_projects/inc/drush.inc @@ -6,6 +6,7 @@ function devshop_project_aliases($project) { $output = 'environments as $name => $environment) { $remote_user = 'aegir'; @@ -25,4 +26,16 @@ PHP; } return $output; +} + + +/** + * Downloads the drush aliases for this site. + * @param $project + */ +function devshop_project_drush_aliases_page($node) { + $project = $node->project; + $filename = $project->name . '.aliases.drushrc.php'; + header("Content-Disposition: attachment; filename='$filename'"); + print devshop_project_aliases($project); } \ No newline at end of file From 138c1c6521cffc230e1451a72679f0db79ceef89 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 21:59:28 -0500 Subject: [PATCH 1079/3476] Removing the disabling of "revert features" checkbox. Let's be smart about it. This prevents running it when we deploy a database, for example. --- devshop_projects/inc/forms.inc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 99d6a0a60..c960d96bf 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -245,12 +245,9 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ $form['environment']['settings']['deploy']['revert'] = array( '#type' => 'checkbox', '#title' => t('Revert all features.'), + '#description' => t('If features is enabled, revert all of them.'), '#default_value' => $environment->settings->deploy['revert'], ); - if (!_devshop_projects_site_has_module($environment->site, 'features')){ - $form['environment']['settings']['deploy']['revert']['#description'] .= ' ' . t('Features module is not enabled in this environment.'); - $form['environment']['settings']['deploy']['revert']['#disabled'] = 'disabled'; - } $form['environment']['settings']['deploy']['update'] = array( '#type' => 'checkbox', From 7809a769493583ed7b94315c81d95b908e5b2905 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 10 Dec 2014 22:01:41 -0500 Subject: [PATCH 1080/3476] Setting deploy hooks on as default. --- devshop_projects/inc/forms.inc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index c960d96bf..11def5659 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -246,18 +246,17 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#type' => 'checkbox', '#title' => t('Revert all features.'), '#description' => t('If features is enabled, revert all of them.'), - '#default_value' => $environment->settings->deploy['revert'], + '#default_value' => empty($node->nid)? 1: $environment->settings->deploy['revert'], ); - $form['environment']['settings']['deploy']['update'] = array( '#type' => 'checkbox', '#title' => t('Run database updates.'), - '#default_value' => $environment->settings->deploy['update'], + '#default_value' => empty($node->nid)? 1: $environment->settings->deploy['update'], ); $form['environment']['settings']['deploy']['cache'] = array( '#type' => 'checkbox', '#title' => t('Clear all caches.'), - '#default_value' => $environment->settings->deploy['cache'], + '#default_value' => empty($node->nid)? 1: $environment->settings->deploy['cache'], ); // Add our own submit handler From b32c087bc868471e2c6869a0079e88f5f254b07c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 11 Dec 2014 10:03:33 -0500 Subject: [PATCH 1081/3476] Making deploy hooks forms consistent. Removing blockage of features revert. --- devshop_projects/tasks/deploy.inc | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/devshop_projects/tasks/deploy.inc b/devshop_projects/tasks/deploy.inc index d9b9e7c69..5c0170541 100644 --- a/devshop_projects/tasks/deploy.inc +++ b/devshop_projects/tasks/deploy.inc @@ -58,25 +58,21 @@ function hosting_task_devshop_deploy_form($node) { ); } - $form['update'] = array( - '#title' => t('Run update.php after code pull?'), - '#type' => 'checkbox', - '#default_value' => $node->environment->settings->deploy['update'], - ); - $form['revert'] = array( - '#title' => t('Revert all features after code pull?'), + '#title' => t('Revert all features.'), + '#description' => t('If features is enabled, revert all of them.'), '#type' => 'checkbox', '#default_value' => $node->environment->settings->deploy['revert'], ); - if (!_devshop_projects_site_has_module($node->nid, 'features')){ - $form['revert']['#description'] .= ' ' . t('Features module is not enabled in this environment.'); - $form['revert']['#disabled'] = 'disabled'; - } + $form['update'] = array( + '#title' => t('Run database updates.'), + '#type' => 'checkbox', + '#default_value' => $node->environment->settings->deploy['update'], + ); $form['cache'] = array( - '#title' => t('Clear cache after code pull?'), + '#title' => t('Clear all caches.'), '#type' => 'checkbox', '#default_value' => $node->environment->settings->deploy['cache'], ); From 0e30c4319fb94ef6afaeb266436ad07e000bd43f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 11 Dec 2014 11:09:17 -0500 Subject: [PATCH 1082/3476] Moving drush aliases button. --- devshop_projects/project-nav.tpl.php | 69 ++++++++++++++-------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/devshop_projects/project-nav.tpl.php b/devshop_projects/project-nav.tpl.php index 200693108..76dc6b74f 100644 --- a/devshop_projects/project-nav.tpl.php +++ b/devshop_projects/project-nav.tpl.php @@ -17,8 +17,42 @@
  • - + +
  • + +
  • + + + @@ -68,39 +102,6 @@ - - - - - From e873677d6f75b6fa51d5308d5ccec293406f4fc1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 11 Dec 2014 20:59:42 -0500 Subject: [PATCH 1083/3476] Getting there with new tasks gear. --- devshop_projects/inc/ui.inc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index c6a9cb902..8ac7d94bc 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -363,20 +363,6 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = theme('ctools_dropdown', t('Actions'), $actions); $rows[] = $row; - // Environment Tasks - if ($environment->site) { - $environment->tasks = hosting_get_tasks('rid', $environment->site); - } - else { - $environment->tasks = hosting_get_tasks('rid', $environment->platform); - } - $environment->task_count = count($environment->tasks); - $environment->active_tasks = 0; - - if (function_exists('boots_render_tasks')){ - $environment->tasks_list = boots_render_tasks($environment->tasks, 'environment btn btn-small btn-link'); - } - foreach ($environment->tasks as &$task) { if ($task->task_status == HOSTING_TASK_QUEUED || $task->task_status == HOSTING_TASK_PROCESSING){ $environment->active_tasks++; From 30b821139374d54c9ea698465daecafc59032ff9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 11:45:50 -0500 Subject: [PATCH 1084/3476] Making hosting_sync form our own. --- devshop_projects/inc/forms.inc | 58 +++++++++++++++++++++++++++++++++- devshop_projects/inc/nodes.inc | 4 +++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 11def5659..5d09d0461 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -115,13 +115,69 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ } break; - // Alter title of deploy task. + // Deploy task form case 'devshop-deploy': + + // Alter title of deploy task. $node = node_load($form['nid']['#value']); drupal_set_title(t('Deploy code to Environment "@env"', array('@env' => $node->environment->name))); $form['actions']['cancel']['#value'] = l(t('Cancel'), "node/{$node->project->nid}"); $form['#submit'][] = 'devshop_deploy_redirect_to_project'; break; + + // Sync task form + case 'sync': + if (isset($_GET['source'])) { + $node = node_load($form['nid']['#value']); + $source = node_load($_GET['source']); + + if (empty($source) || $source->type != 'site') { + break; + } + + drupal_set_title(t('Deploy data to Environment "@env"', array('@env' => $node->environment->name))); + $environment = $node->environment; + $source_environment = $source->environment; + $form['#submit'][] = 'devshop_deploy_redirect_to_project'; + + $form['help'] = array( + '#prefix' => '', + + 'source' => array( + '#value' => t('Source: ') . l($source_environment->url, $source_environment->url, array('attributes' => array('target' => '_blank'))), + '#prefix' => '

    ', + '#suffix' => '

    ', + ), + 'target' => array( + '#value' => t('Target: ') . l($environment->url, $environment->url, array('attributes' => array('target' => '_blank'))) . ' ' . t('Data will be destroyed.') . '', + '#prefix' => '

    ', + '#suffix' => '

    ', + ), + ); + + // Alter the "source" item + $form['parameters']['source'] = array( + '#type' => 'value', + '#value' => $source->environment->drush_alias, + ); + + // Don't copy modules and themes. + unset($form['parameters']['modules']); + unset($form['parameters']['themes']); + unset($form['parameters']['libraries']); + + // Better output + $form['parameters']['database']['#prefix'] = ''; + $form['parameters']['registry-rebuild']['#prefix'] = ''; + $form['parameters']['backup']['#weight'] = -11; + + $form['actions']['submit']['#value'] = t('Deploy Data'); + $form['actions']['cancel']['#value'] = l(t('Cancel'), "node/{$node->project->nid}"); + + $form['warning']['#value'] = '
    ' . t('Clicking "Deploy Data" will DESTROY the database for the environment !link.', array('!link' => l($environment->url, $environment->url, array('attributes' => array('target' => '_blank'))))) . '
    '; + } + break; } } diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 2e7c2edf9..713589fa9 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -64,6 +64,7 @@ function devshop_projects_load($node) { p.publish_path as root, http.title as remote_host, sn.title as uri, + sn.title as drush_alias, http.title as web_server, db.title as db_server, n.title as project_name, @@ -87,6 +88,9 @@ function devshop_projects_load($node) { continue; } + // Drush alias + $environment->drush_alias = '@' . $environment->drush_alias; + // Unserialize environment settings. $environment->settings = (object) unserialize($environment->settings); From 5c0700b0a01d45c886cb13315faab712ee8897af Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 12:07:39 -0500 Subject: [PATCH 1085/3476] Issue #2027673: Removing devshop sync task! --- README.txt | 18 --- devshop_projects/devshop_projects.drush.inc | 12 -- devshop_projects/devshop_projects.module | 2 - devshop_projects/tasks/sync.inc | 116 -------------------- devshop_projects/tasks/tasks.inc | 6 - 5 files changed, 154 deletions(-) delete mode 100644 devshop_projects/tasks/sync.inc diff --git a/README.txt b/README.txt index c1e5188fc..fb51469ea 100644 --- a/README.txt +++ b/README.txt @@ -90,21 +90,3 @@ All tasks are run on project nodes, and all tasks have specific permissions, so - Commits the result, with a part automated and part customized commit message. - (Optionally) pushes the commits. - (Optionally) force-reverts after a commit. - -3. Sync Content - $ drush @project_NAME provision-devshop-sync SOURCE_ENVIRONMENT DESTINATION_ENVIRONMENT - - This task makes it easy to syncronize the database and filesdown from other - environments within the project. - - WARNING: This will DESTROY the destination site's database! - - This task: - - (optionally) Pulls code - - Drops the @destination database. - - Creates an SQL dump from @source. - - Copies the SQL dump to the local system (if @source is a remote). - - Imports the SQL dump into @destination database. - - (optionally) Runs update.php. - - (optionally) Runs features-revert-all. - - (optionally) Clears all caches. diff --git a/devshop_projects/devshop_projects.drush.inc b/devshop_projects/devshop_projects.drush.inc index 45cd3f837..01e900d54 100644 --- a/devshop_projects/devshop_projects.drush.inc +++ b/devshop_projects/devshop_projects.drush.inc @@ -59,18 +59,6 @@ function drush_devshop_projects_pre_hosting_task() { $task->options['revert'] = $task->task_args['revert']; } - // Sync - if ($task->ref->type == 'project' && $task->task_type == 'devshop-sync') { - $task->args[] = $task->task_args['source']; - $task->args[] = $task->task_args['destination']; - $task->options['database'] = $task->task_args['database']; - $task->options['files'] = $task->task_args['files']; - $task->options['pull'] = $task->task_args['pull']; - $task->options['update'] = $task->task_args['update']; - $task->options['revert'] = $task->task_args['revert']; - $task->options['cache'] = $task->task_args['cache']; - } - // Download if ($task->ref->type == 'project' && $task->task_type == 'devshop-dl') { $task->options['modules'] = $task->task_args['modules']; diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index 7f53dffeb..d50975279 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -17,7 +17,6 @@ include_once('tasks/tasks.inc'); include_once('tasks/commit.inc'); include_once('tasks/create.inc'); include_once('tasks/pull.inc'); -include_once('tasks/sync.inc'); include_once('tasks/deploy.inc'); @@ -42,7 +41,6 @@ function devshop_projects_perm() { 'create devshop-create task', 'create devshop-commit task', 'create devshop-pull task', - 'create devshop-sync task', 'create devshop-delete task', 'create devshop-deploy task', 'create project', diff --git a/devshop_projects/tasks/sync.inc b/devshop_projects/tasks/sync.inc deleted file mode 100644 index 5b449e44c..000000000 --- a/devshop_projects/tasks/sync.inc +++ /dev/null @@ -1,116 +0,0 @@ -nid; - $project = $node->project; - - $form = array(); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the source environment.'), 'source', 'Source'); - devshop_projects_tasks_add_environment_to_form($form, $node, t('Choose the destination environment. Data from this environment will be DESTROYED.'), 'destination', 'Destination'); - - $form['source']['#prefix'] = '
    '; - $form['source']['#suffix'] = '
    '; - $form['destination']['#prefix'] = '
    '; - $form['destination']['#suffix'] = '
    '; - - // Look for any locked sites. - foreach ($project->environments as $environment) { - if ($environment->settings->locked){ - // Remove live site from destination options. - unset($form['destination']['#options'][$environment->name]); - - $source_options = $form['source']['#options']; - unset($source_options[$environment->name]); - $form['source']['#options'] = array_merge(array($environment->name => $environment->name . " (Locked)"), $source_options); - $form['source']['#default_value'] = $environment->name; - } - } - - // Detect pre-selected environments - if ($_GET['source']) { - $form['source']['#default_value'] = $_GET['source']; - $form['destination']['#default_value'] = $_GET['dest']; - - $form['source']['#type'] = 'value'; - $form['destination']['#type'] = 'value'; - - $form['source_label'] = array( - '#type' => 'item', - '#title' => t('Source Environment'), - '#value' => $_GET['source'], - '#description' => t('The environment to copy data from.'), - '#prefix' => '
    ', - '#suffix' => '
    ', - ); - $form['destination_label'] = array( - '#type' => 'item', - '#title' => t('Destination Environment'), - '#value' => $_GET['dest'], - '#description' => t("This environment's data will be DESTROYED, replaced with data from the Source."), - '#prefix' => '
    ', - '#suffix' => '
    ', - ); - } - - $form['database'] = array( - '#title' => t('Copy database from source to destination.'), - '#type' => 'checkbox', - '#default_value' => 1, - '#prefix' => '
    What to sync', - ); - - $form['files'] = array( - '#title' => t('Copy files folder from source to destination.'), - '#type' => 'checkbox', - '#default_value' => 0, - '#suffix' => '
    ', - ); - - // We have to add the fieldset via prefix because of the way aegir handles this form. - $form['pull'] = array( - '#title' => t('Pull code on Destination'), - '#type' => 'checkbox', - '#default_value' => 1, - '#prefix' => '
    Actions', - ); - $form['update'] = array( - '#title' => t('Run update.php on Destination'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - if (_devshop_projects_project_has_module($node, 'features')){ - $form['revert'] = array( - '#title' => t('Revert all features on Destination'), - '#type' => 'checkbox', - '#default_value' => 1, - ); - } - $form['cache'] = array( - '#title' => t('Clear cache on Destination'), - '#type' => 'checkbox', - '#default_value' => 1, - '#suffix' => '
    ', - ); - $form['#validate'][] = 'hosting_task_devshop_sync_form_validate'; - return $form; -} - -/** - * Validation for hosting_task_devshop_sync_form() - */ -function hosting_task_devshop_sync_form_validate(&$form, &$form_state){ - // Can't sync to self - if ($form_state['values']['parameters']['source'] == $form_state['values']['parameters']['destination']){ - form_set_error('source', t('The source cannot be the same as the destination.')); - } -} \ No newline at end of file diff --git a/devshop_projects/tasks/tasks.inc b/devshop_projects/tasks/tasks.inc index 0858020e3..90d8aa437 100644 --- a/devshop_projects/tasks/tasks.inc +++ b/devshop_projects/tasks/tasks.inc @@ -35,12 +35,6 @@ function devshop_projects_hosting_tasks() { 'access callback' => 'devshop_hosting_task_menu_access', 'dialog' => TRUE, ); - $tasks['project']['devshop-sync'] = array( - 'title' => t('Sync Data'), - 'description' => t('Copy database and files from one environment to another.'), - 'access callback' => 'devshop_hosting_task_menu_access', - 'dialog' => TRUE, - ); $tasks['project']['delete'] = array( 'title' => t('Delete Project'), 'description' => t('Delete a project and all associated sites and platforms.'), From dbd6fb3b66285155a8cc125ed77e6a265ebb31de Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 12:23:14 -0500 Subject: [PATCH 1086/3476] Handle locked environments properly. --- devshop_projects/inc/forms.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 5d09d0461..b7dcc68f3 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -281,9 +281,9 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ ); $form['environment']['settings']['locked'] = array( '#type' => 'checkbox', - '#title' => t('Locked'), + '#title' => t('Lock Database'), '#default_value' => $environment->settings->locked, - '#description' => t('Prevent devshop users from overwriting the database.'), + '#description' => t('Prevent devshop users from destroying the database.') . '

    Drush users may still overwrite the database.

    ', ); $form['environment']['settings']['pull_disabled'] = array( From 6c5dee232c938c67090125cf08b2b9509efe3644 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 12:27:22 -0500 Subject: [PATCH 1087/3476] Fixing server nids. --- devshop_projects/inc/nodes.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 713589fa9..a5f4ec288 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -66,7 +66,9 @@ function devshop_projects_load($node) { sn.title as uri, sn.title as drush_alias, http.title as web_server, + http.nid as web_server_nid, db.title as db_server, + db.nid as db_server_nid, n.title as project_name, s.vid, sn.title as default_domain @@ -104,13 +106,13 @@ function devshop_projects_load($node) { // Get extra server info $environment->servers['http'] = array( - 'nid' => $environment->settings->web_server, + 'nid' => $environment->web_server_nid, 'name' => $environment->web_server ); // HTTP Server $environment->servers['db'] = array( - 'nid' => $environment->settings->db_server, + 'nid' => $environment->db_server, 'name' => $environment->db_server ); From eb24828209ed10cdadabf90e288affc0aa0f11bd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 12:31:47 -0500 Subject: [PATCH 1088/3476] Jazzing up the stack display. --- devshop_projects/inc/nodes.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index a5f4ec288..c357bc5e1 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -107,13 +107,13 @@ function devshop_projects_load($node) { // Get extra server info $environment->servers['http'] = array( 'nid' => $environment->web_server_nid, - 'name' => $environment->web_server + 'name' => $environment->web_server, ); // HTTP Server $environment->servers['db'] = array( 'nid' => $environment->db_server, - 'name' => $environment->db_server + 'name' => $environment->db_server, ); // Last commit From 017eab10669a1cff9323a9fe0e01db4b2a143c86 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 13:39:00 -0500 Subject: [PATCH 1089/3476] Getting additional actions on evironments. --- devshop_projects/inc/ui.inc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 8ac7d94bc..fc3a82de6 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -318,10 +318,18 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { } // The actions we want - $site_actions = array('flush_cache', 'login_reset', 'backup'); + $site_actions = array( + 'flush_cache', + 'login_reset', + 'backup', + 'restore' => 'restore', + 'rebuild_registry', + 'run_cron', + 'update', + ); - if (!$environment->settings->locked){ - $site_actions[] = 'restore'; + if ($environment->settings->locked){ + unset($site_actions['restore']); } // Add disable or delete task based on hosting variable. From 2b4234a7da5de67ef94f58543e4f138981e9e598 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 13:39:33 -0500 Subject: [PATCH 1090/3476] Prettying up the commit link. --- devshop_projects/inc/nodes.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index c357bc5e1..9a3f40dc8 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -119,7 +119,7 @@ function devshop_projects_load($node) { // Last commit $path = $project->code_path . '/' . $environment->name; // $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar
    %s' HEAD^...HEAD"); - $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar' --max-count=1"); + $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar %s' --max-count=1"); if (empty($environment->git_current)){ $environment->git_current = t('Cloning in progress...'); From 9d9e29fa67150bfe74580e4eaf3826a51fa137a3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 13:54:28 -0500 Subject: [PATCH 1091/3476] Moving drush aliases. --- devshop_projects/inc/theme.inc | 23 +++++++++++++---- devshop_projects/project-nav.tpl.php | 37 +--------------------------- 2 files changed, 19 insertions(+), 41 deletions(-) diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index e3f17e2da..98d048902 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -93,6 +93,24 @@ function devshop_projects_preprocess_page(&$vars){ } } + +/** + * Preprocess page + */ +function devshop_projects_preprocess_node(&$vars){ + + // On project node edit page + if (isset($vars['node']) && $vars['node']->type == 'project'){ + + $vars['drush_aliases'] = devshop_project_aliases($vars['node']->project); + $vars['aliases_url'] = url("node/{$vars['node']->nid}/aliases"); + + $vars['access_note'] = t('NOTE: To grant users authorization to access these environments with drush remotely, you must put their private key into the file /var/aegir/.ssh/authorized_keys.'); + } +} + + + /** * Preprocess for project_nav.tpl.php */ @@ -173,9 +191,4 @@ function template_preprocess_devshop_project_nav(&$vars){ // if (user_access('access project logs')){ // $vars['logs_link'] = l(' ' . t('Logs'), "node/$node->nid/logs", array('html' => TRUE)); // } - - $vars['drush_aliases'] = devshop_project_aliases($project); - $vars['aliases_url'] = url("node/{$node->nid}/aliases"); - - $vars['access_note'] = t('NOTE: To grant users authorization to access these environments with drush remotely, you must put their private key into the file /var/aegir/.ssh/authorized_keys.'); } \ No newline at end of file diff --git a/devshop_projects/project-nav.tpl.php b/devshop_projects/project-nav.tpl.php index 76dc6b74f..95d8c7e45 100644 --- a/devshop_projects/project-nav.tpl.php +++ b/devshop_projects/project-nav.tpl.php @@ -17,42 +17,7 @@
  • - -
  • - -
  • - - - - + From 2e62bc4bb4f11eb70b632e17107898440a7ce970 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 13:58:43 -0500 Subject: [PATCH 1092/3476] Make sure tasks is an array. --- devshop_projects/inc/ui.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index fc3a82de6..5fdf47e40 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -371,6 +371,7 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = theme('ctools_dropdown', t('Actions'), $actions); $rows[] = $row; + if (!is_array($environment->tasks)) $environment->tasks = array(); foreach ($environment->tasks as &$task) { if ($task->task_status == HOSTING_TASK_QUEUED || $task->task_status == HOSTING_TASK_PROCESSING){ $environment->active_tasks++; From 6ab1b32b7ef5691fad882d7b1ac6574ab791013f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 12 Dec 2014 14:09:27 -0500 Subject: [PATCH 1093/3476] Adding solr server detection and display. --- devshop_projects/inc/nodes.inc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 9a3f40dc8..61364eed3 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -104,6 +104,7 @@ function devshop_projects_load($node) { $release = hosting_package_instance_load($iid); $environment->version = $release->version; + // @TODO: Make "servers" more abstract so we can add many more. // Get extra server info $environment->servers['http'] = array( 'nid' => $environment->web_server_nid, @@ -116,6 +117,19 @@ function devshop_projects_load($node) { 'name' => $environment->db_server, ); + // Load Solr if present + if (module_exists('hosting_solr')) { + $server_nid = db_result(db_query("SELECT server FROM {hosting_solr} WHERE nid=%d", $environment->site)); + + $servers = hosting_get_servers('solr'); + if ($server_nid && isset($servers[$server_nid])) { + $environment->servers['solr'] = array( + 'nid' => $server_nid, + 'name' => $servers[$server_nid], + ); + } + } + // Last commit $path = $project->code_path . '/' . $environment->name; // $environment->git_current = shell_exec("cd $path; git log --pretty=format:'%h %an %ar
    %s' HEAD^...HEAD"); From 7d6d12d65d291f854fea9f77ded747a3986a7b56 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 17 Dec 2014 06:29:31 -0500 Subject: [PATCH 1094/3476] De-duplicating task loading. Fixing environment status classes. --- devshop_projects/inc/ui.inc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/devshop_projects/inc/ui.inc b/devshop_projects/inc/ui.inc index 5fdf47e40..10f9a1737 100644 --- a/devshop_projects/inc/ui.inc +++ b/devshop_projects/inc/ui.inc @@ -370,21 +370,21 @@ function devshop_projects_view($node, $teaser = FALSE, $page = FALSE) { $row[] = theme('ctools_dropdown', t('Actions'), $actions); $rows[] = $row; - - if (!is_array($environment->tasks)) $environment->tasks = array(); - foreach ($environment->tasks as &$task) { - if ($task->task_status == HOSTING_TASK_QUEUED || $task->task_status == HOSTING_TASK_PROCESSING){ - $environment->active_tasks++; - } - } - - // Progress Bar - if ($environment->active_tasks > 0){ - $environment->progress_classes = 'progress-bar-striped progress-bar-warning active'; - $environment->active_tasks++; - } else { - $environment->progress_classes = 'inactive'; - } +// +// if (!is_array($environment->tasks)) $environment->tasks = array(); +// foreach ($environment->tasks as &$task) { +// if ($task->task_status == HOSTING_TASK_QUEUED || $task->task_status == HOSTING_TASK_PROCESSING){ +// $environment->active_tasks++; +// } +// } +// +// // Progress Bar +// if ($environment->active_tasks > 0){ +// $environment->progress_classes = 'progress-bar-striped progress-bar-warning active'; +// $environment->active_tasks++; +// } else { +// $environment->progress_classes = 'inactive'; +// } } $header = array(); $table = theme('table', $header, $rows, array('class' => 'environments-table')); From 853414fdacdd180c345543ca0af28281b1b6d885 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 19 Dec 2014 14:11:08 -0500 Subject: [PATCH 1095/3476] Make repo root available as a property on an environment. --- devshop_projects/inc/nodes.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devshop_projects/inc/nodes.inc b/devshop_projects/inc/nodes.inc index 61364eed3..bedce0eb6 100644 --- a/devshop_projects/inc/nodes.inc +++ b/devshop_projects/inc/nodes.inc @@ -139,6 +139,9 @@ function devshop_projects_load($node) { $environment->git_current = t('Cloning in progress...'); } + // Repo root + $environment->repo_root = empty($project->drupal_path)? $environment->root: str_replace($project->drupal_path, '', $environment->root); + // Environments URL // @TODO: Check for http or https. // @TODO: Load a login URL if available. From bf2b4a051be1c6a37f421f0323f8a3578575ae1c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 19 Dec 2014 14:13:44 -0500 Subject: [PATCH 1096/3476] Adding acquia cloud hooks as an option to run on deployment. --- devshop_projects/inc/forms.inc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index b7dcc68f3..e5c8facef 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -315,6 +315,16 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ '#default_value' => empty($node->nid)? 1: $environment->settings->deploy['cache'], ); + // If acquia cloud hooks are detected, add the option to fire them. + if (file_exists($environment->repo_root . '/hooks/common/post-code-update')) { + $form['environment']['settings']['deploy']['acquia_hooks'] = array( + '#type' => 'checkbox', + '#title' => t('Run Acquia "post-code-update" hook scripts for environment "%env".', array('%env' => $environment->name)), + '#description' => 'Cloud hooks were detected in your project. Check this box to run the "post-code-update" hook every time you deploy code to this environment.', + '#default_value' => empty($node->nid)? 0: $environment->settings->deploy['acquia_hooks'], + ); + } + // Add our own submit handler $form['buttons']['submit']['#submit'][] = 'devshop_projects_environment_settings_submit'; } From 64d9ed020947cf27ba33a99f6f323aa35eb775dc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 10 Jan 2015 23:21:32 -0500 Subject: [PATCH 1097/3476] Fixing up deploy db server form alters. --- devshop_projects/inc/forms.inc | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index e5c8facef..4d469cedb 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -77,41 +77,32 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ case 'migrate': // To change the database server, we use the migrate form. - if ($_GET['db_server']) { - drupal_set_title(t('Migrate Database')); + if ($_GET['deploy'] == 'stack') { + drupal_set_title(t('Change Database Server')); $site_node = node_load($form['nid']['#value']); $environment = $site_node->environment; - $form['help']['#value'] = t("Are you sure you want to change this site's database server?"); - - // Set DB Server values - $form['parameters']['new_db_server']['#type'] = 'value'; - $db_server = $_GET['db_server']; - $db_server_nid = db_result(db_query('SELECT nid FROM {node} WHERE type = "server" && title = "%s"', $db_server)); - $form['parameters']['new_db_server']['#default_value'] = $db_server_nid; + $form['help']['#weight'] = 100; + $form['help']['#value'] = t("Are you sure you want to change this environment's database server?"); // Set URI value $form['parameters']['new_uri']['#type'] = 'value'; $form['parameters']['new_uri']['#value'] = $form['parameters']['new_uri']['#default_value']; + // Display something helpful $form['old'] = array( '#type' => 'item', '#title' => t('Current Database Server'), - '#value' => l($environment->servers['db']['name'], 'node/' . $environment->servers['db']['nid']), + '#value' => l($environment->servers['db']['name'], 'hosting/c/server_' . $environment->servers['db']['nid']), '#weight' => '-1', ); - // Display something helpful - $form['new'] = array( - '#type' => 'item', - '#title' => t('New Database Server'), - '#value' => l($db_server, "node/$db_server_nid"), - '#weight' => '0', - ); - - // @TODO: not sure how to hide the radios. We'll just use CSS for now. - $form['parameters']['#prefix'] = ''; - } break; From e3a1193fd553786ec11c712403e685fc78f94be5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 11 Jan 2015 14:19:08 -0500 Subject: [PATCH 1098/3476] removing accidental dsm --- devshop_projects/inc/forms.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/devshop_projects/inc/forms.inc b/devshop_projects/inc/forms.inc index 4d469cedb..21689eafe 100644 --- a/devshop_projects/inc/forms.inc +++ b/devshop_projects/inc/forms.inc @@ -101,7 +101,6 @@ function devshop_projects_form_alter(&$form, &$form_state, $form_id){ unset($form['parameters'][$key]); } } - dsm($form['parameters']); $form['parameters']['#suffix'] = ''; } break; From 2405026116960a1ee1e8c88e8a3f0dec78aa5d0a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 11 Jan 2015 16:33:54 -0500 Subject: [PATCH 1099/3476] Environment settings dropdown. --- devshop_projects/inc/theme.inc | 6 ++++++ devshop_projects/project-nav.tpl.php | 20 ++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 98d048902..1d3fef9cd 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -188,6 +188,12 @@ function template_preprocess_devshop_project_nav(&$vars){ $vars['settings_link'] = l(' ' . t('Settings'), "node/$node->nid/edit", array('html' => TRUE)); } + if (arg(2) == 'edit') { + $vars['settings_active'] = 'active'; + } + + + dsm($vars['project']); // if (user_access('access project logs')){ // $vars['logs_link'] = l(' ' . t('Logs'), "node/$node->nid/logs", array('html' => TRUE)); // } diff --git a/devshop_projects/project-nav.tpl.php b/devshop_projects/project-nav.tpl.php index 95d8c7e45..b2f7e4a32 100644 --- a/devshop_projects/project-nav.tpl.php +++ b/devshop_projects/project-nav.tpl.php @@ -9,10 +9,22 @@
  • - -
  • - - +
  • From 8975755cae5ba66b1f8f6e81dd298ca9a26e5750 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 11 Jan 2015 16:45:01 -0500 Subject: [PATCH 1100/3476] Logs dropdown link. --- devshop_projects/inc/theme.inc | 9 +++------ devshop_projects/project-nav.tpl.php | 23 +++++++++++++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/devshop_projects/inc/theme.inc b/devshop_projects/inc/theme.inc index 1d3fef9cd..9a922743e 100644 --- a/devshop_projects/inc/theme.inc +++ b/devshop_projects/inc/theme.inc @@ -191,10 +191,7 @@ function template_preprocess_devshop_project_nav(&$vars){ if (arg(2) == 'edit') { $vars['settings_active'] = 'active'; } - - - dsm($vars['project']); -// if (user_access('access project logs')){ -// $vars['logs_link'] = l(' ' . t('Logs'), "node/$node->nid/logs", array('html' => TRUE)); -// } + if (arg(2) == 'logs') { + $vars['logs_active'] = 'active'; + } } \ No newline at end of file diff --git a/devshop_projects/project-nav.tpl.php b/devshop_projects/project-nav.tpl.php index b2f7e4a32..45378b59c 100644 --- a/devshop_projects/project-nav.tpl.php +++ b/devshop_projects/project-nav.tpl.php @@ -25,11 +25,26 @@ + - -
  • - - + + + From 86e1b44b9ba7ca04b8e8c800583f8ace29e287c6 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 11 Jan 2015 17:30:01 -0500 Subject: [PATCH 1101/3476] Adding task logs page. --- devshop_projects/devshop_projects.module | 14 ++++++++++++++ devshop_projects/inc/logs.inc | 15 +++++++++++++++ devshop_projects/project-nav.tpl.php | 1 - 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 devshop_projects/inc/logs.inc diff --git a/devshop_projects/devshop_projects.module b/devshop_projects/devshop_projects.module index d50975279..0abcbb71e 100644 --- a/devshop_projects/devshop_projects.module +++ b/devshop_projects/devshop_projects.module @@ -153,6 +153,17 @@ function devshop_projects_menu() { 'file path' => drupal_get_path('module', 'node'), 'type' => MENU_LOCAL_TASK, ); + // Environment logs pages + $items['node/%node/logs/%'] = array( + 'title' => 'Logs', + 'page callback' => 'devshop_project_logs_page', + 'page arguments' => array(1, 3), + 'access callback' => 'devshop_project_logs_access', + 'access arguments' => array('view', 1), + 'weight' => 1, + 'file' => 'inc/logs.inc', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); // Drush aliases download. $items['node/%node/aliases'] = array( 'title' => 'Drush Aliases', @@ -167,6 +178,9 @@ function devshop_projects_menu() { return $items; } +function devshop_project_logs_access() { + return TRUE; +} /** * Implements hook_block() diff --git a/devshop_projects/inc/logs.inc b/devshop_projects/inc/logs.inc new file mode 100644 index 000000000..8e182da2b --- /dev/null +++ b/devshop_projects/inc/logs.inc @@ -0,0 +1,15 @@ +project->environments[$environment]; + + $env = t('Environment Tasks Log'); + $output = "

    {$environment->name} {$env}

    "; + $output .= views_embed_view('hosting_task_log', 'page_1', $environment->site); + + return $output; +} diff --git a/devshop_projects/project-nav.tpl.php b/devshop_projects/project-nav.tpl.php index 45378b59c..6c623bd87 100644 --- a/devshop_projects/project-nav.tpl.php +++ b/devshop_projects/project-nav.tpl.php @@ -34,7 +34,6 @@ + + + +
    + + + + +
    +
    From 77992cce1754e92959e652aa44d8216c4b9f0bc2 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 16 Feb 2015 19:38:01 -0500 Subject: [PATCH 1247/3476] merge fix From 4859f5ca6a2208f55498f71046a124e7f1517bac Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 16 Mar 2015 11:18:30 -0400 Subject: [PATCH 1248/3476] Respecting perms in the UI. --- themes/boots/node-project.tpl.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/themes/boots/node-project.tpl.php b/themes/boots/node-project.tpl.php index 441f6cc14..0cf6ae25a 100644 --- a/themes/boots/node-project.tpl.php +++ b/themes/boots/node-project.tpl.php @@ -261,9 +261,12 @@
    - +
    From 6b5ddaa4c9016b5dcb63e8959aa375ee0af062a3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 16 Mar 2015 11:28:07 -0400 Subject: [PATCH 1249/3476] Respecting perms in the UI. --- themes/boots/node-project.tpl.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/themes/boots/node-project.tpl.php b/themes/boots/node-project.tpl.php index 0cf6ae25a..0d0d2e423 100644 --- a/themes/boots/node-project.tpl.php +++ b/themes/boots/node-project.tpl.php @@ -259,6 +259,13 @@
    +
    @@ -382,6 +389,8 @@
    + +
    From 83d856df42f92ce287671bf1c1cc3900c9f9789c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 16 Mar 2015 11:38:26 -0400 Subject: [PATCH 1250/3476] Respecting perms in the UI for settings. --- modules/devshop/devshop_projects/project-nav.tpl.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/devshop/devshop_projects/project-nav.tpl.php b/modules/devshop/devshop_projects/project-nav.tpl.php index 6c623bd87..7baa1dc8b 100644 --- a/modules/devshop/devshop_projects/project-nav.tpl.php +++ b/modules/devshop/devshop_projects/project-nav.tpl.php @@ -8,6 +8,7 @@
  • + +
    - + + diff --git a/themes/boots/template.php b/themes/boots/template.php index 1476b0147..0da53f48c 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -115,7 +115,9 @@ function boots_render_tasks($tasks = NULL, $class = '', $actions = array()){ array_unshift($items, array( 'class' => 'divider', )); - array_unshift($items, l(' ' . t('Environment Settings'), "node/{$environment->project_nid}/edit/{$environment->name}", array('html' => TRUE))); + if (node_access('update', $environment_node)) { + array_unshift($items, l(' ' . t('Environment Settings'), "node/{$environment->project_nid}/edit/{$environment->name}", array('html' => TRUE))); + } $action_items = array(); foreach ($actions as $link) { @@ -545,4 +547,4 @@ function boots_status_messages($display = NULL) { $output .= "\n"; } return $output; -} \ No newline at end of file +} From 462ad31259f8ad8677f3c01a37bf7cfb3221968b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 17 Mar 2015 21:49:51 -0400 Subject: [PATCH 1255/3476] permalink button --- themes/boots/node-project.tpl.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/themes/boots/node-project.tpl.php b/themes/boots/node-project.tpl.php index 9f602f8e0..cc9921b47 100644 --- a/themes/boots/node-project.tpl.php +++ b/themes/boots/node-project.tpl.php @@ -428,8 +428,7 @@ From 391ebec9b058bf5437cc486d0f19477c975b06cd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 18 Mar 2015 10:34:27 -0400 Subject: [PATCH 1256/3476] bad html --- themes/boots/node-project.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/boots/node-project.tpl.php b/themes/boots/node-project.tpl.php index cc9921b47..a4f8d1a5c 100644 --- a/themes/boots/node-project.tpl.php +++ b/themes/boots/node-project.tpl.php @@ -428,7 +428,7 @@ From 6915c02518596dfd941bfbcd4ada5c11fc976f6e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 24 Mar 2015 10:02:54 -0400 Subject: [PATCH 1257/3476] adding link to results of slack notifications. --- .../devshop_testing/devshop_testing.module | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/devshop/devshop_testing/devshop_testing.module b/modules/devshop/devshop_testing/devshop_testing.module index e7900985a..495ac534b 100644 --- a/modules/devshop/devshop_testing/devshop_testing.module +++ b/modules/devshop/devshop_testing/devshop_testing.module @@ -339,3 +339,25 @@ function devshop_testing_preprocess_node(&$vars) { } } } + + +/** + * Implements hook_hosting_slack_message_alter() + */ +function devshop_testing_hosting_slack_message_alter(&$message_options) { + + + if ($message_options->task->task_type == 'test') { + + $url = url('devshop_tests/' . $message_options->task->nid . '/' . $message_options->task->vid, array('absolute' => TRUE)); + + $attachment = new stdClass(); + $attachment->fallback = t('Results: ') . $url; + $attachment->title = t('Results: '); + $attachment->text = $url; + $attachment->title_link = $url; + + $message_options->attachments[] = $attachment; + + } +} \ No newline at end of file From 061072d7398d3bff815b26b24ea36f13f514cbfa Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 26 Mar 2015 12:42:35 -0400 Subject: [PATCH 1258/3476] expanding scenario outlines by default. --- test.provision.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.provision.inc b/test.provision.inc index 7f7ab8100..8e503c893 100644 --- a/test.provision.inc +++ b/test.provision.inc @@ -108,7 +108,7 @@ YML; $feature = '/' . $feature; } $feature_path = empty($feature)? '': "features{$feature}"; - $cmd = "$full_behat_bin_path $feature_path --config $behat_yml_file --profile $environment --colors"; + $cmd = "$full_behat_bin_path $feature_path --config $behat_yml_file --profile $environment --colors --format-settings='{\"expand\": true}' "; drush_log("Running '$cmd' in $full_behat_folder_path", 'status'); // If drush_shell_cd_and_exec passes... From ebcb4db24f5ff572ace15e884cf94f658bce2bb7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 1 Apr 2015 21:23:16 -0400 Subject: [PATCH 1259/3476] Saving output to a file. --- .../devshop_projects/devshop_projects.drush.inc | 12 ++++++++++++ .../devshop_testing/devshop_testing.drush.inc | 1 + 2 files changed, 13 insertions(+) diff --git a/modules/devshop/devshop_projects/devshop_projects.drush.inc b/modules/devshop/devshop_projects/devshop_projects.drush.inc index b71feed1d..2bb72a187 100644 --- a/modules/devshop/devshop_projects/devshop_projects.drush.inc +++ b/modules/devshop/devshop_projects/devshop_projects.drush.inc @@ -71,6 +71,18 @@ function drush_devshop_projects_pre_hosting_task() { $task->options['test-type'] = $task->task_args['test_type']; $task->options['behat-folder-path'] = $task->task_args['behat_folder_path']; $task->options['behat-bin-path'] = $task->task_args['behat_bin_path']; + + $path = variable_get('file_directory_path', '') . '/tests'; + if (!file_exists($path)) { + drush_log("Creating $path", 'ok'); + mkdir($path); + } + $task->options['output-path'] = tempnam($path, 'test-results.') . '.txt'; + + $task->task_args['output_path'] = $task->options['output-path']; + + $task->no_verify = TRUE; + node_save($task); } } diff --git a/modules/devshop/devshop_testing/devshop_testing.drush.inc b/modules/devshop/devshop_testing/devshop_testing.drush.inc index e7dcd09d7..426911a2d 100644 --- a/modules/devshop/devshop_testing/devshop_testing.drush.inc +++ b/modules/devshop/devshop_testing/devshop_testing.drush.inc @@ -14,6 +14,7 @@ function devshop_testing_post_hosting_test_task($task, $data) { // Save test run results as a task arg $task->task_args['test_results'] = $results; + $task->task_args['test_results_file'] = $data['context']['behat_test_results_file']; node_save($task); } From 64be2e8acbe0915daa0a47d55491a267d9343dfd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 1 Apr 2015 21:23:16 -0400 Subject: [PATCH 1260/3476] Saving output to a file. --- test.provision.inc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test.provision.inc b/test.provision.inc index 7f7ab8100..34cb359d2 100644 --- a/test.provision.inc +++ b/test.provision.inc @@ -13,6 +13,12 @@ function drush_devshop_provision_pre_provision_test(){ if (!empty($tests) && is_array($tests)) { drush_set_option('tests-to-run', array_filter($tests)); } + + // Look for desired output path + $output_path = drush_get_option('output-path'); + if (empty($output_path)) { + drush_set_option('output-path', tempnam('', 'test-results.') . '.txt'); + } } /** @@ -20,7 +26,7 @@ function drush_devshop_provision_pre_provision_test(){ */ function drush_devshop_provision_provision_test() { drush_log(dt('Provision DevShop Run Tests started...'), 'status'); - // Get tests tp run + // Get tests to run $type = drush_get_option('test-type', d()->environment['settings']['testing']['test_type']); $tests_to_run = drush_get_option('tests-to-run', array_filter(d()->environment['settings']['testing']['tests_to_run'])); @@ -108,7 +114,13 @@ YML; $feature = '/' . $feature; } $feature_path = empty($feature)? '': "features{$feature}"; - $cmd = "$full_behat_bin_path $feature_path --config $behat_yml_file --profile $environment --colors"; + + // Get output path from option. + $output_path = drush_get_option('output-path'); + + // Create Command + $cmd = "$full_behat_bin_path $feature_path --config $behat_yml_file --profile $environment --colors --out $output_path"; + drush_log("Running '$cmd' in $full_behat_folder_path", 'status'); // If drush_shell_cd_and_exec passes... @@ -139,6 +151,7 @@ COMMAND: $cmd URL: $url RESULTS: + Saved to $output_path $behat_results TXT; From fc0e5cb6ad26fc3f22d671c295c15bd0d686c06f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 1 Apr 2015 22:02:44 -0400 Subject: [PATCH 1261/3476] save results to a file. --- .../devshop_projects.drush.inc | 11 ++++++++-- .../devshop_testing/devshop_testing.drush.inc | 21 ++++++++++--------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/modules/devshop/devshop_projects/devshop_projects.drush.inc b/modules/devshop/devshop_projects/devshop_projects.drush.inc index 2bb72a187..0ddc2e35b 100644 --- a/modules/devshop/devshop_projects/devshop_projects.drush.inc +++ b/modules/devshop/devshop_projects/devshop_projects.drush.inc @@ -67,6 +67,7 @@ function drush_devshop_projects_pre_hosting_task() { // Run tests if ($task->ref->type == 'site' && $task->task_type == 'test') { + $task->options['tests-to-run'] = $task->task_args['tests_to_run']; $task->options['test-type'] = $task->task_args['test_type']; $task->options['behat-folder-path'] = $task->task_args['behat_folder_path']; @@ -77,10 +78,16 @@ function drush_devshop_projects_pre_hosting_task() { drush_log("Creating $path", 'ok'); mkdir($path); } - $task->options['output-path'] = tempnam($path, 'test-results.') . '.txt'; - $task->task_args['output_path'] = $task->options['output-path']; + // Pass to drush provision-test command + $output_path = tempnam($path, 'test.') . '.txt'; + $output_filename = $path . '/' . pathinfo($output_path, PATHINFO_BASENAME); + $task->options['output-path'] = $output_path; + + // Save output path as a task argument. + $task->task_args['output_path'] = $task->options['output-path']; + $task->task_args['output_link'] = url($output_filename, array('absolute' => TRUE)); $task->no_verify = TRUE; node_save($task); } diff --git a/modules/devshop/devshop_testing/devshop_testing.drush.inc b/modules/devshop/devshop_testing/devshop_testing.drush.inc index 426911a2d..47c0dfa40 100644 --- a/modules/devshop/devshop_testing/devshop_testing.drush.inc +++ b/modules/devshop/devshop_testing/devshop_testing.drush.inc @@ -6,16 +6,17 @@ */ function devshop_testing_post_hosting_test_task($task, $data) { - // Get test results - // @TODO: Handle Simpletest as well - if (isset($data['context']['behat_test_results'])) { - $results = $data['context']['behat_test_results']; - } - - // Save test run results as a task arg - $task->task_args['test_results'] = $results; - $task->task_args['test_results_file'] = $data['context']['behat_test_results_file']; - node_save($task); + // We don't really need this if we are saving to a file. +// // Get test results +// // @TODO: Handle Simpletest as well +// if (isset($data['context']['behat_test_results'])) { +// $results = $data['context']['behat_test_results']; +// } +// +// // Save test run results as a task arg +// $task->task_args['test_results'] = $results; +// $task->task_args['test_results_file'] = $data['context']['behat_test_results_file']; +// node_save($task); } /** From c98f6619c63ea1424537584830507dfb9be1c0b3 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 1 Apr 2015 22:09:34 -0400 Subject: [PATCH 1262/3476] logs display --- .../devshop/devshop_testing/devshop_testing.module | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/devshop/devshop_testing/devshop_testing.module b/modules/devshop/devshop_testing/devshop_testing.module index 495ac534b..5e35666b6 100644 --- a/modules/devshop/devshop_testing/devshop_testing.module +++ b/modules/devshop/devshop_testing/devshop_testing.module @@ -252,10 +252,10 @@ function devshop_testing_nodeapi_task_load(&$node, $a3, $a4) { unset($node->task_args['test_results']); } - if ($node->type == 'task' && !empty($node->task_args['test_results'])) { + if ($node->type == 'task' && !empty($node->task_args['output_path']) && file_exists($node->task_args['output_path'])) { $converter = new AnsiToHtmlConverter(); - $output = $converter->convert($node->task_args['test_results']); + $output = $converter->convert(file_get_contents($node->task_args['output_path'])); $output = strtr($output, array( "\n" => "
    ", @@ -267,9 +267,16 @@ function devshop_testing_nodeapi_task_load(&$node, $a3, $a4) { } } +/** + * Implements hook_nodeapi_TYPE_OP() + * @param $node + * @param $a3 + * @param $a4 + */ function devshop_testing_nodeapi_task_view(&$node, $a3, $a4) { - if ($node->type == 'task' && !empty($node->task_args['test_results'])) { + if ($node->type == 'task' && !empty($node->test_results_formatted)) { + unset($node->content['hosting_log']); $node->content['results'] = array( '#type' => 'item', '#value' => $node->test_results_formatted, From 37e10d4a84c1d95f09aff3cd42e39cb9a05b3ebb Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 1 Apr 2015 23:14:14 -0400 Subject: [PATCH 1263/3476] ajax callbacks for auto follow --- .../devshop_testing/devshop_testing.module | 43 +++++++++++-------- modules/devshop/devshop_testing/pages.inc | 43 ++++++++++++++++++- .../devshop/devshop_testing/test-reload.js | 11 +++++ 3 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 modules/devshop/devshop_testing/test-reload.js diff --git a/modules/devshop/devshop_testing/devshop_testing.module b/modules/devshop/devshop_testing/devshop_testing.module index 5e35666b6..b71ee3199 100644 --- a/modules/devshop/devshop_testing/devshop_testing.module +++ b/modules/devshop/devshop_testing/devshop_testing.module @@ -17,6 +17,12 @@ function devshop_testing_menu() { 'file' => 'pages.inc', 'access arguments' => array('access test results'), ); + $items['devshop_tests/ansi-to-html'] = array( + 'page callback' => 'devshop_testing_results_page_raw', + 'page arguments' => array(2), + 'file' => 'pages.inc', + 'access arguments' => array('access test results'), + ); return $items; } @@ -240,12 +246,13 @@ function devshop_testing_task_submit(&$form, &$form_state) { form_set_value($form['parameters']['tests_to_run'], $value, $form_state); } - -//require_once 'ansi-to-html\SensioLabs\AnsiConverter\AnsiToHtmlConverter.php'; -require_once __DIR__.'/ansi-to-html/vendor/autoload.php'; - -use SensioLabs\AnsiConverter\AnsiToHtmlConverter; - +/** + * Implements hook_nodeapi_TYPE_OP() + * + * @param $node + * @param $a3 + * @param $a4 + */ function devshop_testing_nodeapi_task_load(&$node, $a3, $a4) { if ($node->task_type == 'test' && ($node->task_status == HOSTING_TASK_PROCESSING || $node->task_status == HOSTING_TASK_QUEUED)) { @@ -253,17 +260,8 @@ function devshop_testing_nodeapi_task_load(&$node, $a3, $a4) { } if ($node->type == 'task' && !empty($node->task_args['output_path']) && file_exists($node->task_args['output_path'])) { - - $converter = new AnsiToHtmlConverter(); - $output = $converter->convert(file_get_contents($node->task_args['output_path'])); - - $output = strtr($output, array( - "\n" => "
    ", - " " => "  ", - )); - - $node->test_results_formatted = '
    ' . $output . '
    '; - + include_once('pages.inc'); + $node->test_results_formatted = devshop_testing_results_to_html(file_get_contents($node->task_args['output_path'])); } } @@ -281,7 +279,18 @@ function devshop_testing_nodeapi_task_view(&$node, $a3, $a4) { '#type' => 'item', '#value' => $node->test_results_formatted, '#weight' => -100, + '#prefix' => '
    ', + '#suffix' => '
    ', ); + + // If task is still running, add the javascript to reload it. + $filename = pathinfo($node->task_args['output_path'], PATHINFO_BASENAME); + drupal_add_js(drupal_get_path('module', 'devshop_testing') . '/test-reload.js'); + drupal_add_js(array( + 'test_running' => TRUE, + 'test_url' => url('devshop_tests/ansi-to-html/' . $filename), + ), 'setting'); + } } diff --git a/modules/devshop/devshop_testing/pages.inc b/modules/devshop/devshop_testing/pages.inc index f1852c8d7..9c7c3bc68 100644 --- a/modules/devshop/devshop_testing/pages.inc +++ b/modules/devshop/devshop_testing/pages.inc @@ -1,5 +1,10 @@ 'table')); } + $filename = pathinfo($node->task_args['output_path'], PATHINFO_BASENAME); + drupal_add_js(drupal_get_path('module', 'devshop_testing') . '/test-reload.js'); + drupal_add_js(array( + 'test_running' => TRUE, + 'test_url' => url('devshop_tests/ansi-to-html/' . $filename), + ), 'setting'); + return '
    ' . $node->test_results_formatted . '
    '; +} + +/** + * Page callback for ajax test results. + * @param $filename + */ +function devshop_testing_results_page_raw($filename) { + + $path = variable_get('file_directory_path', '') . '/tests/' . $filename; + $output = devshop_testing_results_to_html(file_get_contents($path)); + print $output; +} + +/** + * Helper to format test results. + * @param $ascii + * @return string + */ +function devshop_testing_results_to_html($ascii) { + + $theme = new SolarizedXTermTheme(); + $converter = new AnsiToHtmlConverter($theme, false); + $html = $converter->convert($ascii); + $html = strtr($html, array( + "\n" => "
    ", + " " => "  ", + )); - return $node->test_results_formatted; + $output = ""; + $output .= "
    $html
    "; + return $output; } \ No newline at end of file diff --git a/modules/devshop/devshop_testing/test-reload.js b/modules/devshop/devshop_testing/test-reload.js new file mode 100644 index 000000000..b5a6cae0d --- /dev/null +++ b/modules/devshop/devshop_testing/test-reload.js @@ -0,0 +1,11 @@ + + setInterval(function(){ + console.log('loading results...'); + console.log(Drupal.settings.test_url); + if (Drupal.settings.test_url) { + $('.results-wrapper').load(Drupal.settings.test_url, function () { + window.scrollTo(0,document.body.scrollHeight); + }); + } + }, 1000); + From 6d8ed6ebac68d3c45039756f930559af5268792b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 1 Apr 2015 23:51:03 -0400 Subject: [PATCH 1264/3476] Follow Logs ability! --- .../devshop_testing/devshop_testing.module | 18 +++++++++++ modules/devshop/devshop_testing/pages.inc | 24 +++++++++++++++ .../devshop/devshop_testing/test-reload.js | 30 +++++++++++++++---- modules/devshop/devshop_testing/testing.css | 6 ++++ 4 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 modules/devshop/devshop_testing/testing.css diff --git a/modules/devshop/devshop_testing/devshop_testing.module b/modules/devshop/devshop_testing/devshop_testing.module index b71ee3199..874aac8f7 100644 --- a/modules/devshop/devshop_testing/devshop_testing.module +++ b/modules/devshop/devshop_testing/devshop_testing.module @@ -17,6 +17,13 @@ function devshop_testing_menu() { 'file' => 'pages.inc', 'access arguments' => array('access test results'), ); + $items['devshop_tests/%/%/status'] = array( + 'title' => 'Test Status', + 'page callback' => 'devshop_testing_results_status', + 'page arguments' => array(1, 2), + 'file' => 'pages.inc', + 'access arguments' => array('access test results'), + ); $items['devshop_tests/ansi-to-html'] = array( 'page callback' => 'devshop_testing_results_page_raw', 'page arguments' => array(2), @@ -262,6 +269,7 @@ function devshop_testing_nodeapi_task_load(&$node, $a3, $a4) { if ($node->type == 'task' && !empty($node->task_args['output_path']) && file_exists($node->task_args['output_path'])) { include_once('pages.inc'); $node->test_results_formatted = devshop_testing_results_to_html(file_get_contents($node->task_args['output_path'])); + } } @@ -289,8 +297,18 @@ function devshop_testing_nodeapi_task_view(&$node, $a3, $a4) { drupal_add_js(array( 'test_running' => TRUE, 'test_url' => url('devshop_tests/ansi-to-html/' . $filename), + 'test_status_url' => url("devshop_tests/{$node->nid}/{$node->vid}/status"), ), 'setting'); + if ($node->task_status == HOSTING_TASK_PROCESSING || $node->task_status == HOSTING_TASK_QUEUED ) { + $node->content['follow_checkbox'] = array( + '#type' => 'markup', + '#value' => '
    +

    URL: {$site->environment->url}

    +
    + {$node->test_results_formatted} +
    + +HTML; + drupal_add_css(drupal_get_path('module', 'devshop_testing') . '/testing.css'); return $output; } diff --git a/themes/boots/boots.css b/themes/boots/boots.css index 77e960d95..b4c0f7c5a 100644 --- a/themes/boots/boots.css +++ b/themes/boots/boots.css @@ -534,3 +534,7 @@ th.checkbox, td.checkbox { display: none; } + +.padded-top { + padding-top: 1em; +} diff --git a/themes/boots/js/boots.js b/themes/boots/js/boots.js index ed9bcac5f..6dba70a22 100644 --- a/themes/boots/js/boots.js +++ b/themes/boots/js/boots.js @@ -2,4 +2,6 @@ // Match the height of environments so the grid doesn't break. $('.environment-wrapper').matchHeight(); + + })(jQuery); diff --git a/themes/boots/node-task.tpl.php b/themes/boots/node-task.tpl.php index 8bcc82175..2bb18f7b9 100644 --- a/themes/boots/node-task.tpl.php +++ b/themes/boots/node-task.tpl.php @@ -1,3 +1,12 @@ + + - + @@ -65,9 +74,41 @@ -
    - + +test_results_formatted): ?> +
    + + + + + +
    +
    +
    +
    + test_results_formatted; ?> +
    + +
    +
    +
    +
    + +
    +
    +
    + + + +
    \ No newline at end of file From 48370e9426ff9459c5deca3c2f1f17c91fe099d0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 14 Apr 2015 15:41:33 -0400 Subject: [PATCH 1307/3476] Fixing bad active live environment colors. --- themes/boots/boots.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/themes/boots/boots.css b/themes/boots/boots.css index b4c0f7c5a..960da1704 100644 --- a/themes/boots/boots.css +++ b/themes/boots/boots.css @@ -260,12 +260,12 @@ body.admin-menu .navbar-static-top { { color: #8a6d3b; } - .environment.active .environment-header a, .environment.active .environment-header span, -.environment.active .environment-header button +.environment.active .environment-header button, +.environment.active .environment-tasks button.btn-link { - color: #8a6d3b; + color: #8a6d3b !important; } .environment.active .environment-header ul a { From 1b871fc16cdf160643edfb2aa06e835deef373ff Mon Sep 17 00:00:00 2001 From: DevShop Date: Thu, 16 Apr 2015 10:28:14 -0500 Subject: [PATCH 1308/3476] using project context to find behat folder --- test.provision.inc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test.provision.inc b/test.provision.inc index f990db482..2b3423602 100644 --- a/test.provision.inc +++ b/test.provision.inc @@ -33,7 +33,10 @@ function drush_devshop_provision_provision_test() { elseif ($type == 'behat') { // Get paths from options or site context. $repo_path = d()->environment['repo_root']; - $behat_folder_path = drush_get_option('behat-folder-path', d()->environment['settings']['testing']['behat_folder_path']); + + $project_context = "project_" . d()->project; + $project = (object) d($project_context)->project; + $behat_folder_path = drush_get_option('behat-folder-path', $project->settings['testing']['behat_folder_path']); $behat_bin_path = drush_get_option('behat-bin-path', d()->environment['settings']['testing']['behat_bin_path']); // If no repo at that path, error. @@ -47,8 +50,8 @@ function drush_devshop_provision_provision_test() { } // Prepare path and command - $full_behat_folder_path = $repo_path . $behat_folder_path; - $full_behat_bin_path = $repo_path . $behat_folder_path . '/' . $behat_bin_path; + $full_behat_folder_path = $repo_path . '/' . $behat_folder_path; + $full_behat_bin_path = $repo_path . '/' . $behat_folder_path . '/' . $behat_bin_path; // Load the behat.yml from behat_folder_path. if (file_exists($full_behat_folder_path . '/behat.yml')) { @@ -58,7 +61,7 @@ function drush_devshop_provision_provision_test() { $behat_yml = file_get_contents($full_behat_folder_path . '/config/behat.yml'); } else { - return drush_set_error('DEVSHOP_MISSING_FILE', dt('behat.yml file not found in behat_folder_path.')); + return drush_set_error('DEVSHOP_MISSING_FILE', dt('behat.yml file not found in behat_folder_path: ') . $full_behat_folder_path); } // Run composer update. From 9baf099c736c918611dc2f12c9ca811c59bde80e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 Apr 2015 11:38:38 -0400 Subject: [PATCH 1309/3476] Parsing github owner and repo on node load. --- modules/devshop/devshop_github/devshop_github.module | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index 352824fb1..e1651858f 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -36,6 +36,14 @@ function devshop_github_nodeapi_project_load(&$node) { $node->project->environments[$pull_request->environment_name]->github_pull_request = $pull_request; } } + + // Parse github owner and repo. + if ($node->project->git_provider == 'github') { + $node->project->github_owner = + $parts = explode('/', parse_url($node->project->git_repo_url, PHP_URL_PATH)); + $node->project->github_owner = $parts[1]; + $node->project->github_repo = $parts[2]; + } } /** @@ -85,7 +93,7 @@ function devshop_github_hosting_task_update_status($task, $status) { $comment = $client->api('issue') ->comments() - ->create($owner, $repo, $task->ref->environment->github_pull_request->number, array( + ->create($task->ref->project->github_owner, $task->ref->project->github_repo, $task->ref->environment->github_pull_request->number, array( 'body' => devshop_github_comment($task, $status), )); drush_log('Comment posted! ' . print_r($comment, 1), 'ok'); From abaafd677a2ae0ca6af11a5c2b06d80d3227848c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 Apr 2015 11:59:46 -0400 Subject: [PATCH 1310/3476] Saving commit status on PR create and syncronize. --- .../devshop_github/devshop_github.module | 69 +++++++++++++------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index e1651858f..ed20514e5 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -140,6 +140,9 @@ function devshop_github_webhook($project_node){ $headers = getallheaders(); $project = $project_node->project; + // Create a github deployment + require_once 'vendor/autoload.php'; + // @TODO: Handle form content from github as well. if ($headers['content-type'] == 'application/json'){ $data = json_decode($GLOBALS['HTTP_RAW_POST_DATA']); @@ -222,6 +225,32 @@ function devshop_github_webhook($project_node){ $message .= "Environment $environment_name created for $project_node->title \n"; } + $owner = $project->github_owner; + $repo = $project->github_repo; + $message .= "About to try to create a deployment for $owner/$repo... \n"; + + try { + $token = variable_get('devshop_github_token', ''); + $client = new \Github\Client(); + $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); + + // Create a status + $sha = $data->pull_request->head->sha; + $url = "http://{$environment_name}.{$project->base_url}"; + + $params = new stdClass(); + $params->state = 'pending'; + $params->target_url = "http://{$environment_name}.{$project->base_url}"; + $params->description = 'Deploying and testing with DevShop...'; + $params->context = 'devshop/test'; + + $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + + } catch (Github\Exception\RuntimeException $e) { + watchdog('devshop_github', 'GitHub API Error: ' . $e->getMessage()); + } + + // Insert PR record if (!$already_have_pr_info) { $info = new stdClass(); @@ -236,37 +265,37 @@ function devshop_github_webhook($project_node){ $message .= ' ' . t('Pull Request info saved to DevShop.'); } } + } - // Notify Github via comment - // Include vendors - require_once 'vendor/autoload.php'; - - // Extract username and repo - list($s, $owner, $repo) = explode('/', parse_url($project->git_repo_url, PHP_URL_PATH)); + // When PR is cloded, delete environment. + elseif ($data->action == 'synchronize') { - $message .= "About to try to create a comment for $owner/$repo... \n"; + // Save a status on the SHA + $owner = $project->github_owner; + $repo = $project->github_repo; + $message .= "About to set commit status for $owner/$repo... \n"; try { $token = variable_get('devshop_github_token', ''); $client = new \Github\Client(); $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); - $comment = $client->api('issue') - ->comments() - ->create($owner, $repo, $data->pull_request->number, array( - 'body' => t('Your environment creation has been queued. Please be patient, you will be notified when it is ready.'), - )); - $message .= 'Comment posted to github. '; + // Create a status + $sha = $data->pull_request->head->sha; + $url = "http://{$environment_name}.{$project->base_url}"; + + $params = new stdClass(); + $params->state = 'pending'; + $params->target_url = "http://{$environment_name}.{$project->base_url}"; + $params->description = 'Deploying and testing with DevShop...'; + $params->context = 'devshop/test'; + + $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + } catch (Github\Exception\RuntimeException $e) { - $message .= 'GitHub API Error: ' . $e->getMessage(); - } catch (Github\Exception\ValidationFailedException $e) { - $message .= 'GitHub API Error: ' . $e->getMessage(); + watchdog('devshop_github', 'GitHub API Error: ' . $e->getMessage()); } - } - - // When PR is cloded, delete environment. - elseif ($data->action == 'synchronize') { // Update the PR record $info = new stdClass(); From 64385579c7880c8119bfdb8e266080d7b4b1931f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 Apr 2015 12:00:01 -0400 Subject: [PATCH 1311/3476] Typo. --- modules/devshop/devshop_github/devshop_github.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index ed20514e5..868166bdd 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -267,7 +267,7 @@ function devshop_github_webhook($project_node){ } } - // When PR is cloded, delete environment. + // When PR is closed, delete environment. elseif ($data->action == 'synchronize') { // Save a status on the SHA From 5306a31295d44f3a8216c3a802d3885f913fe5ac Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 16 Apr 2015 12:29:09 -0400 Subject: [PATCH 1312/3476] Adding commit status updates to deployments and test runs! --- .../devshop_github/devshop_github.drush.inc | 55 +++++++++++++++++++ .../devshop_github/devshop_github.module | 48 +++++++++++++--- 2 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 modules/devshop/devshop_github/devshop_github.drush.inc diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc new file mode 100644 index 000000000..216f4cf6c --- /dev/null +++ b/modules/devshop/devshop_github/devshop_github.drush.inc @@ -0,0 +1,55 @@ +ref->type == 'site' && $task->task_type == 'test') { + + // Include vendors + require_once 'vendor/autoload.php'; + + drush_log('===========================================', 'ok'); + drush_log('Notifying github...', 'ok'); + + + try { + $token = variable_get('devshop_github_token', ''); + $client = new \Github\Client(); + $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); + + // Create a status + $sha = $task->ref->environment->github_pull_request->pull_request_object->head->sha; + + $params = new stdClass(); + $params->state = 'pending'; + $params->target_url = url("devshop_tests/{$task->nid}/{$task->vid}", array('absolute' => TRUE));; + $params->description = 'Test run has been queued.'; + $params->context = 'devshop/test'; + + $owner = $task->ref->project->github_owner; + $repo = $task->ref->project->github_repo; + + $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + + drush_log('Status posted! ', 'ok'); + } catch (Github\Exception\RuntimeException $e) { + drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); + drush_log(l(t('Configure GitHub API'), 'admin/hosting/devshop/github'), 'error'); + } catch (Github\Exception\ValidationFailedException $e) { + drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); + } + + + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index 868166bdd..4166a44c3 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -91,12 +91,44 @@ function devshop_github_hosting_task_update_status($task, $status) { $client = new \Github\Client(); $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); - $comment = $client->api('issue') - ->comments() - ->create($task->ref->project->github_owner, $task->ref->project->github_repo, $task->ref->environment->github_pull_request->number, array( - 'body' => devshop_github_comment($task, $status), - )); - drush_log('Comment posted! ' . print_r($comment, 1), 'ok'); + // Create a status + $sha = $task->ref->environment->github_pull_request->pull_request_object->head->sha; + + if ($task->task_type == 'devshop-deploy'){ + $description = t('Deployed to Environment: ') . _hosting_parse_error_code($status); + $url = $task->ref->environment->url; + } + elseif ($task->task_type == 'test') { + $description = t('Tests: ') . _hosting_parse_error_code($status); + $url = url("devshop_tests/{$task->nid}/{$task->vid}", array('absolute' => TRUE)); + } + else { + $description = 'Something happened...'; + $url = $task->ref->environment->url; + } + + if ($status == HOSTING_TASK_ERROR) { + $state = 'error'; + } + elseif ($status == HOSTING_TASK_PROCESSING) { + $state = 'pending'; + } + elseif ($status == HOSTING_TASK_SUCCESS || $status == HOSTING_TASK_WARNING) { + $state = 'success'; + } + else { + $state = 'error'; + } + + $params = new stdClass(); + $params->state = $state; + $params->target_url = $url; + $params->description = $description; + $params->context = 'devshop/' . $task->task_type; + + $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + + drush_log('Status posted! ', 'ok'); } catch (Github\Exception\RuntimeException $e) { drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); drush_log(l(t('Configure GitHub API'), 'admin/hosting/devshop/github'), 'error'); @@ -282,13 +314,13 @@ function devshop_github_webhook($project_node){ // Create a status $sha = $data->pull_request->head->sha; - $url = "http://{$environment_name}.{$project->base_url}"; + $url = url("node/{$project->nid}", array('absolute' => TRUE)); $params = new stdClass(); $params->state = 'pending'; $params->target_url = "http://{$environment_name}.{$project->base_url}"; $params->description = 'Deploying and testing with DevShop...'; - $params->context = 'devshop/test'; + $params->context = 'devshop/devshop-deploy'; $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); From 56f690d18e036db0b60984c135795925b8fe9278 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 10:03:18 -0400 Subject: [PATCH 1313/3476] Changing README.txt to README.md --- README.txt => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README.txt => README.md (100%) diff --git a/README.txt b/README.md similarity index 100% rename from README.txt rename to README.md From 2dc89f181d5d394a0463a1bd46c22c5f032cbe82 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 21:19:30 -0400 Subject: [PATCH 1314/3476] Adding devshop_github to installer. --- devmaster.profile | 1 + 1 file changed, 1 insertion(+) diff --git a/devmaster.profile b/devmaster.profile index 030bcebe3..3dccf0151 100644 --- a/devmaster.profile +++ b/devmaster.profile @@ -36,6 +36,7 @@ function devmaster_profile_modules() { 'devshop_projects', 'devshop_log', 'devshop_pull', + 'devshop_github', /* NICEITIES */ 'hosting_drush_aliases', From ee12dc2d633a17fc88b6a710f445bfb450b7efe9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 21:22:31 -0400 Subject: [PATCH 1315/3476] Fixing git ignore file. --- .gitignore | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index aa4d2ad52..3d6f4498b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ - -# devshop repos -source - -# Local config -.vagrant +; Ignore modules from make file. +modules/contrib +modules/hosting \ No newline at end of file From 3621eac8c93c49f35dc3ecbdcf002e8a9ca474c8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 21:51:27 -0400 Subject: [PATCH 1316/3476] Fixing versions in devmaster.make file, removing unused modules from install profile. --- devmaster.make | 54 ++++++++++++++++++----------------------------- devmaster.profile | 2 -- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/devmaster.make b/devmaster.make index 2956b5b76..9e3f62e5e 100644 --- a/devmaster.make +++ b/devmaster.make @@ -8,16 +8,15 @@ projects[drupal][type] = "core" includes[devshop] = "drupal-org.make" ; Aegir Modules -projects[hosting][download][type] = 'git' -projects[hosting][download][url] = 'http://git.drupal.org/project/hosting.git' -projects[hosting][download][branch] = '6.x-2.x' +projects[hosting][version] = '6.x-2.3' -projects[hosting_platform_pathauto][version] = "2.1" -projects[hosting_platform_pathauto][subdir] = contrib - -projects[admin_menu][version] = "1.8" +; Contrib Modules +projects[admin_menu][version] = "1.9" projects[admin_menu][subdir] = contrib +projects[adminrole][version] = "1.3" +projects[adminrole][subdir] = contrib + projects[openidadmin][version] = "1.2" projects[openidadmin][subdir] = contrib @@ -30,46 +29,33 @@ projects[jquery_ui][subdir] = contrib projects[jquery_update][version] = "2.0-alpha1" projects[jquery_update][subdir] = contrib -projects[modalframe][version] = "1.9" -projects[modalframe][subdir] = contrib - -projects[views][version] = "3.0" +projects[views][version] = "3.2" projects[views][subdir] = contrib projects[views_bulk_operations][version] = "1.16" projects[views_bulk_operations][subdir] = contrib -projects[ctools][version] = "1.11" -projects[ctools][subdir] = contrib - -; Libraries -libraries[jquery_ui][download][type] = "get" -libraries[jquery_ui][destination] = "modules/contrib/jquery_ui" -libraries[jquery_ui][download][url] = "http://jquery-ui.googlecode.com/files/jquery-ui-1.7.3.zip" -libraries[jquery_ui][directory_name] = "jquery.ui" - -; Contrib modules -projects[adminrole][subdir] = contrib +projects[ctools][version] = "1.12" projects[ctools][subdir] = contrib -projects[jquery_update][subdir] = contrib -; Aegir Contrib +projects[hosting_filemanager][version] = "1.0-beta1" projects[hosting_filemanager][subdir] = contrib + +projects[hosting_tasks_extra][version] = "2.1" projects[hosting_tasks_extra][subdir] = contrib ; Aegir Contrib maintained by devshop maintainers +projects[hosting_solr][version] = "1.0-beta3" projects[hosting_solr][subdir] = contrib -projects[hosting_solr][download][type] = git -projects[hosting_solr][download][branch] = 6.x-1.x - -projects[hosting_drush_aliases][subdir] = contrib -projects[hosting_drush_aliases][download][type] = git -projects[hosting_drush_aliases][download][branch] = 6.x-1.x +projects[hosting_logs][version] = "2.0-alpha1" projects[hosting_logs][subdir] = contrib -projects[hosting_logs][download][type] = git -projects[hosting_logs][download][branch] = 6.x-1.x +projects[hosting_site_backup_manager][version] = "2.0" projects[hosting_site_backup_manager][subdir] = contrib -projects[hosting_site_backup_manager][download][type] = git -projects[hosting_site_backup_manager][download][branch] = 6.x-2.x + +; Libraries +libraries[jquery_ui][download][type] = "get" +libraries[jquery_ui][destination] = "modules/contrib/jquery_ui" +libraries[jquery_ui][download][url] = "http://jquery-ui.googlecode.com/files/jquery-ui-1.7.3.zip" +libraries[jquery_ui][directory_name] = "jquery.ui" diff --git a/devmaster.profile b/devmaster.profile index 3dccf0151..03ab82652 100644 --- a/devmaster.profile +++ b/devmaster.profile @@ -25,7 +25,6 @@ function devmaster_profile_modules() { 'views', 'views_bulk_operations', 'actions_permissions', - 'hosting_platform_pathauto', /* DEVSHOP Contrib */ 'adminrole', @@ -39,7 +38,6 @@ function devmaster_profile_modules() { 'devshop_github', /* NICEITIES */ - 'hosting_drush_aliases', 'hosting_filemanager', 'hosting_logs', 'hosting_tasks_extra', From b4b2739d91c70c8aaf4d4da30be0981d33103d38 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 22:31:36 -0400 Subject: [PATCH 1317/3476] Fixing wrong version number. --- devmaster.make | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devmaster.make b/devmaster.make index 9e3f62e5e..1695054ed 100644 --- a/devmaster.make +++ b/devmaster.make @@ -8,7 +8,7 @@ projects[drupal][type] = "core" includes[devshop] = "drupal-org.make" ; Aegir Modules -projects[hosting][version] = '6.x-2.3' +projects[hosting][version] = "2.3" ; Contrib Modules projects[admin_menu][version] = "1.9" From b0c3256320de11968ff7f736135dcf2a32588f5b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 22:35:33 -0400 Subject: [PATCH 1318/3476] Updating hosting_solr version. --- devmaster.make | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devmaster.make b/devmaster.make index 1695054ed..ffde2d45d 100644 --- a/devmaster.make +++ b/devmaster.make @@ -45,7 +45,7 @@ projects[hosting_tasks_extra][version] = "2.1" projects[hosting_tasks_extra][subdir] = contrib ; Aegir Contrib maintained by devshop maintainers -projects[hosting_solr][version] = "1.0-beta3" +projects[hosting_solr][version] = "1.0" projects[hosting_solr][subdir] = contrib projects[hosting_logs][version] = "2.0-alpha1" From 19b7ccedae91da87e425ef94d669a8b276b98743 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 22:35:56 -0400 Subject: [PATCH 1319/3476] Updating hosting_solr version --- devmaster.make | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devmaster.make b/devmaster.make index ffde2d45d..68c753acc 100644 --- a/devmaster.make +++ b/devmaster.make @@ -45,7 +45,7 @@ projects[hosting_tasks_extra][version] = "2.1" projects[hosting_tasks_extra][subdir] = contrib ; Aegir Contrib maintained by devshop maintainers -projects[hosting_solr][version] = "1.0" +projects[hosting_solr][version] = "1" projects[hosting_solr][subdir] = contrib projects[hosting_logs][version] = "2.0-alpha1" From 1bf794b4124834e838c841e0cb4b8f5ecdb7ffb5 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 22:36:22 -0400 Subject: [PATCH 1320/3476] Fixing hosting_logs version --- devmaster.make | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devmaster.make b/devmaster.make index 68c753acc..2c68fe3a9 100644 --- a/devmaster.make +++ b/devmaster.make @@ -48,7 +48,7 @@ projects[hosting_tasks_extra][subdir] = contrib projects[hosting_solr][version] = "1" projects[hosting_solr][subdir] = contrib -projects[hosting_logs][version] = "2.0-alpha1" +projects[hosting_logs][version] = "2" projects[hosting_logs][subdir] = contrib projects[hosting_site_backup_manager][version] = "2.0" From cbd6a54d077e7a79a4d5366503b593aac20bd3b4 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 23:51:04 -0400 Subject: [PATCH 1321/3476] Adding missing hosting_backup_queue to devmaster.make file. --- devmaster.make | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devmaster.make b/devmaster.make index 2c68fe3a9..bc71a3c5b 100644 --- a/devmaster.make +++ b/devmaster.make @@ -51,6 +51,9 @@ projects[hosting_solr][subdir] = contrib projects[hosting_logs][version] = "2" projects[hosting_logs][subdir] = contrib +projects[hosting_backup_queue][version] = "2" +projects[hosting_backup_queue][subdir] = contrib + projects[hosting_site_backup_manager][version] = "2.0" projects[hosting_site_backup_manager][subdir] = contrib From b34cd07765c70fc6a06584e42f73c829b7f4770b Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 23:53:49 -0400 Subject: [PATCH 1322/3476] Removing bad bin behat call. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cde4fd749..12bc70dc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,14 +7,14 @@ install: # Install DevShop with the standalone installer, using local playbooks. # Run as actual root? Testing to see if this is a problem. - - sudo su - root -c "wget https://raw.githubusercontent.com/opendevshop/devshop/0.x/install.sh && bash install.sh" + - sudo su - root -c "wget https://raw.githubusercontent.com/opendevshop/devshop/0.x/install.sh && bash install.sh"d script: # Test for DevShop - sudo su - aegir -c "drush @hostmaster status" - sudo su -c "cd /usr/share/devshop/tests && composer update" - - sudo su aegir -c "cd /usr/share/devshop/tests && bin/behat --profile travis" +# - sudo su aegir -c "cd /usr/share/devshop/tests && bin/behat --profile travis" # Notify devshop IRC chatroom. notifications: From 4ba512a1c68235639363df9ae281ddf7ce71b3c0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 23:55:08 -0400 Subject: [PATCH 1323/3476] typo in travis.yml! --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 12bc70dc7..55bfb230b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ install: # Install DevShop with the standalone installer, using local playbooks. # Run as actual root? Testing to see if this is a problem. - - sudo su - root -c "wget https://raw.githubusercontent.com/opendevshop/devshop/0.x/install.sh && bash install.sh"d + - sudo su - root -c "wget https://raw.githubusercontent.com/opendevshop/devshop/0.x/install.sh && bash install.sh" script: From 687b982600a5d7e4d9c8e7f8b978cd40d65dec23 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Fri, 1 May 2015 23:58:15 -0400 Subject: [PATCH 1324/3476] removing unneeded composer update. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 55bfb230b..5db901fb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,11 +9,13 @@ install: # Run as actual root? Testing to see if this is a problem. - sudo su - root -c "wget https://raw.githubusercontent.com/opendevshop/devshop/0.x/install.sh && bash install.sh" + # @TODO: Pass the git URL of the repo that is being tested to the install script so this can run on pull requests. + script: # Test for DevShop - sudo su - aegir -c "drush @hostmaster status" - - sudo su -c "cd /usr/share/devshop/tests && composer update" +# - sudo su -c "cd /usr/share/devshop/tests && composer update" # - sudo su aegir -c "cd /usr/share/devshop/tests && bin/behat --profile travis" # Notify devshop IRC chatroom. From b281cdbe8117bf8ee55f528978da290be12e5234 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 2 May 2015 00:03:42 -0400 Subject: [PATCH 1325/3476] Removing hosting_backup_queue --- devmaster.make | 3 --- 1 file changed, 3 deletions(-) diff --git a/devmaster.make b/devmaster.make index bc71a3c5b..2c68fe3a9 100644 --- a/devmaster.make +++ b/devmaster.make @@ -51,9 +51,6 @@ projects[hosting_solr][subdir] = contrib projects[hosting_logs][version] = "2" projects[hosting_logs][subdir] = contrib -projects[hosting_backup_queue][version] = "2" -projects[hosting_backup_queue][subdir] = contrib - projects[hosting_site_backup_manager][version] = "2.0" projects[hosting_site_backup_manager][subdir] = contrib From 7c5b3d4bf3497020348d8d49d04e88b3273dbcd0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 2 May 2015 00:04:55 -0400 Subject: [PATCH 1326/3476] removing modalframe from install profile. --- devmaster.profile | 1 - 1 file changed, 1 deletion(-) diff --git a/devmaster.profile b/devmaster.profile index 03ab82652..348f17930 100644 --- a/devmaster.profile +++ b/devmaster.profile @@ -20,7 +20,6 @@ function devmaster_profile_modules() { 'install_profile_api', 'jquery_ui', 'jquery_update', - 'modalframe', 'admin_menu', 'views', 'views_bulk_operations', From 340244d780bbd054e83b8d540fa0f0f033e95b77 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 3 May 2015 10:10:54 -0400 Subject: [PATCH 1327/3476] Removing legacy readme.txt --- README.txt | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 README.txt diff --git a/README.txt b/README.txt deleted file mode 100644 index fc6520332..000000000 --- a/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -DevShop DevMaster -================= - -This distribution is an addition to the Aegir Hostmaster project. - -Read more about hostmaster at http://drupal.org/project/hostmaster - -For compatibility, the version of devmaster, devshop_provision, and devshop_hosting -will always track that of hostmaster, provision, and hosting. - - From a375ff131bb598a6a87fc6698fa7403627280167 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 3 May 2015 10:17:28 -0400 Subject: [PATCH 1328/3476] Fixing up old README.md in devmaster to reflect that this is no longer the main project. --- README.md | 86 ++++++++++++++++--------------------------------------- 1 file changed, 24 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 269b1246b..cb5f900c3 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,37 @@ -DevShop -======= +DevShop DevMaster +================= -Welcome to the DevShop Source code. +This is the DevShop web-based front-end, called Devmaster. -[![Build Status](https://travis-ci.org/drupalprojects/devshop.svg?branch=6.x-1.x)](https://travis-ci.org/drupalprojects/devshop) +This is a Drupal 6.x distribution. -Contents --------- - -This project contains four important components to DevShop +This project should not be used on it's own. The main devshop project +installer will use this install profile as a part of the setup process. -1. install.sh - A bash script to go from zero to DevShop. All you need is a new server and this script. - This script is designed to work all by itself, and is not dependent on the other files in this repo to run. +See http://github.com/opendevshop/devshop for more information. -2. build-devshop.make - The makefile used to build the DevShop front-end. +Contents +======== -3. devshop.profile - The Drupal installation profile for DevShop. Used to setup the front end. - -4. Vagrantfile - Allows devshop to be launched with Vagrant. Used for testing and development. See README.vagrant.md for more info. +This project contains: -Installation ------------- +1. devmaster.make + The makefile used to build devmaster. -1. Pick a domain and server name to use for DevShop, for example "devshop.thinkdrop.net" -2. Fire up a linux server somewhere, using that domain name as the server's hostname. (Ubuntu 12.04 is the most tested.) - - Rackspace and DigitalOcean use the name of the server to automatically set the hostname, so use your domain name - as the server name when creating it. - - On Amazon Web Services you must [change the hostname manually](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-hostname.html). - - On Linode, you must also [set the hostname manually](https://www.linode.com/docs/getting-started#setting-the-hostname). -3. Add a DNS record that points your domain name (devshop.thinkdrop.net) to your server's IP address. -4. Add a second DNS record that points a wildcard subdomain of your domain (*.devshop.thinkdrop.net) to your server's IP - address. This allows you to setup new sites without having to mess with DNS every time. -5. Login to your server as root, and retrieve and run the install script: - ``` - root@devshop:~# wget http://getdevshop.com/install - root@devshop:~# bash install - ``` +2. devmaster.profile + The Drupal installation profile for Devmaster. -*NOTE:* http://getdevshop.com/install simply redirects to the dev version of install.sh: http://drupalcode.org/project/devshop.git/blob_plain/refs/heads/6.x-1.x:... - - -Usage ------ - -Using devshop is a lot like using aegir. - -Visit http://devshop.thinkdrop.net in the browser to view the front-end. - -SSH into your server as the `aegir` user to access the back-end. - -Use drush to access any of your sites. Use `drush sa` to see the list of available aliases. - -Vagrant -------- - -There is now a vagrantfile for DevShop that makes for an easy way to test it out and to contribute to the development of DevShop. - -It is included in this package. To use, clone this repo and vagrant up: - -See README.vagrant.md for more information. +3. DevShop modules: + All of the modules needed for devshop are contained in this repo, with the + exception of contrib modules that are also useful for Aegir. -Testing -------- +Issues & Development +==================== -Very rudimentary testing is happening on TravisCI at http://travisci.org/drupalprojects/devshop +This repo may be forked if you wish to contribute to development. -The install script has been tested on: +See DEVELOPMENT.md in the main devshop project for more information. + +Issues for devshop may be submitted to the drupal.org issue queue for devshop: - - ubuntu 12.04 - - centos 7.0 +https://www.drupal.org/project/issues/devshop \ No newline at end of file From d0b1d81a3ab4838526380ab3bc8cbbfc48ea4ae8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 3 May 2015 10:19:16 -0400 Subject: [PATCH 1329/3476] Adding whitespace and mention of the theme. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index cb5f900c3..346653cfd 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,22 @@ Contents This project contains: 1. devmaster.make + The makefile used to build devmaster. 2. devmaster.profile + The Drupal installation profile for Devmaster. 3. DevShop modules: + All of the modules needed for devshop are contained in this repo, with the exception of contrib modules that are also useful for Aegir. +4. The DevShop theme "Boots": + + The theme for devshop is contained in this repo. + Issues & Development ==================== From 905f8c55d49b205986bf83da7abd43580c74423e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 3 May 2015 10:20:24 -0400 Subject: [PATCH 1330/3476] Removing legacy INSTALL.txt --- INSTALL.txt | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 INSTALL.txt diff --git a/INSTALL.txt b/INSTALL.txt deleted file mode 100644 index 9a4022e4a..000000000 --- a/INSTALL.txt +++ /dev/null @@ -1,14 +0,0 @@ -DevShop Hosting -=============== - -devshop_hosting is the front-end code for DevShop. This module requires the -devshop distribution to work. - -Documentation is located in the main project: http://drupal.org/project/devshop - -Installation ------------- -Don't install this module by itself. It needs the entire devshop system to -function. - -For installation instructions, see http://drupal.org/project/devshop From ebe7f9da25aa96a3044ac384d257510c2e7ee0db Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sun, 3 May 2015 13:28:19 -0400 Subject: [PATCH 1331/3476] separating wget and install.sh call in travis. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5db901fb7..c457f1ed3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,8 @@ install: # Install DevShop with the standalone installer, using local playbooks. # Run as actual root? Testing to see if this is a problem. - - sudo su - root -c "wget https://raw.githubusercontent.com/opendevshop/devshop/0.x/install.sh && bash install.sh" + - wget https://raw.githubusercontent.com/opendevshop/devshop/0.x/install.sh + - sudo su root -c "bash install.sh" # @TODO: Pass the git URL of the repo that is being tested to the install script so this can run on pull requests. From 800da6d821d9b58957313434865cdbea36b6c872 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 07:30:49 -0400 Subject: [PATCH 1332/3476] un-ignoring vendor --- modules/devshop/devshop_github/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/devshop/devshop_github/.gitignore b/modules/devshop/devshop_github/.gitignore index 5657f6ea7..e69de29bb 100644 --- a/modules/devshop/devshop_github/.gitignore +++ b/modules/devshop/devshop_github/.gitignore @@ -1 +0,0 @@ -vendor \ No newline at end of file From 90af29a2af6c41303e14e8b871fb63d0f97ce1ee Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 07:31:02 -0400 Subject: [PATCH 1333/3476] adding vendor dir to include github api. --- .../devshop_github/vendor/autoload.php | 7 + .../vendor/composer/ClassLoader.php | 413 ++ .../vendor/composer/autoload_classmap.php | 9 + .../vendor/composer/autoload_namespaces.php | 13 + .../vendor/composer/autoload_psr4.php | 9 + .../vendor/composer/autoload_real.php | 50 + .../vendor/composer/installed.json | 223 + .../vendor/guzzle/guzzle/.gitignore | 27 + .../vendor/guzzle/guzzle/.travis.yml | 17 + .../vendor/guzzle/guzzle/CHANGELOG.md | 751 ++++ .../vendor/guzzle/guzzle/LICENSE | 19 + .../vendor/guzzle/guzzle/README.md | 57 + .../vendor/guzzle/guzzle/UPGRADING.md | 537 +++ .../vendor/guzzle/guzzle/build.xml | 45 + .../vendor/guzzle/guzzle/composer.json | 82 + .../vendor/guzzle/guzzle/docs/Makefile | 153 + .../docs/_downloads/guzzle-schema-1.0.json | 176 + .../guzzle/docs/_static/guzzle-icon.png | Bin 0 -> 803 bytes .../guzzle/guzzle/docs/_static/homepage.css | 122 + .../guzzle/guzzle/docs/_static/logo.png | Bin 0 -> 247678 bytes .../guzzle/guzzle/docs/_static/prettify.css | 41 + .../guzzle/guzzle/docs/_static/prettify.js | 28 + .../guzzle/guzzle/docs/_templates/index.html | 106 + .../guzzle/docs/_templates/leftbar.html | 0 .../guzzle/docs/_templates/nav_links.html | 5 + .../guzzle/guzzle/docs/batching/batching.rst | 183 + .../vendor/guzzle/guzzle/docs/conf.py | 94 + .../vendor/guzzle/guzzle/docs/docs.rst | 73 + .../guzzle/docs/getting-started/faq.rst | 29 + .../docs/getting-started/installation.rst | 154 + .../guzzle/docs/getting-started/overview.rst | 85 + .../guzzle/guzzle/docs/http-client/client.rst | 569 +++ .../guzzle/docs/http-client/entity-bodies.rst | 151 + .../docs/http-client/http-redirects.rst | 99 + .../guzzle/docs/http-client/request.rst | 667 +++ .../guzzle/docs/http-client/response.rst | 141 + .../guzzle/docs/http-client/uri-templates.rst | 52 + .../vendor/guzzle/guzzle/docs/index.rst | 5 + .../docs/iterators/guzzle-iterators.rst | 97 + .../docs/iterators/resource-iterators.rst | 149 + .../guzzle/docs/plugins/async-plugin.rst | 18 + .../guzzle/docs/plugins/backoff-plugin.rst | 22 + .../guzzle/docs/plugins/cache-plugin.rst | 169 + .../guzzle/docs/plugins/cookie-plugin.rst | 33 + .../guzzle/docs/plugins/creating-plugins.rst | 93 + .../guzzle/docs/plugins/curl-auth-plugin.rst | 32 + .../guzzle/docs/plugins/history-plugin.rst | 24 + .../guzzle/guzzle/docs/plugins/log-plugin.rst | 69 + .../docs/plugins/md5-validator-plugin.rst | 29 + .../guzzle/docs/plugins/mock-plugin.rst | 27 + .../guzzle/docs/plugins/oauth-plugin.rst | 30 + .../guzzle/docs/plugins/plugins-list.rst.inc | 9 + .../guzzle/docs/plugins/plugins-overview.rst | 59 + .../guzzle/guzzle/docs/requirements.txt | 2 + .../guzzle/docs/testing/unit-testing.rst | 201 + .../guzzle-service-descriptions.rst | 619 +++ .../using-the-service-builder.rst | 316 ++ .../webservice-client/webservice-client.rst | 659 +++ .../vendor/guzzle/guzzle/phar-stub.php | 16 + .../guzzle/guzzle/phing/build.properties.dist | 16 + .../guzzle/phing/imports/dependencies.xml | 33 + .../guzzle/guzzle/phing/imports/deploy.xml | 142 + .../guzzle/phing/tasks/ComposerLintTask.php | 152 + .../phing/tasks/GuzzlePearPharPackageTask.php | 338 ++ .../guzzle/phing/tasks/GuzzleSubSplitTask.php | 385 ++ .../vendor/guzzle/guzzle/phpunit.xml.dist | 48 + .../Guzzle/Batch/AbstractBatchDecorator.php | 66 + .../guzzle/guzzle/src/Guzzle/Batch/Batch.php | 92 + .../guzzle/src/Guzzle/Batch/BatchBuilder.php | 199 + .../src/Guzzle/Batch/BatchClosureDivisor.php | 39 + .../src/Guzzle/Batch/BatchClosureTransfer.php | 40 + .../src/Guzzle/Batch/BatchCommandTransfer.php | 75 + .../Guzzle/Batch/BatchDivisorInterface.php | 18 + .../src/Guzzle/Batch/BatchInterface.php | 32 + .../src/Guzzle/Batch/BatchRequestTransfer.php | 65 + .../src/Guzzle/Batch/BatchSizeDivisor.php | 47 + .../Guzzle/Batch/BatchTransferInterface.php | 16 + .../Exception/BatchTransferException.php | 90 + .../Guzzle/Batch/ExceptionBufferingBatch.php | 50 + .../guzzle/src/Guzzle/Batch/FlushingBatch.php | 60 + .../guzzle/src/Guzzle/Batch/HistoryBatch.php | 39 + .../src/Guzzle/Batch/NotifyingBatch.php | 38 + .../guzzle/src/Guzzle/Batch/composer.json | 31 + .../src/Guzzle/Cache/AbstractCacheAdapter.php | 21 + .../src/Guzzle/Cache/CacheAdapterFactory.php | 117 + .../Guzzle/Cache/CacheAdapterInterface.php | 55 + .../src/Guzzle/Cache/ClosureCacheAdapter.php | 57 + .../src/Guzzle/Cache/DoctrineCacheAdapter.php | 41 + .../src/Guzzle/Cache/NullCacheAdapter.php | 31 + .../src/Guzzle/Cache/Zf1CacheAdapter.php | 44 + .../src/Guzzle/Cache/Zf2CacheAdapter.php | 41 + .../guzzle/src/Guzzle/Cache/composer.json | 27 + .../Guzzle/Common/AbstractHasDispatcher.php | 49 + .../guzzle/src/Guzzle/Common/Collection.php | 403 ++ .../guzzle/guzzle/src/Guzzle/Common/Event.php | 52 + .../Exception/BadMethodCallException.php | 5 + .../Common/Exception/ExceptionCollection.php | 108 + .../Common/Exception/GuzzleException.php | 8 + .../Exception/InvalidArgumentException.php | 5 + .../Common/Exception/RuntimeException.php | 5 + .../Exception/UnexpectedValueException.php | 5 + .../src/Guzzle/Common/FromConfigInterface.php | 18 + .../Guzzle/Common/HasDispatcherInterface.php | 54 + .../src/Guzzle/Common/ToArrayInterface.php | 16 + .../guzzle/src/Guzzle/Common/Version.php | 29 + .../guzzle/src/Guzzle/Common/composer.json | 20 + .../Http/AbstractEntityBodyDecorator.php | 221 + .../src/Guzzle/Http/CachingEntityBody.php | 229 + .../guzzle/guzzle/src/Guzzle/Http/Client.php | 524 +++ .../src/Guzzle/Http/ClientInterface.php | 223 + .../src/Guzzle/Http/Curl/CurlHandle.php | 464 ++ .../guzzle/src/Guzzle/Http/Curl/CurlMulti.php | 423 ++ .../Guzzle/Http/Curl/CurlMultiInterface.php | 58 + .../src/Guzzle/Http/Curl/CurlMultiProxy.php | 150 + .../src/Guzzle/Http/Curl/CurlVersion.php | 66 + .../src/Guzzle/Http/Curl/RequestMediator.php | 147 + .../guzzle/src/Guzzle/Http/EntityBody.php | 201 + .../src/Guzzle/Http/EntityBodyInterface.php | 73 + .../Http/Exception/BadResponseException.php | 69 + .../ClientErrorResponseException.php | 8 + .../CouldNotRewindStreamException.php | 7 + .../Guzzle/Http/Exception/CurlException.php | 101 + .../Guzzle/Http/Exception/HttpException.php | 10 + .../Http/Exception/MultiTransferException.php | 145 + .../Http/Exception/RequestException.php | 39 + .../ServerErrorResponseException.php | 8 + .../Exception/TooManyRedirectsException.php | 5 + .../src/Guzzle/Http/IoEmittingEntityBody.php | 83 + .../Guzzle/Http/Message/AbstractMessage.php | 220 + .../Http/Message/EntityEnclosingRequest.php | 247 ++ .../EntityEnclosingRequestInterface.php | 137 + .../guzzle/src/Guzzle/Http/Message/Header.php | 182 + .../Http/Message/Header/CacheControl.php | 121 + .../Http/Message/Header/HeaderCollection.php | 108 + .../Http/Message/Header/HeaderFactory.php | 26 + .../Message/Header/HeaderFactoryInterface.php | 19 + .../Http/Message/Header/HeaderInterface.php | 83 + .../src/Guzzle/Http/Message/Header/Link.php | 93 + .../Guzzle/Http/Message/MessageInterface.php | 102 + .../src/Guzzle/Http/Message/PostFile.php | 124 + .../Guzzle/Http/Message/PostFileInterface.php | 83 + .../src/Guzzle/Http/Message/Request.php | 638 +++ .../Guzzle/Http/Message/RequestFactory.php | 359 ++ .../Http/Message/RequestFactoryInterface.php | 105 + .../Guzzle/Http/Message/RequestInterface.php | 318 ++ .../src/Guzzle/Http/Message/Response.php | 968 +++++ .../guzzle/src/Guzzle/Http/Mimetypes.php | 962 ++++ .../Http/QueryAggregator/CommaAggregator.php | 20 + .../QueryAggregator/DuplicateAggregator.php | 22 + .../Http/QueryAggregator/PhpAggregator.php | 27 + .../QueryAggregatorInterface.php | 22 + .../guzzle/src/Guzzle/Http/QueryString.php | 297 ++ .../src/Guzzle/Http/ReadLimitEntityBody.php | 122 + .../guzzle/src/Guzzle/Http/RedirectPlugin.php | 250 ++ .../src/Guzzle/Http/Resources/cacert.pem | 3870 +++++++++++++++++ .../guzzle/src/Guzzle/Http/StaticClient.php | 157 + .../guzzle/guzzle/src/Guzzle/Http/Url.php | 554 +++ .../guzzle/src/Guzzle/Http/composer.json | 32 + .../src/Guzzle/Inflection/Inflector.php | 38 + .../Guzzle/Inflection/InflectorInterface.php | 27 + .../Guzzle/Inflection/MemoizingInflector.php | 70 + .../Inflection/PreComputedInflector.php | 59 + .../src/Guzzle/Inflection/composer.json | 26 + .../src/Guzzle/Iterator/AppendIterator.php | 19 + .../src/Guzzle/Iterator/ChunkedIterator.php | 56 + .../src/Guzzle/Iterator/FilterIterator.php | 36 + .../src/Guzzle/Iterator/MapIterator.php | 34 + .../Guzzle/Iterator/MethodProxyIterator.php | 27 + .../guzzle/src/Guzzle/Iterator/README.md | 25 + .../guzzle/src/Guzzle/Iterator/composer.json | 27 + .../src/Guzzle/Log/AbstractLogAdapter.php | 16 + .../guzzle/src/Guzzle/Log/ArrayLogAdapter.php | 34 + .../src/Guzzle/Log/ClosureLogAdapter.php | 23 + .../src/Guzzle/Log/LogAdapterInterface.php | 18 + .../src/Guzzle/Log/MessageFormatter.php | 179 + .../src/Guzzle/Log/MonologLogAdapter.php | 34 + .../guzzle/src/Guzzle/Log/PsrLogAdapter.php | 36 + .../guzzle/src/Guzzle/Log/Zf1LogAdapter.php | 24 + .../guzzle/src/Guzzle/Log/Zf2LogAdapter.php | 21 + .../guzzle/src/Guzzle/Log/composer.json | 29 + .../src/Guzzle/Parser/Cookie/CookieParser.php | 131 + .../Parser/Cookie/CookieParserInterface.php | 33 + .../Parser/Message/AbstractMessageParser.php | 58 + .../Guzzle/Parser/Message/MessageParser.php | 110 + .../Parser/Message/MessageParserInterface.php | 27 + .../Parser/Message/PeclHttpMessageParser.php | 48 + .../src/Guzzle/Parser/ParserRegistry.php | 75 + .../Parser/UriTemplate/PeclUriTemplate.php | 26 + .../Guzzle/Parser/UriTemplate/UriTemplate.php | 254 ++ .../UriTemplate/UriTemplateInterface.php | 21 + .../src/Guzzle/Parser/Url/UrlParser.php | 48 + .../Guzzle/Parser/Url/UrlParserInterface.php | 19 + .../guzzle/src/Guzzle/Parser/composer.json | 19 + .../src/Guzzle/Plugin/Async/AsyncPlugin.php | 84 + .../src/Guzzle/Plugin/Async/composer.json | 27 + .../Backoff/AbstractBackoffStrategy.php | 91 + .../AbstractErrorCodeBackoffStrategy.php | 40 + .../Guzzle/Plugin/Backoff/BackoffLogger.php | 76 + .../Guzzle/Plugin/Backoff/BackoffPlugin.php | 126 + .../Backoff/BackoffStrategyInterface.php | 30 + .../Backoff/CallbackBackoffStrategy.php | 47 + .../Backoff/ConstantBackoffStrategy.php | 34 + .../Plugin/Backoff/CurlBackoffStrategy.php | 28 + .../Backoff/ExponentialBackoffStrategy.php | 25 + .../Plugin/Backoff/HttpBackoffStrategy.php | 30 + .../Plugin/Backoff/LinearBackoffStrategy.php | 36 + .../Backoff/ReasonPhraseBackoffStrategy.php | 25 + .../Backoff/TruncatedBackoffStrategy.php | 36 + .../src/Guzzle/Plugin/Backoff/composer.json | 28 + .../Cache/CacheKeyProviderInterface.php | 11 + .../src/Guzzle/Plugin/Cache/CachePlugin.php | 353 ++ .../Plugin/Cache/CacheStorageInterface.php | 43 + .../Plugin/Cache/CallbackCanCacheStrategy.php | 53 + .../Cache/CanCacheStrategyInterface.php | 30 + .../Plugin/Cache/DefaultCacheKeyProvider.php | 46 + .../Plugin/Cache/DefaultCacheStorage.php | 266 ++ .../Plugin/Cache/DefaultCanCacheStrategy.php | 32 + .../Plugin/Cache/DefaultRevalidation.php | 174 + .../Guzzle/Plugin/Cache/DenyRevalidation.php | 19 + .../Plugin/Cache/RevalidationInterface.php | 32 + .../Guzzle/Plugin/Cache/SkipRevalidation.php | 19 + .../src/Guzzle/Plugin/Cache/composer.json | 28 + .../src/Guzzle/Plugin/Cookie/Cookie.php | 538 +++ .../Cookie/CookieJar/ArrayCookieJar.php | 237 + .../Cookie/CookieJar/CookieJarInterface.php | 85 + .../Plugin/Cookie/CookieJar/FileCookieJar.php | 65 + .../src/Guzzle/Plugin/Cookie/CookiePlugin.php | 70 + .../Exception/InvalidCookieException.php | 7 + .../src/Guzzle/Plugin/Cookie/composer.json | 27 + .../Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php | 46 + .../src/Guzzle/Plugin/CurlAuth/composer.json | 27 + .../ErrorResponseExceptionInterface.php | 22 + .../ErrorResponse/ErrorResponsePlugin.php | 72 + .../Exception/ErrorResponseException.php | 7 + .../Guzzle/Plugin/ErrorResponse/composer.json | 27 + .../Guzzle/Plugin/History/HistoryPlugin.php | 163 + .../src/Guzzle/Plugin/History/composer.json | 27 + .../src/Guzzle/Plugin/Log/LogPlugin.php | 161 + .../src/Guzzle/Plugin/Log/composer.json | 28 + .../Plugin/Md5/CommandContentMd5Plugin.php | 57 + .../Guzzle/Plugin/Md5/Md5ValidatorPlugin.php | 88 + .../src/Guzzle/Plugin/Md5/composer.json | 27 + .../src/Guzzle/Plugin/Mock/MockPlugin.php | 245 ++ .../src/Guzzle/Plugin/Mock/composer.json | 27 + .../src/Guzzle/Plugin/Oauth/OauthPlugin.php | 306 ++ .../src/Guzzle/Plugin/Oauth/composer.json | 27 + .../guzzle/src/Guzzle/Plugin/composer.json | 44 + .../Guzzle/Service/AbstractConfigLoader.php | 177 + .../Guzzle/Service/Builder/ServiceBuilder.php | 189 + .../Builder/ServiceBuilderInterface.php | 40 + .../Service/Builder/ServiceBuilderLoader.php | 89 + .../Guzzle/Service/CachingConfigLoader.php | 46 + .../guzzle/src/Guzzle/Service/Client.php | 297 ++ .../src/Guzzle/Service/ClientInterface.php | 68 + .../Service/Command/AbstractCommand.php | 390 ++ .../Guzzle/Service/Command/ClosureCommand.php | 41 + .../Service/Command/CommandInterface.php | 128 + .../Command/CreateResponseClassEvent.php | 32 + .../Command/DefaultRequestSerializer.php | 169 + .../Service/Command/DefaultResponseParser.php | 55 + .../Service/Command/Factory/AliasFactory.php | 39 + .../Command/Factory/CompositeFactory.php | 154 + .../Command/Factory/ConcreteClassFactory.php | 47 + .../Command/Factory/FactoryInterface.php | 21 + .../Service/Command/Factory/MapFactory.php | 27 + .../Factory/ServiceDescriptionFactory.php | 71 + .../Request/AbstractRequestVisitor.php | 69 + .../LocationVisitor/Request/BodyVisitor.php | 58 + .../LocationVisitor/Request/HeaderVisitor.php | 44 + .../LocationVisitor/Request/JsonVisitor.php | 63 + .../Request/PostFieldVisitor.php | 18 + .../Request/PostFileVisitor.php | 24 + .../LocationVisitor/Request/QueryVisitor.php | 18 + .../Request/RequestVisitorInterface.php | 31 + .../Request/ResponseBodyVisitor.php | 18 + .../LocationVisitor/Request/XmlVisitor.php | 252 ++ .../Response/AbstractResponseVisitor.php | 26 + .../LocationVisitor/Response/BodyVisitor.php | 23 + .../Response/HeaderVisitor.php | 50 + .../LocationVisitor/Response/JsonVisitor.php | 93 + .../Response/ReasonPhraseVisitor.php | 23 + .../Response/ResponseVisitorInterface.php | 46 + .../Response/StatusCodeVisitor.php | 23 + .../LocationVisitor/Response/XmlVisitor.php | 151 + .../LocationVisitor/VisitorFlyweight.php | 138 + .../Service/Command/OperationCommand.php | 89 + .../Command/OperationResponseParser.php | 195 + .../Command/RequestSerializerInterface.php | 21 + .../Command/ResponseClassInterface.php | 18 + .../Command/ResponseParserInterface.php | 18 + .../Guzzle/Service/ConfigLoaderInterface.php | 22 + .../Guzzle/Service/Description/Operation.php | 547 +++ .../Description/OperationInterface.php | 159 + .../Guzzle/Service/Description/Parameter.php | 925 ++++ .../Service/Description/SchemaFormatter.php | 156 + .../Service/Description/SchemaValidator.php | 291 ++ .../Description/ServiceDescription.php | 271 ++ .../ServiceDescriptionInterface.php | 106 + .../Description/ServiceDescriptionLoader.php | 64 + .../Description/ValidatorInterface.php | 28 + .../Service/Exception/CommandException.php | 7 + .../Exception/CommandTransferException.php | 119 + .../Exception/DescriptionBuilderException.php | 7 + .../InconsistentClientTransferException.php | 38 + .../Exception/ResponseClassException.php | 9 + .../Exception/ServiceBuilderException.php | 7 + .../Exception/ServiceNotFoundException.php | 5 + .../Service/Exception/ValidationException.php | 30 + .../AbstractResourceIteratorFactory.php | 37 + .../CompositeResourceIteratorFactory.php | 67 + .../Resource/MapResourceIteratorFactory.php | 34 + .../src/Guzzle/Service/Resource/Model.php | 64 + .../Service/Resource/ResourceIterator.php | 254 ++ .../Resource/ResourceIteratorApplyBatched.php | 111 + .../Resource/ResourceIteratorClassFactory.php | 60 + .../ResourceIteratorFactoryInterface.php | 30 + .../Resource/ResourceIteratorInterface.php | 61 + .../guzzle/src/Guzzle/Service/composer.json | 29 + .../Guzzle/Stream/PhpStreamRequestFactory.php | 284 ++ .../guzzle/src/Guzzle/Stream/Stream.php | 289 ++ .../src/Guzzle/Stream/StreamInterface.php | 218 + .../Stream/StreamRequestFactoryInterface.php | 24 + .../guzzle/src/Guzzle/Stream/composer.json | 30 + .../Batch/AbstractBatchDecoratorTest.php | 33 + .../Guzzle/Tests/Batch/BatchBuilderTest.php | 86 + .../Tests/Batch/BatchClosureDivisorTest.php | 36 + .../Tests/Batch/BatchClosureTransferTest.php | 52 + .../Tests/Batch/BatchCommandTransferTest.php | 83 + .../Tests/Batch/BatchRequestTransferTest.php | 80 + .../Tests/Batch/BatchSizeDivisorTest.php | 24 + .../tests/Guzzle/Tests/Batch/BatchTest.php | 91 + .../Batch/ExceptionBufferingBatchTest.php | 45 + .../Guzzle/Tests/Batch/FlushingBatchTest.php | 40 + .../Guzzle/Tests/Batch/HistoryBatchTest.php | 26 + .../Guzzle/Tests/Batch/NotifyingBatchTest.php | 45 + .../Tests/Cache/CacheAdapterFactoryTest.php | 64 + .../Guzzle/Tests/Cache/CacheAdapterTest.php | 68 + .../Tests/Cache/ClosureCacheAdapterTest.php | 94 + .../Tests/Cache/NullCacheAdapterTest.php | 20 + .../Tests/Cache/Zf2CacheAdapterTest.php | 58 + .../Common/AbstractHasDispatcherTest.php | 63 + .../Guzzle/Tests/Common/CollectionTest.php | 529 +++ .../tests/Guzzle/Tests/Common/EventTest.php | 62 + .../Exception/BatchTransferExceptionTest.php | 21 + .../Exception/ExceptionCollectionTest.php | 66 + .../tests/Guzzle/Tests/Common/VersionTest.php | 27 + .../tests/Guzzle/Tests/GuzzleTestCase.php | 235 + .../Http/AbstractEntityBodyDecoratorTest.php | 34 + .../Tests/Http/CachingEntityBodyTest.php | 249 ++ .../tests/Guzzle/Tests/Http/ClientTest.php | 601 +++ .../Guzzle/Tests/Http/Curl/CurlHandleTest.php | 947 ++++ .../Tests/Http/Curl/CurlMultiProxyTest.php | 110 + .../Guzzle/Tests/Http/Curl/CurlMultiTest.php | 455 ++ .../Tests/Http/Curl/CurlVersionTest.php | 39 + .../Tests/Http/Curl/RequestMediatorTest.php | 67 + .../Guzzle/Tests/Http/EntityBodyTest.php | 182 + .../Http/Exception/CurlExceptionTest.php | 27 + .../Tests/Http/Exception/ExceptionTest.php | 66 + .../Exception/MultiTransferExceptionTest.php | 51 + .../Tests/Http/IoEmittingEntityBodyTest.php | 47 + .../Http/Message/AbstractMessageTest.php | 136 + .../Message/EntityEnclosingRequestTest.php | 434 ++ .../Http/Message/Header/HeaderFactoryTest.php | 29 + .../Tests/Http/Message/Header/LinkTest.php | 63 + .../Tests/Http/Message/HeaderComparison.php | 135 + .../Http/Message/HeaderComparisonTest.php | 115 + .../Guzzle/Tests/Http/Message/HeaderTest.php | 162 + .../Tests/Http/Message/PostFileTest.php | 88 + .../Tests/Http/Message/RequestFactoryTest.php | 616 +++ .../Guzzle/Tests/Http/Message/RequestTest.php | 639 +++ .../Tests/Http/Message/ResponseTest.php | 677 +++ .../tests/Guzzle/Tests/Http/MimetypesTest.php | 31 + .../QueryAggregator/CommaAggregatorTest.php | 30 + .../DuplicateAggregatorTest.php | 30 + .../QueryAggregator/PhpAggregatorTest.php | 32 + .../Guzzle/Tests/Http/QueryStringTest.php | 233 + .../Tests/Http/ReadLimitEntityBodyTest.php | 81 + .../Guzzle/Tests/Http/RedirectPluginTest.php | 277 ++ .../guzzle/tests/Guzzle/Tests/Http/Server.php | 191 + .../Guzzle/Tests/Http/StaticClientTest.php | 67 + .../tests/Guzzle/Tests/Http/UrlTest.php | 303 ++ .../guzzle/tests/Guzzle/Tests/Http/server.js | 146 + .../Guzzle/Tests/Inflection/InflectorTest.php | 37 + .../Inflection/MemoizingInflectorTest.php | 46 + .../Inflection/PreComputedInflectorTest.php | 45 + .../Tests/Iterator/AppendIteratorTest.php | 29 + .../Tests/Iterator/ChunkedIteratorTest.php | 52 + .../Tests/Iterator/FilterIteratorTest.php | 28 + .../Guzzle/Tests/Iterator/MapIteratorTest.php | 28 + .../Iterator/MethodProxyIteratorTest.php | 28 + .../Guzzle/Tests/Log/ArrayLogAdapterTest.php | 23 + .../Tests/Log/ClosureLogAdapterTest.php | 30 + .../Guzzle/Tests/Log/MessageFormatterTest.php | 143 + .../Guzzle/Tests/Log/PsrLogAdapterTest.php | 25 + .../Guzzle/Tests/Log/Zf2LogAdapterTest.php | 51 + .../Guzzle/Tests/Mock/CustomResponseModel.php | 21 + .../Guzzle/Tests/Mock/ErrorResponseMock.php | 25 + .../tests/Guzzle/Tests/Mock/ExceptionMock.php | 11 + .../tests/Guzzle/Tests/Mock/MockMulti.php | 11 + .../tests/Guzzle/Tests/Mock/MockObserver.php | 65 + .../tests/Guzzle/Tests/Mock/MockSubject.php | 7 + .../Parser/Cookie/CookieParserProvider.php | 381 ++ .../Tests/Parser/Cookie/CookieParserTest.php | 22 + .../Parser/Message/MessageParserProvider.php | 225 + .../Parser/Message/MessageParserTest.php | 58 + .../Message/PeclHttpMessageParserTest.php | 36 + .../Tests/Parser/ParserRegistryTest.php | 33 + .../UriTemplate/AbstractUriTemplateTest.php | 113 + .../UriTemplate/PeclUriTemplateTest.php | 27 + .../Parser/UriTemplate/UriTemplateTest.php | 106 + .../Tests/Plugin/Async/AsyncPluginTest.php | 93 + .../Backoff/AbstractBackoffStrategyTest.php | 86 + .../Plugin/Backoff/BackoffLoggerTest.php | 110 + .../Plugin/Backoff/BackoffPluginTest.php | 297 ++ .../Backoff/CallbackBackoffStrategyTest.php | 31 + .../Backoff/ConstantBackoffStrategyTest.php | 20 + .../Backoff/CurlBackoffStrategyTest.php | 36 + .../ExponentialBackoffStrategyTest.php | 23 + .../Backoff/HttpBackoffStrategyTest.php | 47 + .../Backoff/LinearBackoffStrategyTest.php | 21 + .../ReasonPhraseBackoffStrategyTest.php | 32 + .../Backoff/TruncatedBackoffStrategyTest.php | 30 + .../Tests/Plugin/Cache/CachePluginTest.php | 441 ++ .../Cache/CallbackCanCacheStrategyTest.php | 72 + .../Plugin/Cache/DefaultCacheStorageTest.php | 193 + .../Cache/DefaultCanCacheStrategyTest.php | 40 + .../Plugin/Cache/DefaultRevalidationTest.php | 248 ++ .../Plugin/Cache/DenyRevalidationTest.php | 19 + .../Plugin/Cache/SkipRevalidationTest.php | 19 + .../Cookie/CookieJar/ArrayCookieJarTest.php | 385 ++ .../Cookie/CookieJar/FileCookieJarTest.php | 63 + .../Tests/Plugin/Cookie/CookiePluginTest.php | 134 + .../Guzzle/Tests/Plugin/Cookie/CookieTest.php | 223 + .../Plugin/CurlAuth/CurlAuthPluginTest.php | 39 + .../ErrorResponse/ErrorResponsePluginTest.php | 137 + .../Plugin/History/HistoryPluginTest.php | 140 + .../Guzzle/Tests/Plugin/Log/LogPluginTest.php | 95 + .../Md5/CommandContentMd5PluginTest.php | 97 + .../Plugin/Md5/Md5ValidatorPluginTest.php | 120 + .../Tests/Plugin/Mock/MockPluginTest.php | 199 + .../Tests/Plugin/Oauth/OauthPluginTest.php | 345 ++ .../Service/AbstractConfigLoaderTest.php | 149 + .../Builder/ServiceBuilderLoaderTest.php | 177 + .../Service/Builder/ServiceBuilderTest.php | 317 ++ .../Tests/Service/CachingConfigLoaderTest.php | 43 + .../tests/Guzzle/Tests/Service/ClientTest.php | 320 ++ .../Service/Command/AbstractCommandTest.php | 16 + .../Service/Command/ClosureCommandTest.php | 54 + .../Tests/Service/Command/CommandTest.php | 445 ++ .../Command/DefaultRequestSerializerTest.php | 122 + .../Command/DefaultResponseParserTest.php | 59 + .../Command/Factory/AliasFactoryTest.php | 76 + .../Command/Factory/CompositeFactoryTest.php | 124 + .../Factory/ConcreteClassFactoryTest.php | 49 + .../Command/Factory/MapFactoryTest.php | 37 + .../Factory/ServiceDescriptionFactoryTest.php | 68 + .../Request/AbstractVisitorTestCase.php | 110 + .../Request/BodyVisitorTest.php | 63 + .../Request/HeaderVisitorTest.php | 48 + .../Request/JsonVisitorTest.php | 60 + .../Request/PostFieldVisitorTest.php | 33 + .../Request/PostFileVisitorTest.php | 54 + .../Request/QueryVisitorTest.php | 48 + .../Request/ResponseBodyVisitorTest.php | 20 + .../Request/XmlVisitorTest.php | 558 +++ .../Response/AbstractResponseVisitorTest.php | 29 + .../Response/BodyVisitorTest.php | 21 + .../Response/HeaderVisitorTest.php | 98 + .../Response/JsonVisitorTest.php | 157 + .../Response/ReasonPhraseVisitorTest.php | 21 + .../Response/StatusCodeVisitorTest.php | 21 + .../Response/XmlVisitorTest.php | 431 ++ .../LocationVisitor/VisitorFlyweightTest.php | 53 + .../Service/Command/OperationCommandTest.php | 102 + .../Command/OperationResponseParserTest.php | 335 ++ .../Service/Description/OperationTest.php | 308 ++ .../Service/Description/ParameterTest.php | 411 ++ .../Description/SchemaFormatterTest.php | 61 + .../Description/SchemaValidatorTest.php | 326 ++ .../ServiceDescriptionLoaderTest.php | 177 + .../Description/ServiceDescriptionTest.php | 240 + .../CommandTransferExceptionTest.php | 66 + ...nconsistentClientTransferExceptionTest.php | 15 + .../Exception/ValidationExceptionTest.php | 17 + .../Service/Mock/Command/IterableCommand.php | 31 + .../Service/Mock/Command/MockCommand.php | 32 + .../Service/Mock/Command/OtherCommand.php | 30 + .../Tests/Service/Mock/Command/Sub/Sub.php | 7 + .../Guzzle/Tests/Service/Mock/MockClient.php | 36 + .../Mock/Model/MockCommandIterator.php | 42 + .../CompositeResourceIteratorFactoryTest.php | 37 + .../MapResourceIteratorFactoryTest.php | 40 + .../Tests/Service/Resource/ModelTest.php | 65 + .../ResourceIteratorClassFactoryTest.php | 41 + .../Service/Resource/ResourceIteratorTest.php | 184 + .../Stream/PhpStreamRequestFactoryTest.php | 172 + .../tests/Guzzle/Tests/Stream/StreamTest.php | 189 + .../tests/Guzzle/Tests/TestData/FileBody.txt | 0 .../Tests/TestData/description/bar.json | 3 + .../Tests/TestData/description/baz.json | 3 + .../Tests/TestData/description/foo.json | 8 + .../Tests/TestData/description/recursive.json | 3 + .../tests/Guzzle/Tests/TestData/mock_response | 3 + .../Guzzle/Tests/TestData/services/json1.json | 18 + .../Guzzle/Tests/TestData/services/json2.json | 11 + .../Tests/TestData/services/services.json | 71 + .../Guzzle/Tests/TestData/test_service.json | 40 + .../Guzzle/Tests/TestData/test_service2.json | 7 + .../Guzzle/Tests/TestData/test_service_3.json | 40 + .../vendor/guzzle/guzzle/tests/bootstrap.php | 10 + .../vendor/knplabs/github-api/LICENSE | 22 + .../vendor/knplabs/github-api/README.md | 115 + .../vendor/knplabs/github-api/composer.json | 38 + .../github-api/lib/Github/Api/AbstractApi.php | 204 + .../lib/Github/Api/ApiInterface.php | 16 + .../lib/Github/Api/Authorizations.php | 45 + .../github-api/lib/Github/Api/CurrentUser.php | 154 + .../lib/Github/Api/CurrentUser/DeployKeys.php | 94 + .../lib/Github/Api/CurrentUser/Emails.php | 69 + .../lib/Github/Api/CurrentUser/Followers.php | 70 + .../Github/Api/CurrentUser/Notifications.php | 130 + .../lib/Github/Api/CurrentUser/Starring.php | 73 + .../lib/Github/Api/CurrentUser/Watchers.php | 74 + .../github-api/lib/Github/Api/Deployment.php | 84 + .../github-api/lib/Github/Api/Enterprise.php | 50 + .../lib/Github/Api/Enterprise/License.php | 20 + .../Api/Enterprise/ManagementConsole.php | 77 + .../lib/Github/Api/Enterprise/Stats.php | 128 + .../lib/Github/Api/Enterprise/UserAdmin.php | 36 + .../lib/Github/Api/Gist/Comments.php | 37 + .../github-api/lib/Github/Api/Gists.php | 88 + .../github-api/lib/Github/Api/GitData.php | 58 + .../lib/Github/Api/GitData/Blobs.php | 38 + .../lib/Github/Api/GitData/Commits.php | 27 + .../lib/Github/Api/GitData/References.php | 56 + .../lib/Github/Api/GitData/Tags.php | 40 + .../lib/Github/Api/GitData/Trees.php | 42 + .../github-api/lib/Github/Api/Issue.php | 182 + .../lib/Github/Api/Issue/Comments.php | 70 + .../lib/Github/Api/Issue/Events.php | 30 + .../lib/Github/Api/Issue/Labels.php | 76 + .../lib/Github/Api/Issue/Milestones.php | 64 + .../github-api/lib/Github/Api/Markdown.php | 48 + .../github-api/lib/Github/Api/Meta.php | 22 + .../lib/Github/Api/Notification.php | 60 + .../lib/Github/Api/Organization.php | 68 + .../lib/Github/Api/Organization/Members.php | 53 + .../lib/Github/Api/Organization/Teams.php | 95 + .../github-api/lib/Github/Api/PullRequest.php | 124 + .../lib/Github/Api/PullRequest/Comments.php | 51 + .../github-api/lib/Github/Api/Repo.php | 466 ++ .../lib/Github/Api/Repository/Assets.php | 124 + .../Github/Api/Repository/Collaborators.php | 32 + .../lib/Github/Api/Repository/Comments.php | 72 + .../lib/Github/Api/Repository/Commits.php | 31 + .../lib/Github/Api/Repository/Contents.php | 274 ++ .../lib/Github/Api/Repository/DeployKeys.php | 46 + .../lib/Github/Api/Repository/Downloads.php | 59 + .../lib/Github/Api/Repository/Forks.php | 26 + .../lib/Github/Api/Repository/Hooks.php | 51 + .../lib/Github/Api/Repository/Labels.php | 46 + .../lib/Github/Api/Repository/Releases.php | 98 + .../lib/Github/Api/Repository/Statuses.php | 62 + .../github-api/lib/Github/Api/Search.php | 83 + .../github-api/lib/Github/Api/User.php | 208 + .../knplabs/github-api/lib/Github/Client.php | 343 ++ .../Exception/ApiLimitExceedException.php | 16 + .../Exception/BadMethodCallException.php | 12 + .../lib/Github/Exception/ErrorException.php | 12 + .../Github/Exception/ExceptionInterface.php | 7 + .../Exception/InvalidArgumentException.php | 12 + .../Exception/MissingArgumentException.php | 20 + .../lib/Github/Exception/RuntimeException.php | 12 + ...oFactorAuthenticationRequiredException.php | 19 + .../Exception/ValidationFailedException.php | 12 + .../HttpClient/Cache/CacheInterface.php | 51 + .../HttpClient/Cache/FilesystemCache.php | 85 + .../HttpClient/Cache/GaufretteCache.php | 71 + .../Github/HttpClient/CachedHttpClient.php | 108 + .../lib/Github/HttpClient/HttpClient.php | 190 + .../Github/HttpClient/HttpClientInterface.php | 112 + .../HttpClient/Listener/AuthListener.php | 68 + .../HttpClient/Listener/ErrorListener.php | 94 + .../HttpClient/Message/ResponseMediator.php | 50 + .../github-api/lib/Github/ResultPager.php | 168 + .../lib/Github/ResultPagerInterface.php | 90 + .../Component/EventDispatcher/.gitignore | 3 + .../Component/EventDispatcher/CHANGELOG.md | 23 + .../ContainerAwareEventDispatcher.php | 202 + .../Debug/TraceableEventDispatcher.php | 320 ++ .../TraceableEventDispatcherInterface.php | 34 + .../EventDispatcher/Debug/WrappedListener.php | 71 + .../RegisterListenersPass.php | 110 + .../Component/EventDispatcher/Event.php | 130 + .../EventDispatcher/EventDispatcher.php | 185 + .../EventDispatcherInterface.php | 96 + .../EventSubscriberInterface.php | 50 + .../EventDispatcher/GenericEvent.php | 186 + .../ImmutableEventDispatcher.php | 93 + .../Symfony/Component/EventDispatcher/LICENSE | 19 + .../Component/EventDispatcher/README.md | 27 + .../Tests/AbstractEventDispatcherTest.php | 369 ++ .../ContainerAwareEventDispatcherTest.php | 249 ++ .../Debug/TraceableEventDispatcherTest.php | 185 + .../RegisterListenersPassTest.php | 200 + .../Tests/EventDispatcherTest.php | 22 + .../EventDispatcher/Tests/EventTest.php | 100 + .../Tests/GenericEventTest.php | 139 + .../Tests/ImmutableEventDispatcherTest.php | 105 + .../Component/EventDispatcher/composer.json | 43 + .../EventDispatcher/phpunit.xml.dist | 28 + 611 files changed, 69695 insertions(+) create mode 100644 modules/devshop/devshop_github/vendor/autoload.php create mode 100644 modules/devshop/devshop_github/vendor/composer/ClassLoader.php create mode 100644 modules/devshop/devshop_github/vendor/composer/autoload_classmap.php create mode 100644 modules/devshop/devshop_github/vendor/composer/autoload_namespaces.php create mode 100644 modules/devshop/devshop_github/vendor/composer/autoload_psr4.php create mode 100644 modules/devshop/devshop_github/vendor/composer/autoload_real.php create mode 100644 modules/devshop/devshop_github/vendor/composer/installed.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/.gitignore create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/.travis.yml create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/CHANGELOG.md create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/LICENSE create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/README.md create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/UPGRADING.md create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/build.xml create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/Makefile create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/homepage.css create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/logo.png create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.css create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.js create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/index.html create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/leftbar.html create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/nav_links.html create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/batching/batching.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/conf.py create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/docs.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/faq.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/installation.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/overview.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/client.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/request.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/response.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/index.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/requirements.txt create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/testing/unit-testing.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phar-stub.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/build.properties.dist create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/dependencies.xml create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/deploy.xml create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/phpunit.xml.dist create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Version.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Request.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Response.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/Operation.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/composer.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/GuzzleTestCase.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php create mode 100755 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockMulti.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserProvider.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/MockClient.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json create mode 100644 modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/bootstrap.php create mode 100755 modules/devshop/devshop_github/vendor/knplabs/github-api/LICENSE create mode 100755 modules/devshop/devshop_github/vendor/knplabs/github-api/README.md create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/composer.json create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/ApiInterface.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Authorizations.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/DeployKeys.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Emails.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Followers.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Notifications.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Starring.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Watchers.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Deployment.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/License.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/ManagementConsole.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/Stats.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/UserAdmin.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gist/Comments.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gists.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Blobs.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Commits.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/References.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Tags.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Trees.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Comments.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Events.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Labels.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Milestones.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Markdown.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Meta.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Notification.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Members.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Teams.php create mode 100755 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Comments.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repo.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Assets.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Collaborators.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Comments.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Commits.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Contents.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/DeployKeys.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Downloads.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Forks.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Hooks.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Labels.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Releases.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Statuses.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Search.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/User.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Client.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ApiLimitExceedException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/BadMethodCallException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ErrorException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ExceptionInterface.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/InvalidArgumentException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/MissingArgumentException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/RuntimeException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ValidationFailedException.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/CacheInterface.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/FilesystemCache.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/GaufretteCache.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/CachedHttpClient.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClient.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClientInterface.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/AuthListener.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/ErrorListener.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Message/ResponseMediator.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPager.php create mode 100644 modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPagerInterface.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json create mode 100644 modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist diff --git a/modules/devshop/devshop_github/vendor/autoload.php b/modules/devshop/devshop_github/vendor/autoload.php new file mode 100644 index 000000000..6bba741a6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + private $classMapAuthoritative = false; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-0 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative) { + return false; + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/modules/devshop/devshop_github/vendor/composer/autoload_classmap.php b/modules/devshop/devshop_github/vendor/composer/autoload_classmap.php new file mode 100644 index 000000000..7a91153b0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + array($vendorDir . '/symfony/event-dispatcher'), + 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'), + 'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'), + 'Github\\' => array($vendorDir . '/knplabs/github-api/lib'), +); diff --git a/modules/devshop/devshop_github/vendor/composer/autoload_psr4.php b/modules/devshop/devshop_github/vendor/composer/autoload_psr4.php new file mode 100644 index 000000000..b265c64a2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/composer/autoload_psr4.php @@ -0,0 +1,9 @@ + $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(true); + + return $loader; + } +} + +function composerRequirec023ec1f16bdfa3119c161ea844cfa9e($file) +{ + require $file; +} diff --git a/modules/devshop/devshop_github/vendor/composer/installed.json b/modules/devshop/devshop_github/vendor/composer/installed.json new file mode 100644 index 000000000..0dc9fd353 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/composer/installed.json @@ -0,0 +1,223 @@ +[ + { + "name": "symfony/event-dispatcher", + "version": "v2.6.6", + "version_normalized": "2.6.6.0", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "70f7c8478739ad21e3deef0d977b38c77f1fb284" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/70f7c8478739ad21e3deef0d977b38c77f1fb284", + "reference": "70f7c8478739ad21e3deef0d977b38c77f1fb284", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/phpunit-bridge": "~2.7", + "symfony/stopwatch": "~2.3" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2015-03-13 17:37:22", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.3", + "version_normalized": "3.9.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + "time": "2015-03-18 18:23:50", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "name": "knplabs/github-api", + "version": "1.4.7", + "version_normalized": "1.4.7.0", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/php-github-api.git", + "reference": "d2729cf6b9d5b6fa340b1ff001dd89e319741baa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/php-github-api/zipball/d2729cf6b9d5b6fa340b1ff001dd89e319741baa", + "reference": "d2729cf6b9d5b6fa340b1ff001dd89e319741baa", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "guzzle/guzzle": "~3.7", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "knplabs/gaufrette": "Needed for optional Gaufrette cache" + }, + "time": "2015-04-07 20:24:33", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Github\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thibault Duplessis", + "email": "thibault.duplessis@gmail.com", + "homepage": "http://ornicar.github.com" + }, + { + "name": "KnpLabs Team", + "homepage": "http://knplabs.com" + } + ], + "description": "GitHub API v3 client", + "homepage": "https://github.com/KnpLabs/php-github-api", + "keywords": [ + "api", + "gh", + "gist", + "github" + ] + } +] diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/.gitignore b/modules/devshop/devshop_github/vendor/guzzle/guzzle/.gitignore new file mode 100644 index 000000000..893035d5b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/.gitignore @@ -0,0 +1,27 @@ +# Ingore common cruft +.DS_STORE +coverage +.idea + +# Ignore binary files +guzzle.phar +guzzle-min.phar + +# Ignore potentially sensitive phpunit file +phpunit.xml + +# Ignore composer generated files +composer.phar +composer.lock +composer-test.lock +vendor/ + +# Ignore build files +build/ +phing/build.properties + +# Ignore subsplit working directory +.subsplit + +docs/_build +docs/*.pyc diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/.travis.yml b/modules/devshop/devshop_github/vendor/guzzle/guzzle/.travis.yml new file mode 100644 index 000000000..209e05cd6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/.travis.yml @@ -0,0 +1,17 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - curl --version + - pecl install uri_template-beta || echo "pecl uri_template not available" + - composer self-update + - composer install --no-interaction --prefer-source --dev + - ~/.nvm/nvm.sh install v0.6.14 + +script: composer test diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/CHANGELOG.md b/modules/devshop/devshop_github/vendor/guzzle/guzzle/CHANGELOG.md new file mode 100644 index 000000000..f0dc5444a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/CHANGELOG.md @@ -0,0 +1,751 @@ +# CHANGELOG + +## 3.9.3 - 2015-03-18 + +* Ensuring Content-Length is not stripped from a request when it is `0`. +* Added more information to stream wrapper exceptions. +* Message parser will no longer throw warnings for malformed messages. +* Giving a valid cache TTL when max-age is 0. + +## 3.9.2 - 2014-09-10 + +* Retrying "Connection died, retrying a fresh connect" curl errors. +* Automatically extracting the cacert from the phar in client constructor. +* Added EntityBody support for OPTIONS requests. + +## 3.9.1 - 2014-05-07 + +* Added a fix to ReadLimitEntityBody to ensure it doesn't infinitely loop. +* Added a fix to the stream checksum function so that when the first read + returns a falsey value, it still continues to consume the stream until EOF. + +## 3.9.0 - 2014-04-23 + +* `null`, `false`, and `"_guzzle_blank_"` all now serialize as an empty value + with no trailing "=". See dc1d824277. +* No longer performing an MD5 check on the cacert each time the phar is used, + but rather copying the cacert to the temp directory. +* `"0"` can now be added as a URL path +* Deleting cookies that are set to empty +* If-Modified-Since is no longer unnecessarily added to the CachePlugin +* Cookie path matching now follows RFC 6265 s5.1.4 +* Updated service descriptions are now added to a service client's composite + factory. +* MockPlugin now throws an exception if the queue is empty. +* Properly parsing URLs that start with "http" but are not absolute +* Added the ability to configure the curl_multi_select timeout setting +* OAuth parameters are now sorted using lexicographical byte value ordering +* Fixing invalid usage of an out of range PHP feature in the ErrorResponsePlugin + +## 3.8.1 -2014-01-28 + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +## 3.8.0 - 2013-12-05 + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +## 3.7.4 - 2013-10-02 + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +## 3.7.3 - 2013-09-08 + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +## 3.7.2 - 2013-08-02 + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +## 3.7.1 - 2013-07-05 + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +## 3.7.0 - 2013-06-10 + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +## 3.6.0 - 2013-05-29 + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +## 3.5.0 - 2013-05-13 + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +## 3.4.3 - 2013-04-30 + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +## 3.4.2 - 2013-04-29 + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +## 3.4.1 - 2013-04-16 + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +## 3.4.0 - 2013-04-11 + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +## 3.3.1 - 2013-03-10 + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +## 3.3.0 - 2013-03-03 + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +## 3.2.0 - 2013-02-14 + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +## 3.1.2 - 2013-01-27 + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +## 3.1.1 - 2013-01-20 + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +## 3.1.0 - 2013-01-12 + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +## 3.0.7 - 2012-12-19 + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +## 3.0.6 - 2012-12-09 + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +## 3.0.5 - 2012-11-18 + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +## 3.0.4 - 2012-11-11 + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +## 3.0.3 - 2012-11-04 + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +## 3.0.2 - 2012-10-25 + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +## 3.0.1 - 2012-10-22 + +* Models can now be used like regular collection objects by calling filter, map, etc +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +## 3.0.0 - 2012-10-15 + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +## 2.8.8 - 2012-10-15 + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +## 2.8.7 - 2012-09-30 + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +## 2.8.6 - 2012-09-05 + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +## 2.8.5 - 2012-08-29 + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +## 2.8.4 - 2012-08-15 + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +## 2.8.3 - 2012-07-30 + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()`` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +## 2.8.2 - 2012-07-24 + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`` +* Bug: ``+`` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +## 2.8.1 - 2012-07-16 + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +## 2.8.0 - 2012-07-15 + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +## 2.7.2 - 2012-07-02 + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +## 2.7.1 - 2012-06-26 + +* Minor patch to update version number in UA string +* Updating build process + +## 2.7.0 - 2012-06-25 + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +## 2.6.6 - 2012-06-10 + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +## 2.6.5 - 2012-06-03 + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +## 2.6.4 - 2012-05-30 + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +## 2.6.3 - 2012-05-23 + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +## 2.6.2 - 2012-05-19 + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +## 2.6.1 - 2012-05-19 + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +## 2.6.0 - 2012-05-15 + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +## 2.5.0 - 2012-05-08 + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/LICENSE b/modules/devshop/devshop_github/vendor/guzzle/guzzle/LICENSE new file mode 100644 index 000000000..d51aa6986 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/README.md b/modules/devshop/devshop_github/vendor/guzzle/guzzle/README.md new file mode 100644 index 000000000..6be06bf47 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/README.md @@ -0,0 +1,57 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +# This is an old version of Guzzle + +This repository is for Guzzle 3.x. Guzzle 5.x, the new version of Guzzle, has +been released and is available at +[https://github.com/guzzle/guzzle](https://github.com/guzzle/guzzle). The +documentation for Guzzle version 5+ can be found at +[http://guzzlephp.org](http://guzzlephp.org). + +Guzzle 3 is only maintained for bug and security fixes. Guzzle 3 will be EOL +at some point in late 2015. + +### About Guzzle 3 + +[![Composer Downloads](https://poser.pugx.org/guzzle/guzzle/d/total.png)](https://packagist.org/packages/guzzle/guzzle) + [![Build Status](https://secure.travis-ci.org/guzzle/guzzle3.png?branch=master)](http://travis-ci.org/guzzle/guzzle3) + +- Extremely powerful API provides all the power of cURL with a simple interface. +- Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests. +- Service description DSL allows you build awesome web service clients faster. +- Symfony2 event-based plugin system allows you to completely modify the behavior of a request. + +Get answers with: [Documentation](http://guzzle3.readthedocs.org/en/latest/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net) + +### Installing via Composer + +The recommended way to install Guzzle is through [Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/guzzle:~3.9 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` +## Known Issues + +1. Problem following a specific redirect: https://github.com/guzzle/guzzle/issues/385. + This has been fixed in Guzzle 4/5. +2. Root XML attributes not serialized in a service description: https://github.com/guzzle/guzzle3/issues/5. + This has been fixed in Guzzle 4/5. +3. Accept-Encoding not preserved when following redirect: https://github.com/guzzle/guzzle3/issues/9 + Fixed in Guzzle 4/5. +4. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 + Fixed in Guzzle 4/5. +5. Recursive model references with array items: https://github.com/guzzle/guzzle3/issues/13 + Fixed in Guzzle 4/5 +6. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 + Fixed in Guzzle 4/5. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/UPGRADING.md b/modules/devshop/devshop_github/vendor/guzzle/guzzle/UPGRADING.md new file mode 100644 index 000000000..f58bf1171 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/UPGRADING.md @@ -0,0 +1,537 @@ +Guzzle Upgrade Guide +==================== + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml + + + + + + Get a list of groups + + + Uses a search query to get a list of groups + + + + Create a group + + + + + Delete a group by ID + + + + + + + Update a group + + + + + + +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/build.xml b/modules/devshop/devshop_github/vendor/guzzle/guzzle/build.xml new file mode 100644 index 000000000..2aa62ba9a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/composer.json new file mode 100644 index 000000000..59424b39b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/composer.json @@ -0,0 +1,82 @@ +{ + "name": "guzzle/guzzle", + "type": "library", + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + + "require": { + "php": ">=5.3.3", + "ext-curl": "*", + "symfony/event-dispatcher": "~2.1" + }, + + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + + "scripts": { + "test": "phpunit" + }, + + "require-dev": { + "doctrine/cache": "~1.3", + "symfony/class-loader": "~2.1", + "monolog/monolog": "~1.0", + "psr/log": "~1.0", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3", + "phpunit/phpunit": "3.7.*" + }, + + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/Makefile b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/Makefile new file mode 100644 index 000000000..d92e03f95 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json new file mode 100644 index 000000000..81683026b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json @@ -0,0 +1,176 @@ +{ + "additionalProperties": true, + "name": { + "type": "string", + "description": "Name of the web service" + }, + "apiVersion": { + "type": ["string", "number"], + "description": "Version identifier that the service description is compatible with" + }, + "baseUrl": { + "type": "string", + "description": "Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the process defined in RFC 2396" + }, + "basePath": { + "type": "string", + "description": "Alias of baseUrl" + }, + "_description": { + "type": "string", + "description": "Short summary of the web service. This is actually called 'description' but this JSON schema wont validate using just description." + }, + "operations": { + "description": "Operations of the web service", + "type": "object", + "properties": { + "extends": { + "type": "string", + "description": "Extend from another operation by name. The parent operation must be defined before the child." + }, + "httpMethod": { + "type": "string", + "description": "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" + }, + "uri": { + "type": "string", + "description": "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" + }, + "summary": { + "type": "string", + "description": "Short summary of what the operation does" + }, + "class": { + "type": "string", + "description": "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand" + }, + "responseClass": { + "type": "string", + "description": "This is what is returned from the method. Can be a primitive, class name, or model name." + }, + "responseNotes": { + "type": "string", + "description": "A description of the response returned by the operation" + }, + "responseType": { + "type": "string", + "description": "The type of response that the operation creates. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default.", + "enum": [ "primitive", "class", "model", "documentation" ] + }, + "deprecated": { + "type": "boolean", + "description": "Whether or not the operation is deprecated" + }, + "errorResponses": { + "description": "Errors that could occur while executing the operation", + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "number", + "description": "HTTP response status code of the error" + }, + "reason": { + "type": "string", + "description": "Response reason phrase or description of the error" + }, + "class": { + "type": "string", + "description": "A custom exception class that would be thrown if the error is encountered" + } + } + } + }, + "data": { + "type": "object", + "additionalProperties": "true" + }, + "parameters": { + "$ref": "parameters", + "description": "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." + }, + "additionalParameters": { + "$ref": "parameters", + "description": "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." + } + } + }, + "models": { + "description": "Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP response is parsed into a Guzzle\\Service\\Resource\\Model object.", + "type": "object", + "properties": { + "$ref": "parameters", + "description": "Parameters of the model. When a model is referenced in a responseClass attribute of an operation, parameters define how a HTTP response message is parsed into a Guzzle\\Service\\Resource\\Model." + } + }, + "includes": { + "description": "Service description files to include and extend from (can be a .json, .js, or .php file)", + "type": "array", + "items": { + "type": "string", + "pattern": ".+\\.(js|json|php)$" + } + }, + "definitions": { + "parameters": { + "extends": "http://json-schema.org/schema", + "id": "parameters", + "name": { + "type": "string", + "description": "Unique name of the parameter" + }, + "type": { + "type": ["string", "array"], + "description": "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." + }, + "instanceOf": { + "type": "string", + "description": "When the type is an object, you can specify the class that the object must implement" + }, + "required": { + "type": "boolean", + "description": "Whether or not the parameter is required" + }, + "default": { + "description": "Default value to use if no value is supplied" + }, + "static": { + "type": "bool", + "description": "Set to true to specify that the parameter value cannot be changed from the default setting" + }, + "description": { + "type": "string", + "description": "Documentation of the parameter" + }, + "location": { + "type": "string", + "description": "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" + }, + "sentAs": { + "type": "string", + "description": "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." + }, + "filters": { + "type": "array", + "description": "Array of static method names to to run a parameter value through. Each value in the array must be a string containing the full class path to a static method or an array of complex filter information. You can specify static methods of classes using the full namespace class name followed by ‘::’ (e.g. FooBar::baz()). Some filters require arguments in order to properly filter a value. For complex filters, use a hash containing a ‘method’ key pointing to a static method, and an ‘args’ key containing an array of positional arguments to pass to the method. Arguments can contain keywords that are replaced when filtering a value: '@value‘ is replaced with the value being validated, '@api‘ is replaced with the Parameter object.", + "items": { + "type": ["string", { + "object": { + "properties": { + "method": { + "type": "string", + "description": "PHP function to call", + "required": true + }, + "args": { + "type": "array" + } + } + } + }] + } + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f1017f7e6028c14a9e0694c66a6cfbb2d546adf5 GIT binary patch literal 803 zcmV+;1Kj+HP)@ zosv>1reIXPlo1y){RjSw2_NWGX5O-#W+NK3tBQ_IN%bh7xZ1Yehws80|4azI;aWIJ z_%xhlcTubTO7Dbx z)F-R8gg5MzGv|t4=e_El4GCwW0m6?C;0bG4DRC^TH6-pa>y8_h*QBud6Ms>Qf{oN> z=Q($Dn|DINB{`Ea{)h&^x;i{)XQ{?Z&id71eOkRPqgl17&E%|dJIC&SCwnd zY4oUR`OVorB8A||QZjLWS~cr&OD?HEtM@^bIovNUC5An6?z`xDgJL2H2Mya@dku<1YUfi&QvS8KS8=~uOs!oaF z8OMF7-5yyh}yDkaCp7Ob8b;wv(27WLL#lglguF0fh3d(d@ zP%vrDIA~G}dL)X;YnCMSE4ZM-gfVsYTLItd3J`~_vw^k=W%C_MlG002ovPDHLkV1oLqbt3=( literal 0 HcmV?d00001 diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/homepage.css b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/homepage.css new file mode 100644 index 000000000..70c46d8d5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/homepage.css @@ -0,0 +1,122 @@ +/* Hero unit on homepage */ + +.hero-unit h1 { + font-size: 49px; + margin-bottom: 12px; +} + +.hero-unit { + padding: 40px; +} + +.hero-unit p { + font-size: 17px; +} + +.masthead img { + float: left; + margin-right: 17px; +} + +.hero-unit ul li { + margin-left: 220px; +} + +.hero-unit .buttons { + text-align: center; +} + +.jumbotron { + position: relative; + padding: 40px 0; + color: #fff; + text-shadow: 0 1px 3px rgba(0,0,0,.4), 0 0 30px rgba(0,0,0,.075); + background: #00312F; + background: -moz-linear-gradient(45deg, #002F31 0%, #335A6D 100%); + background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#00312D), color-stop(100%,#33566D)); + background: -webkit-linear-gradient(45deg, #020031 0%,#334F6D 100%); + background: -o-linear-gradient(45deg, #002D31 0%,#334D6D 100%); + background: -ms-linear-gradient(45deg, #002F31 0%,#33516D 100%); + background: linear-gradient(45deg, #020031 0%,#33516D 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#020031', endColorstr='#6d3353',GradientType=1 ); + -webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); + -moz-box-shadow: inset 0 3px 7px rgba(0,0,0,.2), inset 0 -3px 7px rgba(0,0,0,.2); + box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); +} + +.jumbotron h1 { + font-size: 80px; + font-weight: bold; + letter-spacing: -1px; + line-height: 1; +} + +.jumbotron p { + font-size: 24px; + font-weight: 300; + line-height: 1.25; + margin-bottom: 30px; +} + +.masthead { + padding: 40px 0 30px; + margin-bottom: 0; + color: #fff; + margin-top: -19px; +} + +.masthead h1 { + display: none; +} + +.masthead p { + font-size: 40px; + font-weight: 200; + line-height: 1.25; + margin: 12px 0 0 0; +} + +.masthead .btn { + padding: 19px 24px; + font-size: 24px; + font-weight: 200; + border: 0; +} + +/* Social bar on homepage */ + +.social { + padding: 2px 0; + text-align: center; + background-color: #f5f5f5; + border-top: 1px solid #fff; + border-bottom: 1px solid #ddd; + margin: 0 0 20px 0; +} + +.social ul { + margin-top: 0; +} + +.social-buttons { + margin-left: 0; + margin-bottom: 0; + padding-left: 0; + list-style: none; +} + +.social-buttons li { + display: inline-block; + padding: 5px 8px; + line-height: 1; + *display: inline; + *zoom: 1; +} + +.center-announcement { + padding: 10px; + background-color: rgb(238, 243, 255); + border-radius: 8px; + text-align: center; + margin: 24px 0; +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/logo.png b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..965a4ef4139180ea7d33594feba2394dfce6774a GIT binary patch literal 247678 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%0ARzX0ax0O>7%athq>_G8o_) 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 Q^3R@b2)1Zk0&5+MNyUSR-V=Sej)GD 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�{XQapLL3s@DiJOewb9vk!Lb} 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

    kl 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

    2-cn6gsGIE2zEilUdg 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

    Z5%j-C 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%0A=e`l9AQljxQ1J>rUJsDX<`+@ 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 literal 0 HcmV?d00001 diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.css b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.css new file mode 100644 index 000000000..4d410b12e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.css @@ -0,0 +1,41 @@ +.com { + color: #93A1A1; +} +.lit { + color: #195F91; +} +.pun, .opn, .clo { + color: #93A1A1; +} +.fun { + color: #DC322F; +} +.str, .atv { + color: #DD1144; +} +.kwd, .linenums .tag { + color: #1E347B; +} +.typ, .atn, .dec, .var { + color: teal; +} +.pln { + color: #48484C; +} +.prettyprint { + background-color: #F7F7F9; + border: 1px solid #E1E1E8; + padding: 8px; +} +.prettyprint.linenums { + box-shadow: 40px 0 0 #FBFBFC inset, 41px 0 0 #ECECF0 inset; +} +ol.linenums { + margin: 0 0 0 33px; +} +ol.linenums li { + color: #BEBEC5; + line-height: 18px; + padding-left: 12px; + text-shadow: 0 1px 0 #FFFFFF; +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.js b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.js new file mode 100644 index 000000000..eef5ad7e6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_static/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + +

    +
    + guzzle +

    Guzzle

    +

    Guzzle is a PHP HTTP client
    & framework for building RESTful web service clients.

    +

    + View Guzzle on GitHub + Read the docs +

    +
    +
    + + + +
    + +

    Introducing Guzzle

    + +

    Guzzle takes the pain out of sending HTTP requests and the redundancy out of creating web service clients. It's + a framework that includes the tools needed to create a robust web service client, including: + Service descriptions for defining the inputs and outputs of an API, resource iterators for traversing + paginated resources, batching for sending a large number of requests as efficiently as possible.

    + +
      +
    • All the power of cURL with a simple interface.
    • +
    • Persistent connections and parallel requests.
    • +
    • Streams request and response bodies
    • +
    • Service descriptions for quickly building clients.
    • +
    • Powered by the Symfony2 EventDispatcher.
    • +
    • Use all of the code or only specific components.
    • +
    • Plugins for caching, logging, OAuth, mocks, and more
    • +
    • Includes a custom node.js webserver to test your clients.
    • +
    + +
    + Guzzle is now part of Drupal 8 core and powers the official AWS SDK for PHP +
    + +

    GitHub Example

    + +
    <?php
    +require_once 'vendor/autoload.php';
    +use Guzzle\Http\Client;
    +
    +// Create a client and provide a base URL
    +$client = new Client('https://api.github.com');
    +// Create a request with basic Auth
    +$request = $client->get('/user')->setAuth('user', 'pass');
    +// Send the request and get the response
    +$response = $request->send();
    +echo $response->getBody();
    +// >>> {"type":"User", ...
    +echo $response->getHeader('Content-Length');
    +// >>> 792
    +
    + +

    Twitter Example

    +
    <?php
    +// Create a client to work with the Twitter API
    +$client = new Client('https://api.twitter.com/{version}', array(
    +    'version' => '1.1'
    +));
    +
    +// Sign all requests with the OauthPlugin
    +$client->addSubscriber(new Guzzle\Plugin\Oauth\OauthPlugin(array(
    +    'consumer_key'  => '***',
    +    'consumer_secret' => '***',
    +    'token'       => '***',
    +    'token_secret'  => '***'
    +)));
    +
    +echo $client->get('statuses/user_timeline.json')->send()->getBody();
    +// >>> {"public_gists":6,"type":"User" ...
    +
    +// Create a tweet using POST
    +$request = $client->post('statuses/update.json', null, array(
    +    'status' => 'Tweeted with Guzzle, http://guzzlephp.org'
    +));
    +
    +// Send the request and parse the JSON response into an array
    +$data = $request->send()->json();
    +echo $data['text'];
    +// >>> Tweeted with Guzzle, http://t.co/kngJMfRk
    +
    +
    + + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/leftbar.html b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/leftbar.html new file mode 100644 index 000000000..e69de29bb diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/nav_links.html b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/nav_links.html new file mode 100644 index 000000000..d4f2165bb --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/_templates/nav_links.html @@ -0,0 +1,5 @@ +
  • Docs
  • +
  • API
  • +
  • GitHub
  • +
  • Forum
  • +
  • IRC
  • diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/batching/batching.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/batching/batching.rst new file mode 100644 index 000000000..57f04d80f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/batching/batching.rst @@ -0,0 +1,183 @@ +======== +Batching +======== + +Guzzle provides a fairly generic and very customizable batching framework that allows developers to efficiently +transfer requests in parallel. + +Sending requests and commands in parallel +----------------------------------------- + +You can send HTTP requests in parallel by passing an array of ``Guzzle\Http\Message\RequestInterface`` objects to +``Guzzle\Http\Client::send()``: + +.. code-block:: php + + $responses = $client->send(array( + $client->get('http://www.example.com/foo'), + $client->get('http://www.example.com/baz') + $client->get('http://www.example.com/bar') + )); + +You can send commands in parallel by passing an array of ``Guzzle\Service\Command\CommandInterface`` objects +``Guzzle\Service\Client::execute()``: + +.. code-block:: php + + $commands = $client->execute(array( + $client->getCommand('foo'), + $client->getCommand('baz'), + $client->getCommand('bar') + )); + +These approaches work well for most use-cases. When you need more control over the requests that are sent in +parallel or you need to send a large number of requests, you need to use the functionality provided in the +``Guzzle\Batch`` namespace. + +Batching overview +----------------- + +The batch object, ``Guzzle\Batch\Batch``, is a queue. You add requests to the queue until you are ready to transfer +all of the requests. In order to efficiently transfer the items in the queue, the batch object delegates the +responsibility of dividing the queue into manageable parts to a divisor (``Guzzle\Batch\BatchDivisorInterface``). +The batch object then iterates over each array of items created by the divisor and sends them to the batch object's +``Guzzle\Batch\BatchTransferInterface``. + +.. code-block:: php + + use Guzzle\Batch\Batch; + use Guzzle\Http\BatchRequestTransfer; + + // BatchRequestTransfer acts as both the divisor and transfer strategy + $transferStrategy = new BatchRequestTransfer(10); + $divisorStrategy = $transferStrategy; + + $batch = new Batch($transferStrategy, $divisorStrategy); + + // Add some requests to the batch queue + $batch->add($request1) + ->add($request2) + ->add($request3); + + // Flush the queue and retrieve the flushed items + $arrayOfTransferredRequests = $batch->flush(); + +.. note:: + + You might find that your transfer strategy will need to act as both the divisor and transfer strategy. + +Using the BatchBuilder +---------------------- + +The ``Guzzle\Batch\BatchBuilder`` makes it easier to create batch objects. The batch builder also provides an easier +way to add additional behaviors to your batch object. + +Transferring requests +~~~~~~~~~~~~~~~~~~~~~ + +The ``Guzzle\Http\BatchRequestTransfer`` class efficiently transfers HTTP requests in parallel by grouping batches of +requests by the curl_multi handle that is used to transfer the requests. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->build(); + +Transferring commands +~~~~~~~~~~~~~~~~~~~~~ + +The ``Guzzle\Service\Command\BatchCommandTransfer`` class efficiently transfers service commands by grouping commands +by the client that is used to transfer them. You can add commands to a batch object that are transferred by different +clients, and the batch will handle the rest. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferCommands(10) + ->build(); + + $batch->add($client->getCommand('foo')) + ->add($client->getCommand('baz')) + ->add($client->getCommand('bar')); + + $commands = $batch->flush(); + +Batch behaviors +--------------- + +You can add various behaviors to your batch that allow for more customizable transfers. + +Automatically flushing a queue +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\FlushingBatch`` decorator when you want to pump a large number of items into a batch queue and +have the queue automatically flush when the size of the queue reaches a certain threshold. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->autoFlushAt(10) + ->build(); + +Batch builder method: ``autoFlushAt($threshold)`` + +Notifying on flush +~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\NotifyingBatch`` decorator if you want a function to be notified each time the batch queue is +flushed. This is useful when paired with the flushing batch decorator. Pass a callable to the ``notify()`` method of +a batch builder to use this decorator with the builder. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->autoFlushAt(10) + ->notify(function (array $transferredItems) { + echo 'Transferred ' . count($transferredItems) . "items\n"; + }) + ->build(); + +Batch builder method:: ``notify(callable $callback)`` + +Keeping a history +~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\HistoryBatch`` decorator if you want to maintain a history of all the items transferred with +the batch queue. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->keepHistory() + ->build(); + +After transferring items, you can use the ``getHistory()`` of a batch to retrieve an array of transferred items. Be +sure to periodically clear the history using ``clearHistory()``. + +Batch builder method: ``keepHistory()`` + +Exception buffering +~~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\ExceptionBufferingBatch`` decorator to buffer exceptions during a transfer so that you can +transfer as many items as possible then deal with the errored batches after the transfer completes. After transfer, +use the ``getExceptions()`` method of a batch to retrieve an array of +``Guzzle\Batch\Exception\BatchTransferException`` objects. You can use these exceptions to attempt to retry the +failed batches. Be sure to clear the buffered exceptions when you are done with them by using the +``clearExceptions()`` method. + +Batch builder method: ``bufferExceptions()`` diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/conf.py b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/conf.py new file mode 100644 index 000000000..92bc46bb5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/conf.py @@ -0,0 +1,94 @@ +import sys, os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +# -- General configuration ----------------------------------------------------- + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' + +project = u'Guzzle' +copyright = u'2012, Michael Dowling' +version = '3.0.0' +release = '3.0.0' + +exclude_patterns = ['_build'] + +# -- Options for HTML output --------------------------------------------------- + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "Guzzle documentation" +html_short_title = "Guzzle" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': ['localtoc.html', 'leftbar.html', 'searchbox.html'] +} + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Guzzledoc' + +# -- Guzzle Sphinx theme setup ------------------------------------------------ + +sys.path.insert(0, '/Users/dowling/projects/guzzle_sphinx_theme') + +import guzzle_sphinx_theme +html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' +html_theme_path = guzzle_sphinx_theme.html_theme_path() +html_theme = 'guzzle_sphinx_theme' + +# Guzzle theme options (see theme.conf for more information) +html_theme_options = { + "index_template": "index.html", + "project_nav_name": "Guzzle", + "github_user": "guzzle", + "github_repo": "guzzle", + "disqus_comments_shortname": "guzzle", + "google_analytics_account": "UA-22752917-1" +} + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = {} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Guzzle.tex', u'Guzzle Documentation', + u'Michael Dowling', 'manual'), +] + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'guzzle', u'Guzzle Documentation', + [u'Michael Dowling'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Guzzle', u'Guzzle Documentation', + u'Michael Dowling', 'Guzzle', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/docs.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/docs.rst new file mode 100644 index 000000000..cf87908bd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/docs.rst @@ -0,0 +1,73 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services + +==================== +Guzzle Documentation +==================== + +Getting started +--------------- + +.. toctree:: + :maxdepth: 1 + + getting-started/overview + getting-started/installation + getting-started/faq + +The HTTP client +--------------- + +.. toctree:: + :maxdepth: 2 + + http-client/client + http-client/request + http-client/response + http-client/entity-bodies + http-client/http-redirects + http-client/uri-templates + +Plugins +------- + +.. toctree:: + :maxdepth: 1 + + plugins/plugins-overview + plugins/creating-plugins + plugins/async-plugin + plugins/backoff-plugin + plugins/cache-plugin + plugins/cookie-plugin + plugins/curl-auth-plugin + plugins/history-plugin + plugins/log-plugin + plugins/md5-validator-plugin + plugins/mock-plugin + plugins/oauth-plugin + +The web service client +---------------------- + +.. toctree:: + :maxdepth: 1 + + webservice-client/webservice-client + webservice-client/using-the-service-builder + webservice-client/guzzle-service-descriptions + batching/batching + iterators/resource-iterators + iterators/guzzle-iterators + +Testing +------- + +.. toctree:: + :maxdepth: 2 + + testing/unit-testing + +API Docs +-------- + +`Read the API docs `_ diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/faq.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/faq.rst new file mode 100644 index 000000000..a0a3fdbb6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/faq.rst @@ -0,0 +1,29 @@ +=== +FAQ +=== + +What should I do if I get this error: Fatal error: Maximum function nesting level of '100' reached, aborting! +------------------------------------------------------------------------------------------------------------- + +You could run into this error if you have the XDebug extension installed and you execute a lot of requests in +callbacks. This error message comes specifically from the XDebug extension. PHP itself does not have a function +nesting limit. Change this setting in your php.ini to increase the limit:: + + xdebug.max_nesting_level = 1000 + +[`source `_] + +How can I speed up my client? +----------------------------- + +There are several things you can do to speed up your client: + +1. Utilize a C based HTTP message parser (e.g. ``Guzzle\Parser\Message\PeclHttpMessageParser``) +2. Disable operation validation by setting the ``command.disable_validation`` option to true on a command + +Why am I getting a 417 error response? +-------------------------------------- + +This can occur for a number of reasons, but if you are sending PUT, POST, or PATCH requests with an +``Expect: 100-Continue`` header, a server that does not support this header will return a 417 response. You can work +around this by calling ``$request->removeHeader('Expect');`` after setting the entity body of a request. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/installation.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/installation.rst new file mode 100644 index 000000000..77d400131 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/installation.rst @@ -0,0 +1,154 @@ +============ +Installation +============ + +Requirements +------------ + +#. PHP 5.3.3+ compiled with the cURL extension +#. A recent version of cURL 7.16.2+ compiled with OpenSSL and zlib + +Installing Guzzle +----------------- + +Composer +~~~~~~~~ + +The recommended way to install Guzzle is with `Composer `_. Composer is a dependency +management tool for PHP that allows you to declare the dependencies your project needs and installs them into your +project. + +.. code-block:: bash + + # Install Composer + curl -sS https://getcomposer.org/installer | php + + # Add Guzzle as a dependency + php composer.phar require guzzle/guzzle:~3.9 + +After installing, you need to require Composer's autoloader: + +.. code-block:: php + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and other best-practices for defining +dependencies at `getcomposer.org `_. + +Using only specific parts of Guzzle +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While you can always just rely on ``guzzle/guzzle``, Guzzle provides several smaller parts of Guzzle as individual +packages available through Composer. + ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| Package name | Description | ++===============================================================================================+==========================================+ +| `guzzle/common `_ | Provides ``Guzzle\Common`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/http `_ | Provides ``Guzzle\Http`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/parser `_ | Provides ``Guzzle\Parser`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/batch `_ | Provides ``Guzzle\Batch`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/cache `_ | Provides ``Guzzle\Cache`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/inflection `_ | Provides ``Guzzle\Inflection`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/iterator `_ | Provides ``Guzzle\Iterator`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/log `_ | Provides ``Guzzle\Log`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin `_ | Provides ``Guzzle\Plugin`` (all plugins) | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-async `_ | Provides ``Guzzle\Plugin\Async`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-backoff `_ | Provides ``Guzzle\Plugin\BackoffPlugin`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-cache `_ | Provides ``Guzzle\Plugin\Cache`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-cookie `_ | Provides ``Guzzle\Plugin\Cookie`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-error-response `_ | Provides ``Guzzle\Plugin\ErrorResponse`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-history `_ | Provides ``Guzzle\Plugin\History`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-log `_ | Provides ``Guzzle\Plugin\Log`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-md5 `_ | Provides ``Guzzle\Plugin\Md5`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-mock `_ | Provides ``Guzzle\Plugin\Mock`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-oauth `_ | Provides ``Guzzle\Plugin\Oauth`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/service `_ | Provides ``Guzzle\Service`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/stream `_ | Provides ``Guzzle\Stream`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ + +Bleeding edge +^^^^^^^^^^^^^ + +During your development, you can keep up with the latest changes on the master branch by setting the version +requirement for Guzzle to ``dev-master``. + +.. code-block:: js + + { + "require": { + "guzzle/guzzle": "dev-master" + } + } + +PEAR +~~~~ + +Guzzle can be installed through PEAR: + +.. code-block:: bash + + pear channel-discover guzzlephp.org/pear + pear install guzzle/guzzle + +You can install a specific version of Guzzle by providing a version number suffix: + +.. code-block:: bash + + pear install guzzle/guzzle-3.9.0 + +Contributing to Guzzle +---------------------- + +In order to contribute, you'll need to checkout the source from GitHub and install Guzzle's dependencies using +Composer: + +.. code-block:: bash + + git clone https://github.com/guzzle/guzzle.git + cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev + +Guzzle is unit tested with PHPUnit. You will need to create your own phpunit.xml file in order to run the unit tests +(or just copy phpunit.xml.dist to phpunit.xml). Run the tests using the vendored PHPUnit binary: + +.. code-block:: bash + + vendor/bin/phpunit + +You'll need to install node.js v0.5.0 or newer in order to test the cURL implementation. + +Framework integrations +---------------------- + +Using Guzzle with Symfony +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Bundles are available on GitHub: + +- `DdeboerGuzzleBundle `_ for Guzzle 2 +- `MisdGuzzleBundle `_ for Guzzle 3 + +Using Guzzle with Silex +~~~~~~~~~~~~~~~~~~~~~~~ + +A `Guzzle Silex service provider `_ is available on GitHub. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/overview.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/overview.rst new file mode 100644 index 000000000..505b40978 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/getting-started/overview.rst @@ -0,0 +1,85 @@ +================= +Welcome to Guzzle +================= + +What is Guzzle? +~~~~~~~~~~~~~~~ + +Guzzle is a PHP HTTP client and framework for building web service clients. Guzzle takes the pain out of sending HTTP +requests and the redundancy out of creating web service clients. + +Features at a glance +-------------------- + +- All the power of cURL with a simple interface. +- Persistent connections and parallel requests. +- Streams request and response bodies +- Service descriptions for quickly building clients. +- Powered by the Symfony2 EventDispatcher. +- Use all of the code or only specific components. +- Plugins for caching, logging, OAuth, mocks, and more +- Includes a custom node.js webserver to test your clients. +- Service descriptions for defining the inputs and outputs of an API +- Resource iterators for traversing paginated resources +- Batching for sending a large number of requests as efficiently as possible + +.. code-block:: php + + // Really simple using a static facade + Guzzle\Http\StaticClient::mount(); + $response = Guzzle::get('http://guzzlephp.org'); + + // More control using a client class + $client = new \Guzzle\Http\Client('http://guzzlephp.org'); + $request = $client->get('/'); + $response = $request->send(); + +License +------- + +Licensed using the `MIT license `_. + + Copyright (c) 2013 Michael Dowling + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Contributing +------------ + +Guidelines +~~~~~~~~~~ + +This is still a work in progress, but there are only a few rules: + +1. Guzzle follows PSR-0, PSR-1, and PSR-2 +2. All pull requests must include unit tests to ensure the change works as expected and to prevent future regressions + +Reporting a security vulnerability +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We want to ensure that Guzzle is a secure HTTP client library for everyone. If you've discovered a security +vulnerability in Guzzle, we appreciate your help in disclosing it to us in a +`responsible manner `_. + +Publicly disclosing a vulnerability can put the entire community at risk. If you've discovered a security concern, +please email us at security@guzzlephp.org. We'll work with you to make sure that we understand the scope of the issue, +and that we fully address your concern. We consider correspondence sent to security@guzzlephp.org our highest priority, +and work to address any issues that arise as quickly as possible. + +After a security vulnerability has been corrected, a security hotfix release will be deployed as soon as possible. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/client.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/client.rst new file mode 100644 index 000000000..723d729db --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/client.rst @@ -0,0 +1,569 @@ +====================== +The Guzzle HTTP client +====================== + +Guzzle gives PHP developers complete control over HTTP requests while utilizing HTTP/1.1 best practices. Guzzle's HTTP +functionality is a robust framework built on top of the `PHP libcurl bindings `_. + +The three main parts of the Guzzle HTTP client are: + ++--------------+-------------------------------------------------------------------------------------------------------+ +| Clients | ``Guzzle\Http\Client`` (creates and sends requests, associates a response with a request) | ++--------------+-------------------------------------------------------------------------------------------------------+ +| Requests | ``Guzzle\Http\Message\Request`` (requests with no body), | +| | ``Guzzle\Http\Message\EntityEnclosingRequest`` (requests with a body) | ++--------------+-------------------------------------------------------------------------------------------------------+ +| Responses | ``Guzzle\Http\Message\Response`` | ++--------------+-------------------------------------------------------------------------------------------------------+ + +Creating a Client +----------------- + +Clients create requests, send requests, and set responses on a request object. When instantiating a client object, +you can pass an optional "base URL" and optional array of configuration options. A base URL is a +:doc:`URI template ` that contains the URL of a remote server. When creating requests with a relative +URL, the base URL of a client will be merged into the request's URL. + +.. code-block:: php + + use Guzzle\Http\Client; + + // Create a client and provide a base URL + $client = new Client('https://api.github.com'); + + $request = $client->get('/user'); + $request->setAuth('user', 'pass'); + echo $request->getUrl(); + // >>> https://api.github.com/user + + // You must send a request in order for the transfer to occur + $response = $request->send(); + + echo $response->getBody(); + // >>> {"type":"User", ... + + echo $response->getHeader('Content-Length'); + // >>> 792 + + $data = $response->json(); + echo $data['type']; + // >>> User + +Base URLs +~~~~~~~~~ + +Notice that the URL provided to the client's ``get()`` method is relative. Relative URLs will always merge into the +base URL of the client. There are a few rules that control how the URLs are merged. + +.. tip:: + + Guzzle follows `RFC 3986 `_ when merging base URLs and + relative URLs. + +In the above example, we passed ``/user`` to the ``get()`` method of the client. This is a relative URL, so it will +merge into the base URL of the client-- resulting in the derived URL of ``https://api.github.com/users``. + +``/user`` is a relative URL but uses an absolute path because it contains the leading slash. Absolute paths will +overwrite any existing path of the base URL. If an absolute path is provided (e.g. ``/path/to/something``), then the +path specified in the base URL of the client will be replaced with the absolute path, and the query string provided +by the relative URL will replace the query string of the base URL. + +Omitting the leading slash and using relative paths will add to the path of the base URL of the client. So using a +client base URL of ``https://api.twitter.com/v1.1`` and creating a GET request with ``statuses/user_timeline.json`` +will result in a URL of ``https://api.twitter.com/v1.1/statuses/user_timeline.json``. If a relative path and a query +string are provided, then the relative path will be appended to the base URL path, and the query string provided will +be merged into the query string of the base URL. + +If an absolute URL is provided (e.g. ``http://httpbin.org/ip``), then the request will completely use the absolute URL +as-is without merging in any of the URL parts specified in the base URL. + +Configuration options +~~~~~~~~~~~~~~~~~~~~~ + +The second argument of the client's constructor is an array of configuration data. This can include URI template data +or special options that alter the client's behavior: + ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``request.options`` | Associative array of :ref:`Request options ` to apply to every | +| | request created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``redirect.disable`` | Disable HTTP redirects for every request created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``curl.options`` | Associative array of cURL options to apply to every request created by the client. | +| | if either the key or value of an entry in the array is a string, Guzzle will | +| | attempt to find a matching defined cURL constant automatically (e.g. | +| | "CURLOPT_PROXY" will be converted to the constant ``CURLOPT_PROXY``). | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``ssl.certificate_authority`` | Set to true to use the Guzzle bundled SSL certificate bundle (this is used by | +| | default, 'system' to use the bundle on your system, a string pointing to a file to | +| | use a specific certificate file, a string pointing to a directory to use multiple | +| | certificates, or ``false`` to disable SSL validation (not recommended). | +| | | +| | When using Guzzle inside of a phar file, the bundled SSL certificate will be | +| | extracted to your system's temp folder, and each time a client is created an MD5 | +| | check will be performed to ensure the integrity of the certificate. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``command.params`` | When using a ``Guzzle\Service\Client`` object, this is an associative array of | +| | default options to set on each command created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ + +Here's an example showing how to set various configuration options, including default headers to send with each request, +default query string parameters to add to each request, a default auth scheme for each request, and a proxy to use for +each request. Values can be injected into the client's base URL using variables from the configuration array. + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client('https://api.twitter.com/{version}', array( + 'version' => 'v1.1', + 'request.options' => array( + 'headers' => array('Foo' => 'Bar'), + 'query' => array('testing' => '123'), + 'auth' => array('username', 'password', 'Basic|Digest|NTLM|Any'), + 'proxy' => 'tcp://localhost:80' + ) + )); + +Setting a custom User-Agent +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default Guzzle User-Agent header is ``Guzzle/ curl/ PHP/``. You can +customize the User-Agent header of a client by calling the ``setUserAgent()`` method of a Client object. + +.. code-block:: php + + // Completely override the default User-Agent + $client->setUserAgent('Test/123'); + + // Prepend a string to the default User-Agent + $client->setUserAgent('Test/123', true); + +Creating requests with a client +------------------------------- + +A Client object exposes several methods used to create Request objects: + +* Create a custom HTTP request: ``$client->createRequest($method, $uri, array $headers, $body, $options)`` +* Create a GET request: ``$client->get($uri, array $headers, $options)`` +* Create a HEAD request: ``$client->head($uri, array $headers, $options)`` +* Create a DELETE request: ``$client->delete($uri, array $headers, $body, $options)`` +* Create a POST request: ``$client->post($uri, array $headers, $postBody, $options)`` +* Create a PUT request: ``$client->put($uri, array $headers, $body, $options)`` +* Create a PATCH request: ``$client->patch($uri, array $headers, $body, $options)`` + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client('http://baseurl.com/api/v1'); + + // Create a GET request using Relative to base URL + // URL of the request: http://baseurl.com/api/v1/path?query=123&value=abc) + $request = $client->get('path?query=123&value=abc'); + $response = $request->send(); + + // Create HEAD request using a relative URL with an absolute path + // URL of the request: http://baseurl.com/path?query=123&value=abc + $request = $client->head('/path?query=123&value=abc'); + $response = $request->send(); + + // Create a DELETE request using an absolute URL + $request = $client->delete('http://www.example.com/path?query=123&value=abc'); + $response = $request->send(); + + // Create a PUT request using the contents of a PHP stream as the body + // Specify custom HTTP headers + $request = $client->put('http://www.example.com/upload', array( + 'X-Header' => 'My Header' + ), fopen('http://www.test.com/', 'r')); + $response = $request->send(); + + // Create a POST request and add the POST files manually + $request = $client->post('http://localhost:8983/solr/update') + ->addPostFiles(array('file' => '/path/to/documents.xml')); + $response = $request->send(); + + // Check if a resource supports the DELETE method + $supportsDelete = $client->options('/path')->send()->isMethodAllowed('DELETE'); + $response = $request->send(); + +Client objects create Request objects using a request factory (``Guzzle\Http\Message\RequestFactoryInterface``). +You can inject a custom request factory into the Client using ``$client->setRequestFactory()``, but you can typically +rely on a Client's default request factory. + +Static clients +-------------- + +You can use Guzzle's static client facade to more easily send simple HTTP requests. + +.. code-block:: php + + // Mount the client so that you can access it at \Guzzle + Guzzle\Http\StaticClient::mount(); + $response = Guzzle::get('http://guzzlephp.org'); + +Each request method of the static client (e.g. ``get()``, ``post()`, ``put()``, etc) accepts an associative array of request +options to apply to the request. + +.. code-block:: php + + $response = Guzzle::post('http://test.com', array( + 'headers' => array('X-Foo' => 'Bar'), + 'body' => array('Test' => '123'), + 'timeout' => 10 + )); + +.. _request-options: + +Request options +--------------- + +Request options can be specified when creating a request or in the ``request.options`` parameter of a client. These +options can control various aspects of a request including: headers to send, query string data, where the response +should be downloaded, proxies, auth, etc. + +headers +~~~~~~~ + +Associative array of headers to apply to the request. When specified in the ``$options`` argument of a client creational +method (e.g. ``get()``, ``post()``, etc), the headers in the ``$options`` array will overwrite headers specified in the +``$headers`` array. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'headers' => array('X-Foo' => 'Bar') + )); + +Headers can be specified on a client to add default headers to every request sent by a client. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + + // Set a single header using path syntax + $client->setDefaultOption('headers/X-Foo', 'Bar'); + + // Set all headers + $client->setDefaultOption('headers', array('X-Foo' => 'Bar')); + +.. note:: + + In addition to setting request options when creating requests or using the ``setDefaultOption()`` method, any + default client request option can be set using a client's config object: + + .. code-block:: php + + $client->getConfig()->setPath('request.options/headers/X-Foo', 'Bar'); + +query +~~~~~ + +Associative array of query string parameters to the request. When specified in the ``$options`` argument of a client +creational method, the query string parameters in the ``$options`` array will overwrite query string parameters +specified in the `$url`. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'query' => array('abc' => '123') + )); + +Query string parameters can be specified on a client to add default query string parameters to every request sent by a +client. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + + // Set a single query string parameter using path syntax + $client->setDefaultOption('query/abc', '123'); + + // Set an array of default query string parameters + $client->setDefaultOption('query', array('abc' => '123')); + +body +~~~~ + +Sets the body of a request. The value supplied to the body option can be a ``Guzzle\Http\EntityBodyInterface``, string, +fopen resource, or array when sending POST requests. When a ``body`` request option is supplied, the option value will +overwrite the ``$body`` argument of a client creational method. + +auth +~~~~ + +Specifies and array of HTTP authorization parameters parameters to use with the request. The array must contain the +username in index [0], the password in index [1], and can optionally contain the authentication type in index [2]. +The available authentication types are: "Basic" (default), "Digest", "NTLM", or "Any". + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'auth' => array('username', 'password', 'Digest') + )); + + // You can add auth headers to every request of a client + $client->setDefaultOption('auth', array('username', 'password', 'Digest')); + +cookies +~~~~~~~ + +Specifies an associative array of cookies to add to the request. + +allow_redirects +~~~~~~~~~~~~~~~ + +Specifies whether or not the request should follow redirects. Requests will follow redirects by default. Set +``allow_redirects`` to ``false`` to disable redirects. + +save_to +~~~~~~~ + +The ``save_to`` option specifies where the body of a response is downloaded. You can pass the path to a file, an fopen +resource, or a ``Guzzle\Http\EntityBodyInterface`` object. + +See :ref:`Changing where a response is downloaded ` for more information on setting the +`save_to` option. + +events +~~~~~~ + +The `events` option makes it easy to attach listeners to the various events emitted by a request object. The `events` +options must be an associative array mapping an event name to a Closure or array the contains a Closure and the +priority of the event. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'events' => array( + 'request.before_send' => function (\Guzzle\Common\Event $e) { + echo 'About to send ' . $e['request']; + } + ) + )); + + // Using the static client: + Guzzle::get($url, array( + 'events' => array( + 'request.before_send' => function (\Guzzle\Common\Event $e) { + echo 'About to send ' . $e['request']; + } + ) + )); + +plugins +~~~~~~~ + +The `plugins` options makes it easy to attach an array of plugins to a request. + +.. code-block:: php + + // Using the static client: + Guzzle::get($url, array( + 'plugins' => array( + new Guzzle\Plugin\Cache\CachePlugin(), + new Guzzle\Plugin\Cookie\CookiePlugin() + ) + )); + +exceptions +~~~~~~~~~~ + +The `exceptions` option can be used to disable throwing exceptions for unsuccessful HTTP response codes +(e.g. 404, 500, etc). Set `exceptions` to false to not throw exceptions. + +params +~~~~~~ + +The `params` options can be used to specify an associative array of data parameters to add to a request. Note that +these are not query string parameters. + +timeout / connect_timeout +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify the maximum number of seconds to allow for an entire transfer to take place before timing out using +the `timeout` request option. You can specify the maximum number of seconds to wait while trying to connect using the +`connect_timeout` request option. Set either of these options to 0 to wait indefinitely. + +.. code-block:: php + + $request = $client->get('http://www.example.com', array(), array( + 'timeout' => 20, + 'connect_timeout' => 1.5 + )); + +verify +~~~~~~ + +Set to true to enable SSL certificate validation (the default), false to disable SSL certificate validation, or supply +the path to a CA bundle to enable verification using a custom certificate. + +cert +~~~~ + +The `cert` option lets you specify a PEM formatted SSL client certificate to use with servers that require one. If the +certificate requires a password, provide an array with the password as the second item. + +This would typically be used in conjunction with the `ssl_key` option. + +.. code-block:: php + + $request = $client->get('https://www.example.com', array(), array( + 'cert' => '/etc/pki/client_certificate.pem' + ) + + $request = $client->get('https://www.example.com', array(), array( + 'cert' => array('/etc/pki/client_certificate.pem', 's3cr3tp455w0rd') + ) + +ssl_key +~~~~~~~ + +The `ssl_key` option lets you specify a file containing your PEM formatted private key, optionally protected by a password. +Note: your password is sensitive, keep the PHP script containing it safe. + +This would typically be used in conjunction with the `cert` option. + +.. code-block:: php + + $request = $client->get('https://www.example.com', array(), array( + 'ssl_key' => '/etc/pki/private_key.pem' + ) + + $request = $client->get('https://www.example.com', array(), array( + 'ssl_key' => array('/etc/pki/private_key.pem', 's3cr3tp455w0rd') + ) + +proxy +~~~~~ + +The `proxy` option is used to specify an HTTP proxy (e.g. `http://username:password@192.168.16.1:10`). + +debug +~~~~~ + +The `debug` option is used to show verbose cURL output for a transfer. + +stream +~~~~~~ + +When using a static client, you can set the `stream` option to true to return a `Guzzle\Stream\Stream` object that can +be used to pull data from a stream as needed (rather than have cURL download the entire contents of a response to a +stream all at once). + +.. code-block:: php + + $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); + while (!$stream->feof()) { + echo $stream->readLine(); + } + +Sending requests +---------------- + +Requests can be sent by calling the ``send()`` method of a Request object, but you can also send requests using the +``send()`` method of a Client. + +.. code-block:: php + + $request = $client->get('http://www.amazon.com'); + $response = $client->send($request); + +Sending requests in parallel +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Client's ``send()`` method accept a single ``Guzzle\Http\Message\RequestInterface`` object or an array of +RequestInterface objects. When an array is specified, the requests will be sent in parallel. + +Sending many HTTP requests serially (one at a time) can cause an unnecessary delay in a script's execution. Each +request must complete before a subsequent request can be sent. By sending requests in parallel, a pool of HTTP +requests can complete at the speed of the slowest request in the pool, significantly reducing the amount of time +needed to execute multiple HTTP requests. Guzzle provides a wrapper for the curl_multi functions in PHP. + +Here's an example of sending three requests in parallel using a client object: + +.. code-block:: php + + use Guzzle\Common\Exception\MultiTransferException; + + try { + $responses = $client->send(array( + $client->get('http://www.google.com/'), + $client->head('http://www.google.com/'), + $client->get('https://www.github.com/') + )); + } catch (MultiTransferException $e) { + + echo "The following exceptions were encountered:\n"; + foreach ($e as $exception) { + echo $exception->getMessage() . "\n"; + } + + echo "The following requests failed:\n"; + foreach ($e->getFailedRequests() as $request) { + echo $request . "\n\n"; + } + + echo "The following requests succeeded:\n"; + foreach ($e->getSuccessfulRequests() as $request) { + echo $request . "\n\n"; + } + } + +If the requests succeed, an array of ``Guzzle\Http\Message\Response`` objects are returned. A single request failure +will not cause the entire pool of requests to fail. Any exceptions thrown while transferring a pool of requests will +be aggregated into a ``Guzzle\Common\Exception\MultiTransferException`` exception. + +Plugins and events +------------------ + +Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications +powered by the +`Symfony2 Event Dispatcher component `_. Any +event listener or subscriber attached to a Client object will automatically be attached to each request created by the +client. + +Using the same cookie session for each request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Attach a ``Guzzle\Plugin\Cookie\CookiePlugin`` to a client which will in turn add support for cookies to every request +created by a client, and each request will use the same cookie session: + +.. code-block:: php + + use Guzzle\Plugin\Cookie\CookiePlugin; + use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; + + // Create a new cookie plugin + $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); + + // Add the cookie plugin to the client + $client->addSubscriber($cookiePlugin); + +.. _client-events: + +Events emitted from a client +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Guzzle\Http\Client`` object emits the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| client.create_request | Called when a client creates a request | * client: The client | +| | | * request: The created request | ++------------------------------+--------------------------------------------+------------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Http\Client; + + $client = new Client(); + + // Add a listener that will echo out requests as they are created + $client->getEventDispatcher()->addListener('client.create_request', function (Event $e) { + echo 'Client object: ' . spl_object_hash($e['client']) . "\n"; + echo "Request object: {$e['request']}\n"; + }); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst new file mode 100644 index 000000000..823b0c022 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst @@ -0,0 +1,151 @@ +=========================== +Request and response bodies +=========================== + +`Entity body `_ is the term used for the body of an HTTP +message. The entity body of requests and responses is inherently a +`PHP stream `_ in Guzzle. The body of the request can be either a string or +a PHP stream which are converted into a ``Guzzle\Http\EntityBody`` object using its factory method. When using a +string, the entity body is stored in a `temp PHP stream `_. The use of +temp PHP streams helps to protect your application from running out of memory when sending or receiving large entity +bodies in your messages. When more than 2MB of data is stored in a temp stream, it automatically stores the data on +disk rather than in memory. + +EntityBody objects provide a great deal of functionality: compression, decompression, calculate the Content-MD5, +calculate the Content-Length (when the resource is repeatable), guessing the Content-Type, and more. Guzzle doesn't +need to load an entire entity body into a string when sending or retrieving data; entity bodies are streamed when +being uploaded and downloaded. + +Here's an example of gzip compressing a text file then sending the file to a URL: + +.. code-block:: php + + use Guzzle\Http\EntityBody; + + $body = EntityBody::factory(fopen('/path/to/file.txt', 'r+')); + echo $body->read(1024); + $body->seek(0, SEEK_END); + $body->write('foo'); + echo $body->ftell(); + $body->rewind(); + + // Send a request using the body + $response = $client->put('http://localhost:8080/uploads', null, $body)->send(); + +The body of the request can be specified in the ``Client::put()`` or ``Client::post()`` method, or, you can specify +the body of the request by calling the ``setBody()`` method of any +``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object. + +Compression +----------- + +You can compress the contents of an EntityBody object using the ``compress()`` method. The compress method accepts a +filter that must match to one of the supported +`PHP stream filters `_ on your system (e.g. `zlib.deflate`, +``bzip2.compress``, etc). Compressing an entity body will stream the entire entity body through a stream compression +filter into a temporary PHP stream. You can uncompress an entity body using the ``uncompress()`` method and passing +the PHP stream filter to use when decompressing the stream (e.g. ``zlib.inflate``). + +.. code-block:: php + + use Guzzle\Http\EntityBody; + + $body = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + echo $body->getSize(); + // >>> 1048576 + + // Compress using the default zlib.deflate filter + $body->compress(); + echo $body->getSize(); + // >>> 314572 + + // Decompress the stream + $body->uncompress(); + echo $body->getSize(); + // >>> 1048576 + +Decorators +---------- + +Guzzle provides several EntityBody decorators that can be used to add functionality to an EntityBody at runtime. + +IoEmittingEntityBody +~~~~~~~~~~~~~~~~~~~~ + +This decorator will emit events when data is read from a stream or written to a stream. Add an event subscriber to the +entity body's ``body.read`` or ``body.write`` methods to receive notifications when data data is transferred. + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Http\EntityBody; + use Guzzle\Http\IoEmittingEntityBody; + + $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + $body = new IoEmittingEntityBody($original); + + // Listen for read events + $body->getEventDispatcher()->addListener('body.read', function (Event $e) { + // Grab data from the event + $entityBody = $e['body']; + // Amount of data retrieved from the body + $lengthOfData = $e['length']; + // The actual data that was read + $data = $e['read']; + }); + + // Listen for write events + $body->getEventDispatcher()->addListener('body.write', function (Event $e) { + // Grab data from the event + $entityBody = $e['body']; + // The data that was written + $data = $e['write']; + // The actual amount of data that was written + $data = $e['read']; + }); + +ReadLimitEntityBody +~~~~~~~~~~~~~~~~~~~ + +The ReadLimitEntityBody decorator can be used to transfer a subset or slice of an existing EntityBody object. This can +be useful for breaking a large file into smaller pieces to be sent in chunks (e.g. Amazon S3's multipart upload API). + +.. code-block:: php + + use Guzzle\Http\EntityBody; + use Guzzle\Http\ReadLimitEntityBody; + + $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + echo $original->getSize(); + // >>> 1048576 + + // Limit the size of the body to 1024 bytes and start reading from byte 2048 + $body = new ReadLimitEntityBody($original, 1024, 2048); + echo $body->getSize(); + // >>> 1024 + echo $body->ftell(); + // >>> 0 + +CachingEntityBody +~~~~~~~~~~~~~~~~~ + +The CachingEntityBody decorator is used to allow seeking over previously read bytes on non-seekable read streams. This +can be useful when transferring a non-seekable entity body fails due to needing to rewind the stream (for example, +resulting from a redirect). Data that is read from the remote stream will be buffered in a PHP temp stream so that +previously read bytes are cached first in memory, then on disk. + +.. code-block:: php + + use Guzzle\Http\EntityBody; + use Guzzle\Http\CachingEntityBody; + + $original = EntityBody::factory(fopen('http://www.google.com', 'r')); + $body = new CachingEntityBody($original); + + $body->read(1024); + echo $body->ftell(); + // >>> 1024 + + $body->seek(0); + echo $body->ftell(); + // >>> 0 diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst new file mode 100644 index 000000000..32ba26891 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst @@ -0,0 +1,99 @@ +============== +HTTP redirects +============== + +By default, Guzzle will automatically follow redirects using the non-RFC compliant implementation used by most web +browsers. This means that redirects for POST requests are followed by a GET request. You can force RFC compliance by +enabling the strict mode on a request's parameter object: + +.. code-block:: php + + // Set per request + $request = $client->post(); + $request->getParams()->set('redirect.strict', true); + + // You can set globally on a client so all requests use strict redirects + $client->getConfig()->set('request.params', array( + 'redirect.strict' => true + )); + +By default, Guzzle will redirect up to 5 times before throwing a ``Guzzle\Http\Exception\TooManyRedirectsException``. +You can raise or lower this value using the ``redirect.max`` parameter of a request object: + +.. code-block:: php + + $request->getParams()->set('redirect.max', 2); + +Redirect history +---------------- + +You can get the number of redirects of a request using the resulting response object's ``getRedirectCount()`` method. +Similar to cURL's ``effective_url`` property, Guzzle provides the effective URL, or the last redirect URL that returned +the request, in a response's ``getEffectiveUrl()`` method. + +When testing or debugging, it is often useful to see a history of redirects for a particular request. This can be +achieved using the HistoryPlugin. + +.. code-block:: php + + $request = $client->get('/'); + $history = new Guzzle\Plugin\History\HistoryPlugin(); + $request->addSubscriber($history); + $response = $request->send(); + + // Get the last redirect URL or the URL of the request that received + // this response + echo $response->getEffectiveUrl(); + + // Get the number of redirects + echo $response->getRedirectCount(); + + // Iterate over each sent request and response + foreach ($history->getAll() as $transaction) { + // Request object + echo $transaction['request']->getUrl() . "\n"; + // Response object + echo $transaction['response']->getEffectiveUrl() . "\n"; + } + + // Or, simply cast the HistoryPlugin to a string to view each request and response + echo $history; + +Disabling redirects +------------------- + +You can disable redirects on a client by passing a configuration option in the client's constructor: + +.. code-block:: php + + $client = new Client(null, array('redirect.disable' => true)); + +You can also disable redirects per request: + +.. code-block:: php + + $request = $client->get($url, array(), array('allow_redirects' => false)); + +Redirects and non-repeatable streams +------------------------------------ + +If you are redirected when sending data from a non-repeatable stream and some of the data has been read off of the +stream, then you will get a ``Guzzle\Http\Exception\CouldNotRewindStreamException``. You can get around this error by +adding a custom rewind method to the entity body object being sent in the request. + +.. code-block:: php + + $request = $client->post( + 'http://httpbin.com/redirect/2', + null, + fopen('http://httpbin.com/get', 'r') + ); + + // Add a custom function that can be used to rewind the stream + // (reopen in this example) + $request->getBody()->setRewindFunction(function ($body) { + $body->setStream(fopen('http://httpbin.com/get', 'r')); + return true; + ); + + $response = $client->send(); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/request.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/request.rst new file mode 100644 index 000000000..a8387a915 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/request.rst @@ -0,0 +1,667 @@ +===================== +Using Request objects +===================== + +HTTP request messages +--------------------- + +Request objects are all about building an HTTP message. Each part of an HTTP request message can be set individually +using methods on the request object or set in bulk using the ``setUrl()`` method. Here's the format of an HTTP request +with each part of the request referencing the method used to change it:: + + PUT(a) /path(b)?query=123(c) HTTP/1.1(d) + X-Header(e): header + Content-Length(e): 4 + + data(f) + ++-------------------------+---------------------------------------------------------------------------------+ +| a. **Method** | The request method can only be set when instantiating a request | ++-------------------------+---------------------------------------------------------------------------------+ +| b. **Path** | ``$request->setPath('/path');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| c. **Query** | ``$request->getQuery()->set('query', '123');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| d. **Protocol version** | ``$request->setProtocolVersion('1.1');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| e. **Header** | ``$request->setHeader('X-Header', 'header');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| f. **Entity Body** | ``$request->setBody('data'); // Only available with PUT, POST, PATCH, DELETE`` | ++-------------------------+---------------------------------------------------------------------------------+ + +Creating requests with a client +------------------------------- + +Client objects are responsible for creating HTTP request objects. + +GET requests +~~~~~~~~~~~~ + +`GET requests `_ are the most common form of HTTP +requests. When you visit a website in your browser, the HTML of the website is downloaded using a GET request. GET +requests are idempotent requests that are typically used to download content (an entity) identified by a request URL. + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client(); + + // Create a request that has a query string and an X-Foo header + $request = $client->get('http://www.amazon.com?a=1', array('X-Foo' => 'Bar')); + + // Send the request and get the response + $response = $request->send(); + +You can change where the body of a response is downloaded on any request using the +``$request->setResponseBody(string|EntityBodyInterface|resource)`` method of a request. You can also set the ``save_to`` +option of a request: + +.. code-block:: php + + // Send the response body to a file + $request = $client->get('http://test.com', array(), array('save_to' => '/path/to/file')); + + // Send the response body to an fopen resource + $request = $client->get('http://test.com', array(), array('save_to' => fopen('/path/to/file', 'w'))); + +HEAD requests +~~~~~~~~~~~~~ + +`HEAD requests `_ work exactly like GET requests except +that they do not actually download the response body (entity) of the response message. HEAD requests are useful for +retrieving meta information about an entity identified by a Request-URI. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + $request = $client->head('http://www.amazon.com'); + $response = $request->send(); + echo $response->getContentLength(); + // >>> Will output the Content-Length header value + +DELETE requests +~~~~~~~~~~~~~~~ + +A `DELETE method `_ requests that the origin server +delete the resource identified by the Request-URI. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + $request = $client->delete('http://example.com'); + $response = $request->send(); + +POST requests +~~~~~~~~~~~~~ + +While `POST requests `_ can be used for a number of +reasons, POST requests are often used when submitting HTML form data to a website. POST requests can include an entity +body in the HTTP request. + +POST requests in Guzzle are sent with an ``application/x-www-form-urlencoded`` Content-Type header if POST fields are +present but no files are being sent in the POST. If files are specified in the POST request, then the Content-Type +header will become ``multipart/form-data``. + +The ``post()`` method of a client object accepts four arguments: the URL, optional headers, post fields, and an array of +request options. To send files in the POST request, prepend the ``@`` symbol to the array value (just like you would if +you were using the PHP ``curl_setopt`` function). + +Here's how to create a multipart/form-data POST request containing files and fields: + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post', array(), array( + 'custom_field' => 'my custom value', + 'file_field' => '@/path/to/file.xml' + )); + + $response = $request->send(); + +.. note:: + + Remember to **always** sanitize user input when sending POST requests: + + .. code-block:: php + + // Prevent users from accessing sensitive files by sanitizing input + $_POST = array('firstname' => '@/etc/passwd'); + $request = $client->post('http://www.example.com', array(), array ( + 'firstname' => str_replace('@', '', $_POST['firstname']) + )); + +You can alternatively build up the contents of a POST request. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post') + ->setPostField('custom_field', 'my custom value') + ->addPostFile('file', '/path/to/file.xml'); + + $response = $request->send(); + +Raw POST data +^^^^^^^^^^^^^ + +POST requests can also contain raw POST data that is not related to HTML forms. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post', array(), 'this is the body'); + $response = $request->send(); + +You can set the body of POST request using the ``setBody()`` method of the +``Guzzle\Http\Message\EntityEnclosingRequest`` object. This method accepts a string, a resource returned from +``fopen``, or a ``Guzzle\Http\EntityBodyInterface`` object. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post'); + // Set the body of the POST to stream the contents of /path/to/large_body.txt + $request->setBody(fopen('/path/to/large_body.txt', 'r')); + $response = $request->send(); + +PUT requests +~~~~~~~~~~~~ + +The `PUT method `_ requests that the enclosed entity be +stored under the supplied Request-URI. PUT requests are similar to POST requests in that they both can send an entity +body in the request message. + +The body of a PUT request (any any ``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object) is always stored as +a ``Guzzle\Http\Message\EntityBodyInterface`` object. This allows a great deal of flexibility when sending data to a +remote server. For example, you can stream the contents of a stream returned by fopen, stream the contents of a +callback function, or simply send a string of data. + +.. code-block:: php + + $request = $client->put('http://httpbin.org/put', array(), 'this is the body'); + $response = $request->send(); + +Just like with POST, PATH, and DELETE requests, you can set the body of a PUT request using the ``setBody()`` method. + +.. code-block:: php + + $request = $client->put('http://httpbin.org/put'); + $request->setBody(fopen('/path/to/large_body.txt', 'r')); + $response = $request->send(); + +PATCH requests +~~~~~~~~~~~~~~ + +`PATCH requests `_ are used to modify a resource. + +.. code-block:: php + + $request = $client->patch('http://httpbin.org', array(), 'this is the body'); + $response = $request->send(); + +OPTIONS requests +~~~~~~~~~~~~~~~~ + +The `OPTIONS method `_ represents a request for +information about the communication options available on the request/response chain identified by the Request-URI. + +.. code-block:: php + + $request = $client->options('http://httpbin.org'); + $response = $request->send(); + + // Check if the PUT method is supported by this resource + var_export($response->isMethodAllows('PUT')); + +Custom requests +~~~~~~~~~~~~~~~ + +You can create custom HTTP requests that use non-standard HTTP methods using the ``createRequest()`` method of a +client object. + +.. code-block:: php + + $request = $client->createRequest('COPY', 'http://example.com/foo', array( + 'Destination' => 'http://example.com/bar', + 'Overwrite' => 'T' + )); + $response = $request->send(); + +Query string parameters +----------------------- + +Query string parameters of a request are owned by a request's ``Guzzle\Http\Query`` object that is accessible by +calling ``$request->getQuery()``. The Query class extends from ``Guzzle\Common\Collection`` and allows you to set one +or more query string parameters as key value pairs. You can set a parameter on a Query object using the +``set($key, $value)`` method or access the query string object like an associative array. Any previously specified +value for a key will be overwritten when using ``set()``. Use ``add($key, $value)`` to add a value to query string +object, and in the event of a collision with an existing value at a specific key, the value will be converted to an +array that contains all of the previously set values. + +.. code-block:: php + + $request = new Guzzle\Http\Message\Request('GET', 'http://www.example.com?foo=bar&abc=123'); + + $query = $request->getQuery(); + echo "{$query}\n"; + // >>> foo=bar&abc=123 + + $query->remove('abc'); + echo "{$query}\n"; + // >>> foo=bar + + $query->set('foo', 'baz'); + echo "{$query}\n"; + // >>> foo=baz + + $query->add('foo', 'bar'); + echo "{$query}\n"; + // >>> foo%5B0%5D=baz&foo%5B1%5D=bar + +Whoah! What happened there? When ``foo=bar`` was added to the existing ``foo=baz`` query string parameter, the +aggregator associated with the Query object was used to help convert multi-value query string parameters into a string. +Let's disable URL-encoding to better see what's happening. + +.. code-block:: php + + $query->useUrlEncoding(false); + echo "{$query}\n"; + // >>> foo[0]=baz&foo[1]=bar + +.. note:: + + URL encoding can be disabled by passing false, enabled by passing true, set to use RFC 1738 by passing + ``Query::FORM_URLENCODED`` (internally uses PHP's ``urlencode`` function), or set to RFC 3986 by passing + ``Query::RFC_3986`` (this is the default and internally uses PHP's ``rawurlencode`` function). + +As you can see, the multiple values were converted into query string parameters following the default PHP convention of +adding numerically indexed square bracket suffixes to each key (``foo[0]=baz&foo[1]=bar``). The strategy used to convert +multi-value parameters into a string can be customized using the ``setAggregator()`` method of the Query class. Guzzle +ships with the following query string aggregators by default: + +1. ``Guzzle\Http\QueryAggregator\PhpAggregator``: Aggregates using PHP style brackets (e.g. ``foo[0]=baz&foo[1]=bar``) +2. ``Guzzle\Http\QueryAggregator\DuplicateAggregator``: Performs no aggregation and allows for key value pairs to be + repeated in a URL (e.g. ``foo=baz&foo=bar``) +3. ``Guzzle\Http\QueryAggregator\CommaAggregator``: Aggregates using commas (e.g. ``foo=baz,bar``) + +.. _http-message-headers: + +HTTP Message Headers +-------------------- + +HTTP message headers are case insensitive, multiple occurrences of any header can be present in an HTTP message +(whether it's valid or not), and some servers require specific casing of particular headers. Because of this, request +and response headers are stored in ``Guzzle\Http\Message\Header`` objects. The Header object can be cast as a string, +counted, or iterated to retrieve each value from the header. Casting a Header object to a string will return all of +the header values concatenated together using a glue string (typically ", "). + +A request (and response) object have several methods that allow you to retrieve and modify headers. + +* ``getHeaders()``: Get all of the headers of a message as a ``Guzzle\Http\Message\Header\HeaderCollection`` object. +* ``getHeader($header)``: Get a specific header from a message. If the header exists, you'll get a + ``Guzzle\Http\Message\Header`` object. If the header does not exist, this methods returns ``null``. +* ``hasHeader($header)``: Returns true or false based on if the message has a particular header. +* ``setHeader($header, $value)``: Set a header value and overwrite any previously set value for this header. +* ``addHeader($header, $value)``: Add a header with a particular name. If a previous value was already set by the same, + then the header will contain multiple values. +* ``removeHeader($header)``: Remove a header by name from the message. + +.. code-block:: php + + $request = new Request('GET', 'http://httpbin.com/cookies'); + // addHeader will set and append to any existing header values + $request->addHeader('Foo', 'bar'); + $request->addHeader('foo', 'baz'); + // setHeader overwrites any existing values + $request->setHeader('Test', '123'); + + // Request headers can be cast as a string + echo $request->getHeader('Foo'); + // >>> bar, baz + echo $request->getHeader('Test'); + // >>> 123 + + // You can count the number of headers of a particular case insensitive name + echo count($request->getHeader('foO')); + // >>> 2 + + // You can iterate over Header objects + foreach ($request->getHeader('foo') as $header) { + echo $header . "\n"; + } + + // You can get all of the request headers as a Guzzle\Http\Message\Header\HeaderCollection object + $headers = $request->getHeaders(); + + // Missing headers return NULL + var_export($request->getHeader('Missing')); + // >>> null + + // You can see all of the different variations of a header by calling raw() on the Header + var_export($request->getHeader('foo')->raw()); + +Setting the body of a request +----------------------------- + +Requests that can send a body (e.g. PUT, POST, DELETE, PATCH) are instances of +``Guzzle\Http\Message\EntityEnclosingRequestInterface``. Entity enclosing requests contain several methods that allow +you to specify the body to send with a request. + +Use the ``setBody()`` method of a request to set the body that will be sent with a request. This method accepts a +string, a resource returned by ``fopen()``, an array, or an instance of ``Guzzle\Http\EntityBodyInterface``. The body +will then be streamed from the underlying ``EntityBodyInterface`` object owned by the request. When setting the body +of the request, you can optionally specify a Content-Type header and whether or not to force the request to use +chunked Transfer-Encoding. + +.. code-block:: php + + $request = $client->put('/user.json'); + $request->setBody('{"foo":"baz"}', 'application/json'); + +Content-Type header +~~~~~~~~~~~~~~~~~~~ + +Guzzle will automatically add a Content-Type header to a request if the Content-Type can be guessed based on the file +extension of the payload being sent or the file extension present in the path of a request. + +.. code-block:: php + + $request = $client->put('/user.json', array(), '{"foo":"bar"}'); + // The Content-Type was guessed based on the path of the request + echo $request->getHeader('Content-Type'); + // >>> application/json + + $request = $client->put('/user.json'); + $request->setBody(fopen('/tmp/user_data.json', 'r')); + // The Content-Type was guessed based on the path of the entity body + echo $request->getHeader('Content-Type'); + // >>> application/json + +Transfer-Encoding: chunked header +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When sending HTTP requests that contain a payload, you must let the remote server know how to determine when the entire +message has been sent. This usually is done by supplying a ``Content-Length`` header that tells the origin server the +size of the body that is to be sent. In some cases, the size of the payload being sent in a request cannot be known +before initiating the transfer. In these cases (when using HTTP/1.1), you can use the ``Transfer-Encoding: chunked`` +header. + +If the Content-Length cannot be determined (i.e. using a PHP ``http://`` stream), then Guzzle will automatically add +the ``Transfer-Encoding: chunked`` header to the request. + +.. code-block:: php + + $request = $client->put('/user.json'); + $request->setBody(fopen('http://httpbin.org/get', 'r')); + + // The Content-Length could not be determined + echo $request->getHeader('Transfer-Encoding'); + // >>> chunked + +See :doc:`/http-client/entity-bodies` for more information on entity bodies. + +Expect: 100-Continue header +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Expect: 100-Continue`` header is used to help a client prevent sending a large payload to a server that will +reject the request. This allows clients to fail fast rather than waste bandwidth sending an erroneous payload. Guzzle +will automatically add the ``Expect: 100-Continue`` header to a request when the size of the payload exceeds 1MB or if +the body of the request is not seekable (this helps to prevent errors when a non-seekable body request is redirected). + +.. note:: + + If you find that your larger requests are taking too long to complete, you should first check if the + ``Expect: 100-Continue`` header is being sent with the request. Some servers do not respond well to this header, + which causes cURL to sleep for `1 second `_. + +POST fields and files +~~~~~~~~~~~~~~~~~~~~~ + +Any entity enclosing request can send POST style fields and files. This includes POST, PUT, PATCH, and DELETE requests. +Any request that has set POST fields or files will use cURL's POST message functionality. + +.. code-block:: php + + $request = $client->post('/post'); + // Set an overwrite any previously specified value + $request->setPostField('foo', 'bar'); + // Append a value to any existing values + $request->getPostFields()->add('foo', 'baz'); + // Remove a POST field by name + $request->removePostField('fizz'); + + // Add a file to upload (forces multipart/form-data) + $request->addPostFile('my_file', '/path/to/file', 'plain/text'); + // Remove a POST file by POST key name + $request->removePostFile('my_other_file'); + +.. tip:: + + Adding a large number of POST fields to a POST request is faster if you use the ``addPostFields()`` method so that + you can add and process multiple fields with a single call. Adding multiple POST files is also faster using + ``addPostFiles()``. + +Working with cookies +-------------------- + +Cookies can be modified and retrieved from a request using the following methods: + +.. code-block:: php + + $request->addCookie($name, $value); + $request->removeCookie($name); + $value = $request->getCookie($name); + $valueArray = $request->getCookies(); + +Use the :doc:`cookie plugin ` if you need to reuse cookies between requests. + +.. _request-set-response-body: + +Changing where a response is downloaded +---------------------------------------- + +When a request is sent, the body of the response will be stored in a PHP temp stream by default. You can change the +location in which the response will be downloaded using ``$request->setResponseBody($body)`` or the ``save_to`` request +option. This can be useful for downloading the contents of a URL to a specific file. + +Here's an example of using request options: + +.. code-block:: php + + $request = $this->client->get('http://example.com/large.mov', array(), array( + 'save_to' => '/tmp/large_file.mov' + )); + $request->send(); + var_export(file_exists('/tmp/large_file.mov')); + // >>> true + +Here's an example of using ``setResponseBody()``: + +.. code-block:: php + + $body = fopen('/tmp/large_file.mov', 'w'); + $request = $this->client->get('http://example.com/large.mov'); + $request->setResponseBody($body); + + // You can more easily specify the name of a file to save the contents + // of the response to by passing a string to ``setResponseBody()``. + + $request = $this->client->get('http://example.com/large.mov'); + $request->setResponseBody('/tmp/large_file.mov'); + +Custom cURL options +------------------- + +Most of the functionality implemented in the libcurl bindings has been simplified and abstracted by Guzzle. Developers +who need access to `cURL specific functionality `_ can still add cURL handle +specific behavior to Guzzle HTTP requests by modifying the cURL options collection of a request: + +.. code-block:: php + + $request->getCurlOptions()->set(CURLOPT_LOW_SPEED_LIMIT, 200); + +Other special options that can be set in the ``curl.options`` array include: + ++-------------------------+---------------------------------------------------------------------------------+ +| debug | Adds verbose cURL output to a temp stream owned by the cURL handle object | ++-------------------------+---------------------------------------------------------------------------------+ +| progress | Instructs cURL to emit events when IO events occur. This allows you to be | +| | notified when bytes are transferred over the wire by subscribing to a request's | +| | ``curl.callback.read``, ``curl.callback.write``, and ``curl.callback.progress`` | +| | events. | ++-------------------------+---------------------------------------------------------------------------------+ + +Request options +--------------- + +Requests options can be specified when creating a request or in the ``request.options`` parameter of a client. These +options can control various aspects of a request including: headers to send, query string data, where the response +should be downloaded, proxies, auth, etc. + +.. code-block:: php + + $request = $client->get($url, $headers, array('proxy' => 'http://proxy.com')); + +See :ref:`Request options ` for more information. + +Working with errors +------------------- + +HTTP errors +~~~~~~~~~~~ + +Requests that receive a 4xx or 5xx response will throw a ``Guzzle\Http\Exception\BadResponseException``. More +specifically, 4xx errors throw a ``Guzzle\Http\Exception\ClientErrorResponseException``, and 5xx errors throw a +``Guzzle\Http\Exception\ServerErrorResponseException``. You can catch the specific exceptions or just catch the +BadResponseException to deal with either type of error. Here's an example of catching a generic BadResponseException: + +.. code-block:: php + + try { + $response = $client->get('/not_found.xml')->send(); + } catch (Guzzle\Http\Exception\BadResponseException $e) { + echo 'Uh oh! ' . $e->getMessage(); + echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n"; + echo 'HTTP request: ' . $e->getRequest() . "\n"; + echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n"; + echo 'HTTP response: ' . $e->getResponse() . "\n"; + } + +Throwing an exception when a 4xx or 5xx response is encountered is the default behavior of Guzzle requests. This +behavior can be overridden by adding an event listener with a higher priority than -255 that stops event propagation. +You can subscribe to ``request.error`` to receive notifications any time an unsuccessful response is received. + +You can change the response that will be associated with the request by calling ``setResponse()`` on the +``$event['request']`` object passed into your listener, or by changing the ``$event['response']`` value of the +``Guzzle\Common\Event`` object that is passed to your listener. Transparently changing the response associated with a +request by modifying the event allows you to retry failed requests without complicating the code that uses the client. +This might be useful for sending requests to a web service that has expiring auth tokens. When a response shows that +your token has expired, you can get a new token, retry the request with the new token, and return the successful +response to the user. + +Here's an example of retrying a request using updated authorization credentials when a 401 response is received, +overriding the response of the original request with the new response, and still allowing the default exception +behavior to be called when other non-200 response status codes are encountered: + +.. code-block:: php + + // Add custom error handling to any request created by this client + $client->getEventDispatcher()->addListener('request.error', function(Event $event) { + + if ($event['response']->getStatusCode() == 401) { + + $newRequest = $event['request']->clone(); + $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken()); + $newResponse = $newRequest->send(); + + // Set the response object of the request without firing more events + $event['response'] = $newResponse; + + // You can also change the response and fire the normal chain of + // events by calling $event['request']->setResponse($newResponse); + + // Stop other events from firing when you override 401 responses + $event->stopPropagation(); + } + + }); + +cURL errors +~~~~~~~~~~~ + +Connection problems and cURL specific errors can also occur when transferring requests using Guzzle. When Guzzle +encounters cURL specific errors while transferring a single request, a ``Guzzle\Http\Exception\CurlException`` is +thrown with an informative error message and access to the cURL error message. + +A ``Guzzle\Http\Exception\MultiTransferException`` exception is thrown when a cURL specific error occurs while +transferring multiple requests in parallel. You can then iterate over all of the exceptions encountered during the +transfer. + +Plugins and events +------------------ + +Guzzle request objects expose various events that allow you to hook in custom logic. A request object owns a +``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling +``$request->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or +event subscribers (classes that listen to specific events of a dispatcher). You can add event subscribers to a request +directly by just calling ``$request->addSubscriber($mySubscriber);``. + +.. _request-events: + +Events emitted from a request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Guzzle\Http\Message\Request`` and ``Guzzle\Http\Message\EntityEnclosingRequest`` object emit the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| request.before_send | About to send request | * request: Request to be sent | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.sent | Sent the request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.complete | Completed a full HTTP transaction | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.success | Completed a successful request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.error | Completed an unsuccessful request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.exception | An unsuccessful response was | * request: Request | +| | received. | * response: Received response | +| | | * exception: BadResponseException | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.receive.status_line | Received the start of a response | * line: Full response start line | +| | | * status_code: Status code | +| | | * reason_phrase: Reason phrase | +| | | * previous_response: (e.g. redirect) | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.progress | cURL progress event (only dispatched when | * handle: CurlHandle | +| | ``emit_io`` is set on a request's curl | * download_size: Total download size | +| | options) | * downloaded: Bytes downloaded | +| | | * upload_size: Total upload bytes | +| | | * uploaded: Bytes uploaded | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.write | cURL event called when data is written to | * request: Request | +| | an outgoing stream | * write: Data being written | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.read | cURL event called when data is written to | * request: Request | +| | an incoming stream | * read: Data being read | ++------------------------------+--------------------------------------------+------------------------------------------+ + +Creating a request event listener +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here's an example that listens to the ``request.complete`` event of a request and prints the request and response. + +.. code-block:: php + + use Guzzle\Common\Event; + + $request = $client->get('http://www.google.com'); + + // Echo out the response that was received + $request->getEventDispatcher()->addListener('request.complete', function (Event $e) { + echo $e['request'] . "\n\n"; + echo $e['response']; + }); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/response.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/response.rst new file mode 100644 index 000000000..ba487316f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/response.rst @@ -0,0 +1,141 @@ +====================== +Using Response objects +====================== + +Sending a request will return a ``Guzzle\Http\Message\Response`` object. You can view the raw HTTP response message by +casting the Response object to a string. Casting the response to a string will return the entity body of the response +as a string too, so this might be an expensive operation if the entity body is stored in a file or network stream. If +you only want to see the response headers, you can call ``getRawHeaders()``. + +Response status line +-------------------- + +The different parts of a response's `status line `_ +(the first line of the response HTTP message) are easily retrievable. + +.. code-block:: php + + $response = $client->get('http://www.amazon.com')->send(); + + echo $response->getStatusCode(); // >>> 200 + echo $response->getReasonPhrase(); // >>> OK + echo $response->getProtocol(); // >>> HTTP + echo $response->getProtocolVersion(); // >>> 1.1 + +You can determine the type of the response using several helper methods: + +.. code-block:: php + + $response->isSuccessful(); // true + $response->isInformational(); + $response->isRedirect(); + $response->isClientError(); + $response->isServerError(); + +Response headers +---------------- + +The Response object contains helper methods for retrieving common response headers. These helper methods normalize the +variations of HTTP response headers. + +.. code-block:: php + + $response->getCacheControl(); + $response->getContentType(); + $response->getContentLength(); + $response->getContentEncoding(); + $response->getContentMd5(); + $response->getEtag(); + // etc... There are methods for every known response header + +You can interact with the Response headers using the same exact methods used to interact with Request headers. See +:ref:`http-message-headers` for more information. + +.. code-block:: php + + echo $response->getHeader('Content-Type'); + echo $response->getHeader('Content-Length'); + echo $response->getHeaders()['Content-Type']; // PHP 5.4 + +Response body +------------- + +The entity body object of a response can be retrieved by calling ``$response->getBody()``. The response EntityBody can +be cast to a string, or you can pass ``true`` to this method to retrieve the body as a string. + +.. code-block:: php + + $request = $client->get('http://www.amazon.com'); + $response = $request->send(); + echo $response->getBody(); + +See :doc:`/http-client/entity-bodies` for more information on entity bodies. + +JSON Responses +~~~~~~~~~~~~~~ + +You can easily parse and use a JSON response as an array using the ``json()`` method of a response. This method will +always return an array if the response is valid JSON or if the response body is empty. You will get an exception if you +call this method and the response is not valid JSON. + +.. code-block:: php + + $data = $response->json(); + echo gettype($data); + // >>> array + +XML Responses +~~~~~~~~~~~~~ + +You can easily parse and use a XML response as SimpleXMLElement object using the ``xml()`` method of a response. This +method will always return a SimpleXMLElement object if the response is valid XML or if the response body is empty. You +will get an exception if you call this method and the response is not valid XML. + +.. code-block:: php + + $xml = $response->xml(); + echo $xml->foo; + // >>> Bar! + +Streaming responses +------------------- + +Some web services provide streaming APIs that allow a client to keep a HTTP request open for an extended period of +time while polling and reading. Guzzle provides a simple way to convert HTTP request messages into +``Guzzle\Stream\Stream`` objects so that you can send the initial headers of a request, read the response headers, and +pull in the response body manually as needed. + +Here's an example using the Twitter Streaming API to track the keyword "bieber": + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Stream\PhpStreamRequestFactory; + + $client = new Client('https://stream.twitter.com/1'); + + $request = $client->post('statuses/filter.json', null, array( + 'track' => 'bieber' + )); + + $request->setAuth('myusername', 'mypassword'); + + $factory = new PhpStreamRequestFactory(); + $stream = $factory->fromRequest($request); + + // Read until the stream is closed + while (!$stream->feof()) { + // Read a line from the stream + $line = $stream->readLine(); + // JSON decode the line of data + $data = json_decode($line, true); + } + +You can use the ``stream`` request option when using a static client to more easily create a streaming response. + +.. code-block:: php + + $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); + while (!$stream->feof()) { + echo $stream->readLine(); + } diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst new file mode 100644 index 000000000..c18ac3e8d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst @@ -0,0 +1,52 @@ +============= +URI templates +============= + +The ``$uri`` passed to one of the client's request creational methods or the base URL of a client can utilize URI +templates. Guzzle supports the entire `URI templates RFC `_. URI templates add a +special syntax to URIs that replace template place holders with user defined variables. + +Every request created by a Guzzle HTTP client passes through a URI template so that URI template expressions are +automatically expanded: + +.. code-block:: php + + $client = new Guzzle\Http\Client('https://example.com/', array('a' => 'hi')); + $request = $client->get('/{a}'); + +Because of URI template expansion, the URL of the above request will become ``https://example.com/hi``. Notice that +the template was expanded using configuration variables of the client. You can pass in custom URI template variables +by passing the URI of your request as an array where the first index of the array is the URI template and the second +index of the array are template variables that are merged into the client's configuration variables. + +.. code-block:: php + + $request = $client->get(array('/test{?a,b}', array('b' => 'there'))); + +The URL for this request will become ``https://test.com?a=hi&b=there``. URI templates aren't limited to just simple +variable replacements; URI templates can provide an enormous amount of flexibility when creating request URIs. + +.. code-block:: php + + $request = $client->get(array('http://example.com{+path}{/segments*}{?query,data*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => 'value' + ) + ))); + +The resulting URL would become ``http://example.com/foo/bar/one/two?query=test&more=value``. + +By default, URI template expressions are enclosed in an opening and closing brace (e.g. ``{var}``). If you are working +with a web service that actually uses braces (e.g. Solr), then you can specify a custom regular expression to use to +match URI template expressions. + +.. code-block:: php + + $client->getUriTemplate()->setRegex('/\<\$(.+)\>/'); + $client->get('/<$a>'); + +You can learn about all of the different features of URI templates by reading the +`URI templates RFC `_. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/index.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/index.rst new file mode 100644 index 000000000..f76f3bbe6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/index.rst @@ -0,0 +1,5 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services +.. toctree:: + :hidden: + + docs.rst diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst new file mode 100644 index 000000000..a5c7fd33f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst @@ -0,0 +1,97 @@ +================ +Guzzle iterators +================ + +Guzzle provides several SPL iterators that can be used with other SPL iterators, including Guzzle resource iterators. +Guzzle's ``guzzle/iterator`` component can also be used independently of the rest of Guzzle through Packagist and +Composer: https://packagist.org/packages/guzzle/iterator + +ChunkedIterator +--------------- + +Pulls out multiple values from an inner iterator and yields and array of values for each outer iteration -- essentially +pulling out chunks of values from the inner iterator. + +.. code-block:: php + + use Guzzle\Iterator\ChunkedIterator; + + $inner = new ArrayIterator(range(0, 8)); + $chunkedIterator = new ChunkedIterator($inner, 2); + + foreach ($chunkedIterator as $chunk) { + echo implode(', ', $chunk) . "\n"; + } + + // >>> 0, 1 + // >>> 2, 3 + // >>> 4, 5 + // >>> 6, 7 + // >>> 8 + +FilterIterator +-------------- + +This iterator is used to filter values out of the inner iterator. This iterator can be used when PHP 5.4's +CallbackFilterIterator is not available. + +.. code-block:: php + + use Guzzle\Iterator\FilterIterator; + + $inner = new ArrayIterator(range(1, 10)); + $filterIterator = new FilterIterator($inner, function ($value) { + return $value % 2; + }); + + foreach ($filterIterator as $value) { + echo $value . "\n"; + } + + // >>> 2 + // >>> 4 + // >>> 6 + // >>> 8 + // >>> 10 + +MapIterator +----------- + +This iterator modifies the values of the inner iterator before yielding. + +.. code-block:: php + + use Guzzle\Iterator\MapIterator; + + $inner = new ArrayIterator(range(0, 3)); + + $mapIterator = new MapIterator($inner, function ($value) { + return $value * 10; + }); + + foreach ($mapIterator as $value) { + echo $value . "\n"; + } + + // >>> 0 + // >>> 10 + // >>> 20 + // >>> 30 + +MethodProxyIterator +------------------- + +This decorator is useful when you need to expose a specific method from an inner iterator that might be wrapper +by one or more iterator decorators. This decorator proxies missing method calls to each inner iterator until one +of the inner iterators can fulfill the call. + +.. code-block:: php + + use Guzzle\Iterator\MethodProxyIterator; + + $inner = new \ArrayIterator(); + $proxy = new MethodProxyIterator($inner); + + // Proxy method calls to the ArrayIterator + $proxy->append('a'); + $proxy->append('b'); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst new file mode 100644 index 000000000..ce0bee59f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst @@ -0,0 +1,149 @@ +================== +Resource iterators +================== + +Web services often implement pagination in their responses which requires the end-user to issue a series of consecutive +requests in order to fetch all of the data they asked for. Users of your web service client should not be responsible +for implementing the logic involved in iterating through pages of results. Guzzle provides a simple resource iterator +foundation to make it easier on web service client developers to offer a useful abstraction layer. + +Getting an iterator from a client +--------------------------------- + + ResourceIteratorInterface Guzzle\Service\Client::getIterator($command [, array $commandOptions, array $iteratorOptions ]) + +The ``getIterator`` method of a ``Guzzle\Service\ClientInterface`` object provides a convenient interface for +instantiating a resource iterator for a specific command. This method implicitly uses a +``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object to create resource iterators. Pass an +instantiated command object or the name of a command in the first argument. When passing the name of a command, the +command factory of the client will create the command by name using the ``$commandOptions`` array. The third argument +may be used to pass an array of options to the constructor of the instantiated ``ResourceIteratorInterface`` object. + +.. code-block:: php + + $iterator = $client->getIterator('get_users'); + + foreach ($iterator as $user) { + echo $user['name'] . ' age ' . $user['age'] . PHP_EOL; + } + +The above code sample might execute a single request or a thousand requests. As a consumer of a web service, I don't +care. I just want to iterate over all of the users. + +Iterator options +~~~~~~~~~~~~~~~~ + +The two universal options that iterators should support are ``limit`` and ``page_size``. Using the ``limit`` option +tells the resource iterator to attempt to limit the total number of iterated resources to a specific amount. Keep in +mind that this is not always possible due to limitations that may be inherent to a web service. The ``page_size`` +option is used to tell a resource iterator how many resources to request per page of results. Much like the ``limit`` +option, you can not rely on getting back exactly the number of resources your specify in the ``page_size`` option. + +.. note:: + + The ``limit`` and ``page_size`` options can also be specified on an iterator using the ``setLimit($limit)`` and + ``setPageSize($pageSize)`` methods. + +Resolving iterator class names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default resource iterator factory of a client object expects that your iterators are stored under the ``Model`` +folder of your client and that an iterator is names after the CamelCase name of a command followed by the word +"Iterator". For example, if you wanted to create an iterator for the ``get_users`` command, then your iterator class +would be ``Model\GetUsersIterator`` and would be stored in ``Model/GetUsersIterator.php``. + +Creating an iterator +-------------------- + +While not required, resource iterators in Guzzle typically iterate using a ``Guzzle\Service\Command\CommandInterface`` +object. ``Guzzle\Service\Resource\ResourceIterator``, the default iterator implementation that you should extend, +accepts a command object and array of iterator options in its constructor. The command object passed to the resource +iterator is expected to be ready to execute and not previously executed. The resource iterator keeps a reference of +this command and clones the original command each time a subsequent request needs to be made to fetch more data. + +Implement the sendRequest method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The most important thing (and usually the only thing) you need to do when creating a resource iterator is to implement +the ``sendRequest()`` method of the resource iterator. The ``sendRequest()`` method is called when you begin +iterating or if there are no resources left to iterate and it you expect to retrieve more resources by making a +subsequent request. The ``$this->command`` property of the resource iterator is updated with a cloned copy of the +original command object passed into the constructor of the iterator. Use this command object to issue your subsequent +requests. + +The ``sendRequest()`` method must return an array of the resources you retrieved from making the subsequent call. +Returning an empty array will stop the iteration. If you suspect that your web service client will occasionally return +an empty result set but still requires further iteration, then you must implement a sort of loop in your +``sendRequest()`` method that will continue to issue subsequent requests until your reach the end of the paginated +result set or until additional resources are retrieved from the web service. + +Update the nextToken property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Beyond fetching more results, the ``sendRequest()`` method is responsible for updating the ``$this->nextToken`` +property of the iterator. Setting this property to anything other than null tells the iterator that issuing a +subsequent request using the nextToken value will probably return more results. You must continually update this +value in your ``sendRequest()`` method as each response is received from the web service. + +Example iterator +---------------- + +Let's say you want to implement a resource iterator for the ``get_users`` command of your web service. The +``get_users`` command receives a response that contains a list of users, and if there are more pages of results to +retrieve, returns a value called ``next_user``. This return value is known as the **next token** and should be used to +issue subsequent requests. + +Assume the response to a ``get_users`` command returns JSON data that looks like this: + +.. code-block:: javascript + + { + "users": [ + { "name": "Craig Johnson", "age": 10 }, + { "name": "Tom Barker", "age": 20 }, + { "name": "Bob Mitchell", "age": 74 } + ], + "next_user": "Michael Dowling" + } + +Assume that because there is a ``next_user`` value, there will be more users if a subsequent request is issued. If the +``next_user`` value is missing or null, then we know there are no more results to fetch. Let's implement a resource +iterator for this command. + +.. code-block:: php + + namespace MyService\Model; + + use Guzzle\Service\Resource\ResourceIterator; + + /** + * Iterate over a get_users command + */ + class GetUsersIterator extends ResourceIterator + { + protected function sendRequest() + { + // If a next token is set, then add it to the command + if ($this->nextToken) { + $this->command->set('next_user', $this->nextToken); + } + + // Execute the command and parse the result + $result = $this->command->execute(); + + // Parse the next token + $this->nextToken = isset($result['next_user']) ? $result['next_user'] : false; + + return $result['users']; + } + } + +As you can see, it's pretty simple to implement an iterator. There are a few things that you should notice from this +example: + +1. You do not need to create a new command in the ``sendRequest()`` method. A new command object is cloned from the + original command passed into the constructor of the iterator before the ``sendRequest()`` method is called. + Remember that the resource iterator expects a command that has not been executed. +2. When the ``sendRequest()`` method is first called, you will not have a ``$this->nextToken`` value, so always check + before setting it on a command. Notice that the next token is being updated each time a request is sent. +3. After fetching more resources from the service, always return an array of resources. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst new file mode 100644 index 000000000..9bd8f4251 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst @@ -0,0 +1,18 @@ +============ +Async plugin +============ + +The AsyncPlugin allows you to send requests that do not wait on a response. This is handled through cURL by utilizing +the progress event. When a request has sent all of its data to the remote server, Guzzle adds a 1ms timeout on the +request and instructs cURL to not download the body of the response. The async plugin then catches the exception and +adds a mock response to the request, along with an X-Guzzle-Async header to let you know that the response was not +fully downloaded. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Async\AsyncPlugin; + + $client = new Client('http://www.example.com'); + $client->addSubscriber(new AsyncPlugin()); + $response = $client->get()->send(); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst new file mode 100644 index 000000000..5a7694141 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst @@ -0,0 +1,22 @@ +==================== +Backoff retry plugin +==================== + +The ``Guzzle\Plugin\Backoff\BackoffPlugin`` automatically retries failed HTTP requests using custom backoff strategies: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Backoff\BackoffPlugin; + + $client = new Client('http://www.test.com/'); + // Use a static factory method to get a backoff plugin using the exponential backoff strategy + $backoffPlugin = BackoffPlugin::getExponentialBackoff(); + + // Add the backoff plugin to the client object + $client->addSubscriber($backoffPlugin); + +The BackoffPlugin's constructor accepts a ``Guzzle\Plugin\Backoff\BackoffStrategyInterface`` object that is used to +determine when a retry should be issued and how long to delay between retries. The above code example shows how to +attach a BackoffPlugin to a client that is pre-configured to retry failed 500 and 503 responses using truncated +exponential backoff (emulating the behavior of Guzzle 2's ExponentialBackoffPlugin). diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst new file mode 100644 index 000000000..d2fd5df26 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst @@ -0,0 +1,169 @@ +================= +HTTP Cache plugin +================= + +Guzzle can leverage HTTP's caching specifications using the ``Guzzle\Plugin\Cache\CachePlugin``. The CachePlugin +provides a private transparent proxy cache that caches HTTP responses. The caching logic, based on +`RFC 2616 `_, uses HTTP headers to control caching behavior, +cache lifetime, and supports Vary, ETag, and Last-Modified based revalidation: + +.. code-block:: php + + use Guzzle\Http\Client; + use Doctrine\Common\Cache\FilesystemCache; + use Guzzle\Cache\DoctrineCacheAdapter; + use Guzzle\Plugin\Cache\CachePlugin; + use Guzzle\Plugin\Cache\DefaultCacheStorage; + + $client = new Client('http://www.test.com/'); + + $cachePlugin = new CachePlugin(array( + 'storage' => new DefaultCacheStorage( + new DoctrineCacheAdapter( + new FilesystemCache('/path/to/cache/files') + ) + ) + )); + + // Add the cache plugin to the client object + $client->addSubscriber($cachePlugin); + $client->get('http://www.wikipedia.org/')->send(); + + // The next request will revalidate against the origin server to see if it + // has been modified. If a 304 response is received the response will be + // served from cache + $client->get('http://www.wikipedia.org/')->send(); + +The cache plugin intercepts GET and HEAD requests before they are actually transferred to the origin server. The cache +plugin then generates a hash key based on the request method and URL, and checks to see if a response exists in the cache. If +a response exists in the cache, the cache adapter then checks to make sure that the caching rules associated with the response +satisfy the request, and ensures that response still fresh. If the response is acceptable for the request any required +revalidation, then the cached response is served instead of contacting the origin server. + +Vary +---- + +Cache keys are derived from a request method and a request URL. Multiple responses can map to the same cache key and +stored in Guzzle's underlying cache storage object. You should use the ``Vary`` HTTP header to tell the cache storage +object that the cache response must have been cached for a request that matches the headers specified in the Vary header +of the request. This allows you to have specific cache entries for the same request URL but variations in a request's +headers determine which cache entry is served. Please see the http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44 +for more information. + +Cache options +------------- + +There are several options you can add to requests or clients to modify the behavior of the cache plugin. + +Override cache TTL +~~~~~~~~~~~~~~~~~~ + +You can override the number of seconds a cacheable response is stored in the cache by setting the +``cache.override_ttl`` parameter on the params object of a request: + +.. code-block:: php + + // If the response to the request is cacheable, then the response will be cached for 100 seconds + $request->getParams()->set('cache.override_ttl', 100); + +If a response doesn't specify any freshness policy, it will be kept in cache for 3600 seconds by default. + +Custom caching decision +~~~~~~~~~~~~~~~~~~~~~~~ + +If the service you are interacting with does not return caching headers or returns responses that are normally +something that would not be cached, you can set a custom ``can_cache`` object on the constructor of the CachePlugin +and provide a ``Guzzle\Plugin\Cache\CanCacheInterface`` object. You can use the +``Guzzle\Plugin\Cache\CallbackCanCacheStrategy`` to easily make a caching decision based on an HTTP request and +response. + +Revalidation options +~~~~~~~~~~~~~~~~~~~~ + +You can change the revalidation behavior of a request using the ``cache.revalidate`` parameter. Setting this +parameter to ``never`` will ensure that a revalidation request is never sent, and the response is always served from +the origin server. Setting this parameter to ``skip`` will never revalidate and uses the response stored in the cache. + +Normalizing requests for caching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``cache.key_filter`` parameter if you wish to strip certain query string parameters from your +request before creating a unique hash for the request. This parameter can be useful if your requests have query +string values that cause each request URL to be unique (thus preventing a cache hit). The ``cache.key_filter`` +format is simply a comma separated list of query string values to remove from the URL when creating a cache key. +For example, here we are saying that the ``a`` and ``q`` query string variables should be ignored when generating a +cache key for the request: + +.. code-block:: php + + $request->getParams()->set('cache.key_filter', 'a, q'); + +Other options +~~~~~~~~~~~~~ + +There are many other options available to the CachePlugin that can meet almost any caching requirement, including +custom revalidation implementations, custom cache key generators, custom caching decision strategies, and custom +cache storage objects. Take a look the constructor of ``Guzzle\Plugin\Cache\CachePlugin`` for more information. + +Setting Client-wide cache settings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify cache settings for every request created by a client by adding cache settings to the configuration +options of a client. + +.. code-block:: php + + $client = new Guzzle\Http\Client('http://www.test.com', array( + 'request.params' => array( + 'cache.override_ttl' => 3600, + 'params.cache.revalidate' => 'never' + ) + )); + + echo $client->get('/')->getParams()->get('cache.override_ttl'); + // >>> 3600 + + echo $client->get('/')->getParams()->get('cache.revalidate'); + // >>> never + +Cache revalidation +------------------ + +If the cache plugin determines that a response to a GET request needs revalidation, a conditional GET is transferred +to the origin server. If the origin server returns a 304 response, then a response containing the merged headers of +the cached response with the new response and the entity body of the cached response is returned. Custom revalidation +strategies can be injected into a CachePlugin if needed. + +Cache adapters +-------------- + +Guzzle doesn't try to reinvent the wheel when it comes to caching or logging. Plenty of other frameworks have +excellent solutions in place that you are probably already using in your applications. Guzzle uses adapters for +caching and logging. The cache plugin requires a cache adapter so that is can store responses in a cache. Guzzle +currently supports cache adapters for `Doctrine 2.0 `_ and the +`Zend Framework `_. + +Doctrine cache adapter +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use Doctrine\Common\Cache\ArrayCache; + use Guzzle\Cache\DoctrineCacheAdapter; + use Guzzle\Plugin\Cache\CachePlugin; + + $backend = new ArrayCache(); + $adapter = new DoctrineCacheAdapter($backend); + $cache = new CachePlugin($adapter); + +Zend Framework cache adapter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use Guzzle\Cache\ZendCacheAdapter; + use Zend\Cache\Backend\TestBackend; + + $backend = new TestBackend(); + $adapter = new ZendCacheAdapter($backend); + $cache = new CachePlugin($adapter); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst new file mode 100644 index 000000000..a6cc7d924 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst @@ -0,0 +1,33 @@ +============= +Cookie plugin +============= + +Some web services require a Cookie in order to maintain a session. The ``Guzzle\Plugin\Cookie\CookiePlugin`` will add +cookies to requests and parse cookies from responses using a CookieJar object: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Cookie\CookiePlugin; + use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; + + $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); + + // Add the cookie plugin to a client + $client = new Client('http://www.test.com/'); + $client->addSubscriber($cookiePlugin); + + // Send the request with no cookies and parse the returned cookies + $client->get('http://www.yahoo.com/')->send(); + + // Send the request again, noticing that cookies are being sent + $request = $client->get('http://www.yahoo.com/'); + $request->send(); + + echo $request; + +You can disable cookies per-request by setting the ``cookies.disable`` value to true on a request's params object. + +.. code-block:: php + + $request->getParams()->set('cookies.disable', true); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst new file mode 100644 index 000000000..0870155b5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst @@ -0,0 +1,93 @@ +================ +Creating plugins +================ + +.. highlight:: php + +Guzzle is extremely extensible because of the behavioral modifications that can be added to requests, clients, and +commands using an event system. Before and after the majority of actions are taken in the library, an event is emitted +with the name of the event and context surrounding the event. Observers can subscribe to a subject and modify the +subject based on the events received. Guzzle's event system utilizes the Symfony2 EventDispatcher and is the backbone +of its plugin architecture. + +Overview +-------- + +Plugins must implement the ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` interface. The +``EventSubscriberInterface`` requires that your class implements a static method, ``getSubscribedEvents()``, that +returns an associative array mapping events to methods on the object. See the +`Symfony2 documentation `_ for more information. + +Plugins can be attached to any subject, or object in Guzzle that implements that +``Guzzle\Common\HasDispatcherInterface``. + +Subscribing to a subject +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can subscribe an instantiated observer to an event by calling ``addSubscriber`` on a subject. + +.. code-block:: php + + $testPlugin = new TestPlugin(); + $client->addSubscriber($testPlugin); + +You can also subscribe to only specific events using a closure:: + + $client->getEventDispatcher()->addListener('request.create', function(Event $event) { + echo $event->getName(); + echo $event['request']; + }); + +``Guzzle\Common\Event`` objects are passed to notified functions. The Event object has a ``getName()`` method which +return the name of the emitted event and may contain contextual information that can be accessed like an array. + +Knowing what events to listen to +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Any class that implements the ``Guzzle\Common\HasDispatcherInterface`` must implement a static method, +``getAllEvents()``, that returns an array of the events that are emitted from the object. You can browse the source +to see each event, or you can call the static method directly in your code to get a list of available events. + +Event hooks +----------- + +* :ref:`client-events` +* :ref:`service-client-events` +* :ref:`request-events` +* ``Guzzle\Http\Curl\CurlMulti``: +* :ref:`service-builder-events` + +Examples of the event system +---------------------------- + +Simple Echo plugin +~~~~~~~~~~~~~~~~~~ + +This simple plugin prints a string containing the request that is about to be sent by listening to the +``request.before_send`` event:: + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + class EchoPlugin implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + return array('request.before_send' => 'onBeforeSend'); + } + + public function onBeforeSend(Guzzle\Common\Event $event) + { + echo 'About to send a request: ' . $event['request'] . "\n"; + } + } + + $client = new Guzzle\Service\Client('http://www.test.com/'); + + // Create the plugin and add it as an event subscriber + $plugin = new EchoPlugin(); + $client->addSubscriber($plugin); + + // Send a request and notice that the request is printed to the screen + $client->get('/')->send(); + +Running the above code will print a string containing the HTTP request that is about to be sent. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst new file mode 100644 index 000000000..66d4a01e3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst @@ -0,0 +1,32 @@ +========================== +cURL authentication plugin +========================== + +.. warning:: + + The CurlAuthPlugin is deprecated. You should use the `auth` parameter of a client to add authorization headers to + every request created by a client. + + .. code-block:: php + + $client->setDefaultOption('auth', array('username', 'password', 'Basic|Digest|NTLM|Any')); + +If your web service client requires basic authorization, then you can use the CurlAuthPlugin to easily add an +Authorization header to each request sent by the client. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\CurlAuth\CurlAuthPlugin; + + $client = new Client('http://www.test.com/'); + + // Add the auth plugin to the client object + $authPlugin = new CurlAuthPlugin('username', 'password'); + $client->addSubscriber($authPlugin); + + $response = $client->get('projects/1/people')->send(); + $xml = new SimpleXMLElement($response->getBody(true)); + foreach ($xml->person as $person) { + echo $person->email . "\n"; + } diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst new file mode 100644 index 000000000..b96befe79 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst @@ -0,0 +1,24 @@ +============== +History plugin +============== + +The history plugin tracks all of the requests and responses sent through a request or client. This plugin can be +useful for crawling or unit testing. By default, the history plugin stores up to 10 requests and responses. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\History\HistoryPlugin; + + $client = new Client('http://www.test.com/'); + + // Add the history plugin to the client object + $history = new HistoryPlugin(); + $history->setLimit(5); + $client->addSubscriber($history); + + $client->get('http://www.yahoo.com/')->send(); + + echo $history->getLastRequest(); + echo $history->getLastResponse(); + echo count($history); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst new file mode 100644 index 000000000..3e2b22944 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst @@ -0,0 +1,69 @@ +========== +Log plugin +========== + +Use the ``Guzzle\Plugin\Log\LogPlugin`` to view all data sent over the wire, including entity bodies and redirects. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Log\Zf1LogAdapter; + use Guzzle\Plugin\Log\LogPlugin; + use Guzzle\Log\MessageFormatter; + + $client = new Client('http://www.test.com/'); + + $adapter = new Zf1LogAdapter( + new \Zend_Log(new \Zend_Log_Writer_Stream('php://output')) + ); + $logPlugin = new LogPlugin($adapter, MessageFormatter::DEBUG_FORMAT); + + // Attach the plugin to the client, which will in turn be attached to all + // requests generated by the client + $client->addSubscriber($logPlugin); + + $response = $client->get('http://google.com')->send(); + +The code sample above wraps a ``Zend_Log`` object using a ``Guzzle\Log\Zf1LogAdapter``. After attaching the plugin to +the client, all data sent over the wire will be logged to stdout. + +The first argument of the LogPlugin's constructor accepts a ``Guzzle\Log\LogAdapterInterface`` object. This object is +an adapter that allows you to use the logging capabilities of your favorite log implementation. The second argument of +the constructor accepts a ``Guzzle\Log\MessageFormatter`` or a log messaged format string. The format string uses +variable substitution and allows you to define the log data that is important to your application. The different +variables that can be injected are as follows: + +================== ==================================================================================== +Variable Substitution +================== ==================================================================================== +{request} Full HTTP request message +{response} Full HTTP response message +{ts} Timestamp +{host} Host of the request +{method} Method of the request +{url} URL of the request +{host} Host of the request +{protocol} Request protocol +{version} Protocol version +{resource} Resource of the request (path + query + fragment) +{port} Port of the request +{hostname} Hostname of the machine that sent the request +{code} Status code of the response (if available) +{phrase} Reason phrase of the response (if available) +{curl_error} Curl error message (if available) +{curl_code} Curl error code (if available) +{curl_stderr} Curl standard error (if available) +{connect_time} Time in seconds it took to establish the connection (if available) +{total_time} Total transaction time in seconds for last transfer (if available) +{req_header_*} Replace `*` with the lowercased name of a request header to add to the message +{res_header_*} Replace `*` with the lowercased name of a response header to add to the message +{req_body} Request body +{res_body} Response body +================== ==================================================================================== + +The LogPlugin has a helper method that can be used when debugging that will output the full HTTP request and +response of a transaction: + +.. code-block:: php + + $client->addSubscriber(LogPlugin::getDebugPlugin()); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst new file mode 100644 index 000000000..1b1cfa8a8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst @@ -0,0 +1,29 @@ +==================== +MD5 validator plugin +==================== + +Entity bodies can sometimes be modified over the wire due to a faulty TCP transport or misbehaving proxy. If an HTTP +response contains a Content-MD5 header, then a MD5 hash of the entity body of a response can be compared against the +Content-MD5 header of the response to determine if the response was delivered intact. The +``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` will throw an ``UnexpectedValueException`` if the calculated MD5 hash does +not match the Content-MD5 header value: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Md5\Md5ValidatorPlugin; + + $client = new Client('http://www.test.com/'); + + $md5Plugin = new Md5ValidatorPlugin(); + + // Add the md5 plugin to the client object + $client->addSubscriber($md5Plugin); + + $request = $client->get('http://www.yahoo.com/'); + $request->send(); + +Calculating the MD5 hash of a large entity body or an entity body that was transferred using a Content-Encoding is an +expensive operation. When working in high performance applications, you might consider skipping the MD5 hash +validation for entity bodies bigger than a certain size or Content-Encoded entity bodies +(see ``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` for more information). diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst new file mode 100644 index 000000000..4900cb565 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst @@ -0,0 +1,27 @@ +=========== +Mock plugin +=========== + +The mock plugin is useful for testing Guzzle clients. The mock plugin allows you to queue an array of responses that +will satisfy requests sent from a client by consuming the request queue in FIFO order. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Mock\MockPlugin; + use Guzzle\Http\Message\Response; + + $client = new Client('http://www.test.com/'); + + $mock = new MockPlugin(); + $mock->addResponse(new Response(200)) + ->addResponse(new Response(404)); + + // Add the mock plugin to the client object + $client->addSubscriber($mock); + + // The following request will receive a 200 response from the plugin + $client->get('http://www.example.com/')->send(); + + // The following request will receive a 404 response from the plugin + $client->get('http://www.test.com/')->send(); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst new file mode 100644 index 000000000..e67eabaa1 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst @@ -0,0 +1,30 @@ +============ +OAuth plugin +============ + +Guzzle ships with an OAuth 1.0 plugin that can sign requests using a consumer key, consumer secret, OAuth token, +and OAuth secret. Here's an example showing how to send an authenticated request to the Twitter REST API: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Oauth\OauthPlugin; + + $client = new Client('http://api.twitter.com/1'); + $oauth = new OauthPlugin(array( + 'consumer_key' => 'my_key', + 'consumer_secret' => 'my_secret', + 'token' => 'my_token', + 'token_secret' => 'my_token_secret' + )); + $client->addSubscriber($oauth); + + $response = $client->get('statuses/public_timeline.json')->send(); + +If you need to use a custom signing method, you can pass a ``signature_method`` configuration option in the +constructor of the OAuth plugin. The ``signature_method`` option must be a callable variable that accepts a string to +sign and signing key and returns a signed string. + +.. note:: + + You can omit the ``token`` and ``token_secret`` options to use two-legged OAuth. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc new file mode 100644 index 000000000..8d6d09b46 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc @@ -0,0 +1,9 @@ +* :doc:`/plugins/async-plugin` +* :doc:`/plugins/backoff-plugin` +* :doc:`/plugins/cache-plugin` +* :doc:`/plugins/cookie-plugin` +* :doc:`/plugins/history-plugin` +* :doc:`/plugins/log-plugin` +* :doc:`/plugins/md5-validator-plugin` +* :doc:`/plugins/mock-plugin` +* :doc:`/plugins/oauth-plugin` diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst new file mode 100644 index 000000000..19ae57ece --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst @@ -0,0 +1,59 @@ +====================== +Plugin system overview +====================== + +The workflow of sending a request and parsing a response is driven by Guzzle's event system, which is powered by the +`Symfony2 Event Dispatcher component `_. + +Any object in Guzzle that emits events will implement the ``Guzzle\Common\HasEventDispatcher`` interface. You can add +event subscribers directly to these objects using the ``addSubscriber()`` method, or you can grab the +``Symfony\Component\EventDispatcher\EventDispatcher`` object owned by the object using ``getEventDispatcher()`` and +add a listener or event subscriber. + +Adding event subscribers to clients +----------------------------------- + +Any event subscriber or event listener attached to the EventDispatcher of a ``Guzzle\Http\Client`` or +``Guzzle\Service\Client`` object will automatically be attached to all request objects created by the client. This +allows you to attach, for example, a HistoryPlugin to a client object, and from that point on, every request sent +through that client will utilize the HistoryPlugin. + +.. code-block:: php + + use Guzzle\Plugin\History\HistoryPlugin; + use Guzzle\Service\Client; + + $client = new Client(); + + // Create a history plugin and attach it to the client + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + // Create and send a request. This request will also utilize the HistoryPlugin + $client->get('http://httpbin.org')->send(); + + // Echo out the last sent request by the client + echo $history->getLastRequest(); + +.. tip:: + + :doc:`Create event subscribers `, or *plugins*, to implement reusable logic that can be + shared across clients. Event subscribers are also easier to test than anonymous functions. + +Pre-Built plugins +----------------- + +Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications +powered by the Symfony2 Event Dispatcher component. + +* :doc:`async-plugin` +* :doc:`backoff-plugin` +* :doc:`cache-plugin` +* :doc:`cookie-plugin` +* :doc:`curl-auth-plugin` +* :doc:`history-plugin` +* :doc:`log-plugin` +* :doc:`md5-validator-plugin` +* :doc:`mock-plugin` +* :doc:`oauth-plugin` + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/requirements.txt b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/requirements.txt new file mode 100644 index 000000000..f62e31837 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/requirements.txt @@ -0,0 +1,2 @@ +Sphinx>=1.2b1 +guzzle_sphinx_theme>=0.5.0 diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/testing/unit-testing.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/testing/unit-testing.rst new file mode 100644 index 000000000..f4297af30 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/testing/unit-testing.rst @@ -0,0 +1,201 @@ +=========================== +Unit Testing Guzzle clients +=========================== + +Guzzle provides several tools that will enable you to easily unit test your web service clients. + +* PHPUnit integration +* Mock responses +* node.js web server for integration testing + +PHPUnit integration +------------------- + +Guzzle is unit tested using `PHPUnit `_. Your web service client's unit tests should extend +``Guzzle\Tests\GuzzleTestCase`` so that you can take advantage of some of the built in helpers. + +In order to unit test your client, a developer would need to copy phpunit.xml.dist to phpunit.xml and make any needed +modifications. As a best practice and security measure for you and your contributors, it is recommended to add an +ignore statement to your SCM so that phpunit.xml is ignored. + +Bootstrapping +~~~~~~~~~~~~~ + +Your web service client should have a tests/ folder that contains a bootstrap.php file. The bootstrap.php file +responsible for autoloading and configuring a ``Guzzle\Service\Builder\ServiceBuilder`` that is used throughout your +unit tests for loading a configured client. You can add custom parameters to your phpunit.xml file that expects users +to provide the path to their configuration data. + +.. code-block:: php + + Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Aws\Common\Aws::factory($_SERVER['CONFIG'])); + + Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Guzzle\Service\Builder\ServiceBuilder::factory(array( + 'test.unfuddle' => array( + 'class' => 'Guzzle.Unfuddle.UnfuddleClient', + 'params' => array( + 'username' => 'test_user', + 'password' => '****', + 'subdomain' => 'test' + ) + ) + ))); + +The above code registers a service builder that can be used throughout your unit tests. You would then be able to +retrieve an instantiated and configured Unfuddle client by calling ``$this->getServiceBuilder()->get('test.unfuddle)``. +The above code assumes that ``$_SERVER['CONFIG']`` contains the path to a file that stores service description +configuration. + +Unit testing remote APIs +------------------------ + +Mock responses +~~~~~~~~~~~~~~ + +One of the benefits of unit testing is the ability to quickly determine if there are errors in your code. If your +unit tests run slowly, then they become tedious and will likely be run less frequently. Guzzle's philosophy on unit +testing web service clients is that no network access should be required to run the unit tests. This means that +responses are served from mock responses or local servers. By adhering to this principle, tests will run much faster +and will not require an external resource to be available. The problem with this approach is that your mock responses +must first be gathered and then subsequently updated each time the remote API changes. + +Integration testing over the internet +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can perform integration testing with a web service over the internet by making calls directly to the service. If +the web service you are requesting uses a complex signing algorithm or some other specific implementation, then you +may want to include at least one actual network test that can be run specifically through the command line using +`PHPUnit group annotations `_. + +@group internet annotation +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When creating tests that require an internet connection, it is recommended that you add ``@group internet`` annotations +to your unit tests to specify which tests require network connectivity. + +You can then `run PHPUnit tests `_ that exclude the @internet +group by running ``phpunit --exclude-group internet``. + +API credentials +^^^^^^^^^^^^^^^ + +If API credentials are required to run your integration tests, you must add ```` parameters to your +phpunit.xml.dist file and extract these parameters in your bootstrap.php file. + +.. code-block:: xml + + + + + + + + + + + + + ./Tests + + + + +You can then extract the ``server`` variables in your bootstrap.php file by grabbing them from the ``$_SERVER`` +superglobal: ``$apiUser = $_SERVER['API_USER'];`` + +Further reading +^^^^^^^^^^^^^^^ + +A good discussion on the topic of testing remote APIs can be found in Sebastian Bergmann's +`Real-World Solutions for Developing High-Quality PHP Frameworks and Applications `_. + +Queueing Mock responses +----------------------- + +Mock responses can be used to test if requests are being generated correctly and responses and handled correctly by +your client. Mock responses can be queued up for a client using the ``$this->setMockResponse($client, $path)`` method +of your test class. Pass the client you are adding mock responses to and a single path or array of paths to mock +response files relative to the ``/tests/mock/ folder``. This will queue one or more mock responses for your client by +creating a simple observer on the client. Mock response files must contain a full HTTP response message: + +.. code-block:: none + + HTTP/1.1 200 OK + Date: Wed, 25 Nov 2009 12:00:00 GMT + Connection: close + Server: AmazonS3 + Content-Type: application/xml + + + EU + +After queuing mock responses for a client, you can get an array of the requests that were sent by the client that +were issued a mock response by calling ``$this->getMockedRequests()``. + +You can also use the ``Guzzle\Plugin\Mock\MockPlugin`` object directly with your clients. + +.. code-block:: php + + $plugin = new Guzzle\Plugin\Mock\MockPlugin(); + $plugin->addResponse(new Guzzle\Http\Message\Response(200)); + $client = new Guzzle\Http\Client(); + $client->addSubscriber($plugin); + + // The following request will get the mock response from the plugin in FIFO order + $request = $client->get('http://www.test.com/'); + $request->send(); + + // The MockPlugin maintains a list of requests that were mocked + $this->assertContainsOnly($request, $plugin->getReceivedRequests()); + +node.js web server for integration testing +------------------------------------------ + +Using mock responses is usually enough when testing a web service client. If your client needs to add custom cURL +options to requests, then you should use the node.js test web server to ensure that your HTTP request message is +being created correctly. + +Guzzle is based around PHP's libcurl bindings. cURL sometimes modifies an HTTP request message based on +``CURLOPT_*`` options. Headers that are added to your request by cURL will not be accounted for if you inject mock +responses into your tests. Additionally, some request entity bodies cannot be loaded by the client before transmitting +it to the sever (for example, when using a client as a sort of proxy and streaming content from a remote server). You +might also need to inspect the entity body of a ``multipart/form-data`` POST request. + +.. note:: + + You can skip all of the tests that require the node.js test web server by excluding the ``server`` group: + ``phpunit --exclude-group server`` + +Using the test server +~~~~~~~~~~~~~~~~~~~~~ + +The node.js test server receives requests and returns queued responses. The test server exposes a simple API that is +used to enqueue responses and inspect the requests that it has received. + +Retrieve the server object by calling ``$this->getServer()``. If the node.js server is not running, it will be +started as a forked process and an object that interfaces with the server will be returned. (note: stopping the +server is handled internally by Guzzle.) + +You can queue an HTTP response or an array of responses by calling ``$this->getServer()->enqueue()``: + +.. code-block:: php + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + +The above code queues a single 200 response with an empty body. Responses are queued using a FIFO order; this +response will be returned by the server when it receives the first request and then removed from the queue. If a +request is received by a server with no queued responses, an exception will be thrown in your unit test. + +You can inspect the requests that the server has retrieved by calling ``$this->getServer()->getReceivedRequests()``. +This method accepts an optional ``$hydrate`` parameter that specifies if you are retrieving an array of string HTTP +requests or an array of ``Guzzle\Http\RequestInterface`` subclassed objects. "Hydrating" the requests will allow +greater flexibility in your unit tests so that you can easily assert the state of the various parts of a request. + +You will need to modify the base_url of your web service client in order to use it against the test server. + +.. code-block:: php + + $client = $this->getServiceBuilder()->get('my_client'); + $client->setBaseUrl($this->getServer()->getUrl()); + +After running the above code, all calls made from the ``$client`` object will be sent to the test web server. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst new file mode 100644 index 000000000..ad6070b27 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst @@ -0,0 +1,619 @@ +=========================== +Guzzle service descriptions +=========================== + +Guzzle allows you to serialize HTTP requests and parse HTTP responses using a DSL called a service descriptions. +Service descriptions define web service APIs by documenting each operation, the operation's parameters, validation +options for each parameter, an operation's response, how the response is parsed, and any errors that can be raised for +an operation. Writing a service description for a web service allows you to more quickly consume a web service than +writing concrete commands for each web service operation. + +Guzzle service descriptions can be representing using a PHP array or JSON document. Guzzle's service descriptions are +heavily inspired by `Swagger `_. + +Service description schema +========================== + +A Guzzle Service description must match the following JSON schema document. This document can also serve as a guide when +implementing a Guzzle service description. + +Download the schema here: :download:`Guzzle JSON schema document ` + +.. class:: overflow-height-500px + + .. literalinclude:: ../_downloads/guzzle-schema-1.0.json + :language: json + +Top-level attributes +-------------------- + +Service descriptions are comprised of the following top-level attributes: + +.. code-block:: json + + { + "name": "string", + "apiVersion": "string|number", + "baseUrl": "string", + "description": "string", + "operations": {}, + "models": {}, + "includes": ["string.php", "string.json"] + } + ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| Property Name | Value | Description | ++=========================================+=========================+=======================================================================================================================+ +| name | string | Name of the web service | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| apiVersion | string|number | Version identifier that the service description is compatible with | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| baseUrl or basePath | string | Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the | +| | | process defined in RFC 2396. Some clients require custom logic to determine the baseUrl. In those cases, it is best | +| | | to not include a baseUrl in the service description, but rather allow the factory method of the client to configure | +| | | the client’s baseUrl. | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| description | string | Short summary of the web service | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| operations | object containing | Operations of the service. The key is the name of the operation and value is the attributes of the operation. | +| | :ref:`operation-schema` | | +| | | | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| models | object containing | Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP | +| | :ref:`model-schema` | response is parsed into a ``Guzzle\Service\Resource\Model`` object when an operation uses a ``model`` ``responseType``| ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| includes | array of .js, | Service description files to include and extend from (can be a .json, .js, or .php file) | +| | .json, or .php | | +| | files. | | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| (any additional properties) | mixed | Any additional properties specified as top-level attributes are allowed and will be treated as arbitrary data | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ + +.. _operation-schema: + +Operations +---------- + +Operations are the actions that can be taken on a service. Each operation is given a unique name and has a distinct +endpoint and HTTP method. If an API has a ``DELETE /users/:id`` operation, a satisfactory operation name might be +``DeleteUser`` with a parameter of ``id`` that is inserted into the URI. + +.. class:: overflow-height-250px + + .. code-block:: json + + { + "operations": { + "operationName": { + "extends": "string", + "httpMethod": "GET|POST|PUT|DELETE|PATCH|string", + "uri": "string", + "summary": "string", + "class": "string", + "responseClass": "string", + "responseNotes": "string", + "type": "string", + "description": "string", + "responseType": "primitive|class|(model by name)|documentation|(string)", + "deprecated": false, + "errorResponses": [ + { + "code": 500, + "reason": "Unexpected Error", + "class": "string" + } + ], + "data": { + "foo": "bar", + "baz": "bam" + }, + "parameters": {} + } + } + } + +.. csv-table:: + :header: "Property Name", "Value", "Description" + :widths: 20, 15, 65 + + "extends", "string", "Extend from another operation by name. The parent operation must be defined before the child." + "httpMethod", "string", "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" + "uri", "string", "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" + "summary", "string", "Short summary of what the operation does" + "class", "string", "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand. Using this attribute allows you to define an operation using a service description, but allows more customized logic to be implemented in user-land code." + "responseClass", "string", "Defined what is returned from the method. Can be a primitive, class name, or model name. You can specify the name of a class to return a more customized result from the operation (for example, a domain model object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``." + "responseNotes", "string", "A description of the response returned by the operation" + "responseType", "string", "The type of response that the operation creates: one of primitive, class, model, or documentation. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default." + "deprecated", "boolean", "Whether or not the operation is deprecated" + "errorResponses", "array", "Errors that could occur while executing the operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), 'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this error is encountered)" + "data", "object", "Any arbitrary data to associate with the operation" + "parameters", "object containing :ref:`parameter-schema` objects", "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." + "additionalParameters", "A single :ref:`parameter-schema` object", "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." + +additionalParameters +~~~~~~~~~~~~~~~~~~~~ + +When a webservice offers a large number of parameters that all are set in the same location (for example the query +string or a JSON document), defining each parameter individually can require a lot of time and repetition. Furthermore, +some web services allow for completely arbitrary parameters to be supplied for an operation. The +``additionalParameters`` attribute can be used to solve both of these issues. + +As an example, we can define a Twitter API operation quite easily using ``additionalParameters``. The +GetMentions operation accepts a large number of query string parameters. Defining each of these parameters +is ideal because it provide much more introspection for the client and opens the possibility to use the description with +other tools (e.g. a documentation generator). However, you can very quickly provide a "catch-all" serialization rule +that will place any custom parameters supplied to an operation the generated request's query string parameters. + +.. class:: overflow-height-250px + + .. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "responseClass": "GetMentionsOutput", + "additionalParameters": { + "location": "query" + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +responseClass +~~~~~~~~~~~~~ + +The ``responseClass`` attribute is used to define the return value of an operation (what is returned by calling the +``getResult()`` method of a command object). The value set in the responseClass attribute can be one of "primitive" +(meaning the result with be primitive type like a string), a class name meaning the result will be an instance of a +specific user-land class, or a model name meaning the result will be a ``Guzzle\Service\Resource\Model`` object that +uses a :ref:`model schema ` to define how the HTTP response is parsed. + +.. note:: + + Using a class name with a ``responseClass`` will only work if it is supported by the ``class`` that is instantiated + for the operation. Keep this in mind when specifying a custom ``class`` attribute that points to a custom + ``Guzzle\Service\Command\CommandInterface`` class. The default ``class``, + ``Guzzle\Service\Command\OperationCommand``, does support setting custom ``class`` attributes. + +You can specify the name of a class to return a more customized result from the operation (for example, a domain model +object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``. +Here's a very simple example of implementing a custom responseClass object. + +.. code-block:: json + + { + "operations": { + "test": { + "responseClass": "MyApplication\\User" + } + } + } + +.. code-block:: php + + namespace MyApplication; + + use Guzzle\Service\Command\ResponseClassInterface; + use Guzzle\Service\Command\OperationCommand; + + class User implements ResponseClassInterface + { + protected $name; + + public static function fromCommand(OperationCommand $command) + { + $response = $command->getResponse(); + $xml = $response->xml(); + + return new self((string) $xml->name); + } + + public function __construct($name) + { + $this->name = $name; + } + } + +errorResponses +~~~~~~~~~~~~~~ + +``errorResponses`` is an array containing objects that define the errors that could occur while executing the +operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), +'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this +error is encountered). + +ErrorResponsePlugin +^^^^^^^^^^^^^^^^^^^ + +Error responses are by default only used for documentation. If you don't need very complex exception logic for your web +service errors, then you can use the ``Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin`` to automatically throw defined +exceptions when one of the ``errorResponse`` rules are matched. The error response plugin will listen for the +``request.complete`` event of a request created by a command object. Every response (including a successful response) is +checked against the list of error responses for an exact match using the following order of checks: + +1. Does the errorResponse have a defined ``class``? +2. Is the errorResponse ``code`` equal to the status code of the response? +3. Is the errorResponse ``reason`` equal to the reason phrase of the response? +4. Throw the exception stored in the ``class`` attribute of the errorResponse. + +The ``class`` attribute must point to a class that implements +``Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface``. This interface requires that an error response class +implements ``public static function fromCommand(CommandInterface $command, Response $response)``. This method must +return an object that extends from ``\Exception``. After an exception is returned, it is thrown by the plugin. + +.. _parameter-schema: + +Parameter schema +---------------- + +Parameters in both operations and models are represented using the +`JSON schema `_ syntax. + +.. csv-table:: + :header: "Property Name", "Value", "Description" + :widths: 20, 15, 65 + + "name", "string", "Unique name of the parameter" + "type", "string|array", "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." + "instanceOf", "string", "When the type is an object, you can specify the class that the object must implement" + "required", "boolean", "Whether or not the parameter is required" + "default", "mixed", "Default value to use if no value is supplied" + "static", "boolean", "Set to true to specify that the parameter value cannot be changed from the default setting" + "description", "string", "Documentation of the parameter" + "location", "string", "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" + "sentAs", "string", "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." + "filters", "array", "Array of functions to to run a parameter value through." + +filters +~~~~~~~ + +Each value in the array must be a string containing the full class path to a static method or an array of complex +filter information. You can specify static methods of classes using the full namespace class name followed by +"::" (e.g. ``FooBar::baz()``). Some filters require arguments in order to properly filter a value. For complex filters, +use an object containing a ``method`` attribute pointing to a function, and an ``args`` attribute containing an +array of positional arguments to pass to the function. Arguments can contain keywords that are replaced when filtering +a value: ``@value`` is replaced with the value being filtered, and ``@api`` is replaced with the actual Parameter +object. + +.. code-block:: json + + { + "filters": [ + "strtolower", + { + "method": "MyClass::convertString", + "args": [ "test", "@value", "@api" ] + } + ] + } + +The above example will filter a parameter using ``strtolower``. It will then call the ``convertString`` static method +of ``MyClass``, passing in "test", the actual value of the parameter, and a ``Guzzle\Service\Description\Parameter`` +object. + +Operation parameter location attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The location field of top-level parameters control how a parameter is serialized when generating a request. + +uri location +^^^^^^^^^^^^ + +Parameters are injected into the ``uri`` attribute of the operation using +`URI-template expansion `_. + +.. code-block:: json + + { + "operations": { + "uriTest": { + "uri": "/test/{testValue}", + "parameters": { + "testValue": { + "location": "uri" + } + } + } + } + } + +query location +^^^^^^^^^^^^^^ + +Parameters are injected into the query string of a request. Query values can be nested, which would result in a PHP +style nested query string. The name of a parameter is the default name of the query string parameter added to the +request. You can override this behavior by specifying the ``sentAs`` attribute on the parameter. + +.. code-block:: json + + { + "operations": { + "queryTest": { + "parameters": { + "testValue": { + "location": "query", + "sentAs": "test_value" + } + } + } + } + } + +header location +^^^^^^^^^^^^^^^ + +Parameters are injected as headers on an HTTP request. The name of the parameter is used as the name of the header by +default. You can change the name of the header created by the parameter using the ``sentAs`` attribute. + +Headers that are of type ``object`` will be added as multiple headers to a request using the key of the input array as +the header key. Setting a ``sentAs`` attribute along with a type ``object`` will use the value of ``sentAs`` as a +prefix for each header key. + +body location +^^^^^^^^^^^^^ + +Parameters are injected as the body of a request. The input of these parameters may be anything that can be cast to a +string or a ``Guzzle\Http\EntityBodyInterface`` object. + +postField location +^^^^^^^^^^^^^^^^^^ + +Parameters are inserted as POST fields in a request. Nested values may be supplied and will be represented using +PHP style nested query strings. The POST field name is the same as the parameter name by default. You can use the +``sentAs`` parameter to override the POST field name. + +postFile location +^^^^^^^^^^^^^^^^^ + +Parameters are added as POST files. A postFile value may be a string pointing to a local filename or a +``Guzzle\Http\Message\PostFileInterface`` object. The name of the POST file will be the name of the parameter by +default. You can use a custom POST file name by using the ``sentAs`` attribute. + +Supports "string" and "array" types. + +json location +^^^^^^^^^^^^^ + +Parameters are added to the body of a request as top level keys of a JSON document. Nested values may be specified, +with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When JSON parameters are specified, the +``Content-Type`` of the request will change to ``application/json`` if a ``Content-Type`` has not already been specified +on the request. + +xml location +^^^^^^^^^^^^ + +Parameters are added to the body of a request as top level nodes of an XML document. Nested values may be specified, +with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When XML parameters are specified, the +``Content-Type`` of the request will change to ``application/xml`` if a ``Content-Type`` has not already been specified +on the request. + +responseBody location +^^^^^^^^^^^^^^^^^^^^^ + +Specifies the EntityBody of a response. This can be used to download the response body to a file or a custom Guzzle +EntityBody object. + +No location +^^^^^^^^^^^ + +If a parameter has no location attribute, then the parameter is simply used as a data value. + +Other locations +^^^^^^^^^^^^^^^ + +Custom locations can be registered as new locations or override default locations if needed. + +.. _model-schema: + +Model Schema +------------ + +Models are used in service descriptions to provide generic JSON schema definitions that can be extended from or used in +``$ref`` attributes. Models can also be referenced in a ``responseClass`` attribute to provide valuable output to an +operation. Models are JSON schema documents and use the exact syntax and attributes used in parameters. + +Response Models +~~~~~~~~~~~~~~~ + +Response models describe how a response is parsed into a ``Guzzle\Service\Resource\Model`` object. Response models are +always modeled as JSON schema objects. When an HTTP response is parsed using a response model, the rules specified on +each property of a response model will translate 1:1 as keys in a PHP associative array. When a ``sentAs`` attribute is +found in response model parameters, the value retrieved from the HTTP response is retrieved using the ``sentAs`` +parameter but stored in the response model using the name of the parameter. + +The location field of top-level parameters in a response model tell response parsers how data is retrieved from a +response. + +statusCode location +^^^^^^^^^^^^^^^^^^^ + +Retrieves the status code of the response. + +reasonPhrase location +^^^^^^^^^^^^^^^^^^^^^ + +Retrieves the reason phrase of the response. + +header location +^^^^^^^^^^^^^^^ + +Retrieves a header from the HTTP response. + +body location +^^^^^^^^^^^^^ + +Retrieves the body of an HTTP response. + +json location +^^^^^^^^^^^^^ + +Retrieves a top-level parameter from a JSON document contained in an HTTP response. + +You can use ``additionalProperties`` if the JSON document is wrapped in an outer array. This allows you to parse the +contents of each item in the array using the parsing rules defined in the ``additionalProperties`` schema. + +xml location +^^^^^^^^^^^^ + +Retrieves a top-level node value from an XML document contained in an HTTP response. + +Other locations +^^^^^^^^^^^^^^^ + +Custom locations can be registered as new locations or override default locations if needed. + +Example service description +--------------------------- + +Let's say you're interacting with a web service called 'Foo' that allows for the following routes and methods:: + + GET/POST /users + GET/DELETE /users/:id + +The following JSON service description implements this simple web service: + +.. class:: overflow-height-500px + + .. code-block:: json + + { + "name": "Foo", + "apiVersion": "2012-10-14", + "baseUrl": "http://api.foo.com", + "description": "Foo is an API that allows you to Baz Bar", + "operations": { + "GetUsers": { + "httpMethod": "GET", + "uri": "/users", + "summary": "Gets a list of users", + "responseClass": "GetUsersOutput" + }, + "CreateUser": { + "httpMethod": "POST", + "uri": "/users", + "summary": "Creates a new user", + "responseClass": "CreateUserOutput", + "parameters": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + }, + "GetUser": { + "httpMethod": "GET", + "uri": "/users/{id}", + "summary": "Retrieves a single user", + "responseClass": "GetUserOutput", + "parameters": { + "id": { + "location": "uri", + "description": "User to retrieve by ID", + "required": true + } + } + }, + "DeleteUser": { + "httpMethod": "DELETE", + "uri": "/users/{id}", + "summary": "Deletes a user", + "responseClass": "DeleteUserOutput", + "parameters": { + "id": { + "location": "uri", + "description": "User to delete by ID", + "required": true + } + } + } + }, + "models": { + "GetUsersOutput": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + } + }, + "CreateUserOutput": { + "type": "object", + "properties": { + "id": { + "location": "json", + "type": "string" + }, + "location": { + "location": "header", + "sentAs": "Location", + "type": "string" + } + } + }, + "GetUserOutput": { + "type": "object", + "properties": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + }, + "DeleteUserOutput": { + "type": "object", + "properties": { + "status": { + "location": "statusCode", + "type": "integer" + } + } + } + } + } + +If you attach this service description to a client, you would completely configure the client to interact with the +Foo web service and provide valuable response models for each operation. + +.. code-block:: php + + use Guzzle\Service\Description\ServiceDescription; + + $description = ServiceDescription::factory('/path/to/client.json'); + $client->setDescription($description); + + $command = $client->getCommand('DeleteUser', array('id' => 123)); + $responseModel = $client->execute($command); + echo $responseModel['status']; + +.. note:: + + You can add the service description to your client's factory method or constructor. diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst new file mode 100644 index 000000000..b7113d68b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst @@ -0,0 +1,316 @@ +======================= +Using a service builder +======================= + +The best way to instantiate Guzzle web service clients is to let Guzzle handle building the clients for you using a +ServiceBuilder. A ServiceBuilder is responsible for creating concrete client objects based on configuration settings +and helps to manage credentials for different environments. + +You don't have to use a service builder, but they help to decouple your application from concrete classes and help to +share configuration data across multiple clients. Consider the following example. Here we are creating two clients that +require the same API public key and secret key. The clients are created using their ``factory()`` methods. + +.. code-block:: php + + use MyService\FooClient; + use MyService\BarClient; + + $foo = FooClient::factory(array( + 'key' => 'abc', + 'secret' => '123', + 'custom' => 'and above all' + )); + + $bar = BarClient::factory(array( + 'key' => 'abc', + 'secret' => '123', + 'custom' => 'listen to me' + )); + +The redundant specification of the API keys can be removed using a service builder. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + $builder = ServiceBuilder::factory(array( + 'services' => array( + 'abstract_client' => array( + 'params' => array( + 'key' => 'abc', + 'secret' => '123' + ) + ), + 'foo' => array( + 'extends' => 'abstract_client', + 'class' => 'MyService\FooClient', + 'params' => array( + 'custom' => 'and above all' + ) + ), + 'bar' => array( + 'extends' => 'abstract_client', + 'class' => 'MyService\FooClient', + 'params' => array( + 'custom' => 'listen to me' + ) + ) + ) + )); + + $foo = $builder->get('foo'); + $bar = $builder->get('bar'); + +You can make managing your API keys even easier by saving the service builder configuration in a JSON format in a +.json file. + +Creating a service builder +-------------------------- + +A ServiceBuilder can source information from an array, an PHP include file that returns an array, or a JSON file. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + // Source service definitions from a JSON file + $builder = ServiceBuilder::factory('services.json'); + +Sourcing data from an array +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Data can be source from a PHP array. The array must contain an associative ``services`` array that maps the name of a +client to the configuration information used by the service builder to create the client. Clients are given names +which are used to identify how a client is retrieved from a service builder. This can be useful for using multiple +accounts for the same service or creating development clients vs. production clients. + +.. code-block:: php + + $services = array( + 'includes' => array( + '/path/to/other/services.json', + '/path/to/other/php_services.php' + ), + 'services' => array( + 'abstract.foo' => array( + 'params' => array( + 'username' => 'foo', + 'password' => 'bar' + ) + ), + 'bar' => array( + 'extends' => 'abstract.foo', + 'class' => 'MyClientClass', + 'params' => array( + 'other' => 'abc' + ) + ) + ) + ); + +A service builder configuration array contains two top-level array keys: + ++------------+---------------------------------------------------------------------------------------------------------+ +| Key | Description | ++============+=========================================================================================================+ +| includes | Array of paths to JSON or PHP include files to include in the configuration. | ++------------+---------------------------------------------------------------------------------------------------------+ +| services | Associative array of defined services that can be created by the service builder. Each service can | +| | contain the following keys: | +| | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | Key | Description | | +| | +============+========================================================================================+ | +| | | class | The concrete class to instantiate that implements the | | +| | | | ``Guzzle\Common\FromConfigInterface``. | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | extends | The name of a previously defined service to extend from | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | params | Associative array of parameters to pass to the factory method of the service it is | | +| | | | instantiated | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | alias | An alias that can be used in addition to the array key for retrieving a client from | | +| | | | the service builder. | | +| | +------------+----------------------------------------------------------------------------------------+ | ++------------+---------------------------------------------------------------------------------------------------------+ + +The first client defined, ``abstract.foo``, is used as a placeholder of shared configuration values. Any service +extending abstract.foo will inherit its params. As an example, this can be useful when clients share the same username +and password. + +The next client, ``bar``, extends from ``abstract.foo`` using the ``extends`` attribute referencing the client from +which to extend. Additional parameters can be merged into the original service definition when extending a parent +service. + +.. important:: + + Each client that you intend to instantiate must specify a ``class`` attribute that references the full class name + of the client being created. The class referenced in the ``class`` parameter must implement a static ``factory()`` + method that accepts an array or ``Guzzle\Common\Collection`` object and returns an instantiated object. + +Sourcing from a PHP include +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create service builder configurations using a PHP include file. This can be useful if you wish to take +advantage of an opcode cache like APC to speed up the process of loading and processing the configuration. The PHP +include file is the same format as an array, but you simply create a PHP script that returns an array and save the +file with the .php file extension. + +.. code-block:: php + + '...'); + // Saved as config.php + +This configuration file can then be used with a service builder. + +.. code-block:: php + + $builder = ServiceBuilder::factory('/path/to/config.php'); + +Sourcing from a JSON document +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use JSON documents to serialize your service descriptions. The JSON format uses the exact same structure as +the PHP array syntax, but it's just serialized using JSON. + +.. code-block:: javascript + + { + "includes": ["/path/to/other/services.json", "/path/to/other/php_services.php"], + "services": { + "abstract.foo": { + "params": { + "username": "foo", + "password": "bar" + } + }, + "bar": { + "extends": "abstract.foo", + "class": "MyClientClass", + "params": { + "other": "abc" + } + } + } + } + +Referencing other clients in parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If one of your clients depends on another client as one of its parameters, you can reference that client by name by +enclosing the client's reference key in ``{}``. + +.. code-block:: javascript + + { + "services": { + "token": { + "class": "My\Token\TokenFactory", + "params": { + "access_key": "xyz" + } + }, + "client": { + "class": "My\Client", + "params": { + "token_client": "{token}", + "version": "1.0" + } + } + } + } + +When ``client`` is constructed by the service builder, the service builder will first create the ``token`` service +and then inject the token service into ``client``'s factory method in the ``token_client`` parameter. + +Retrieving clients from a service builder +----------------------------------------- + +Clients are referenced using a customizable name you provide in your service definition. The ServiceBuilder is a sort +of multiton object-- it will only instantiate a client once and return that client for subsequent retrievals. Clients +are retrieved by name (the array key used in the configuration) or by the ``alias`` setting of a service. + +Here's an example of retrieving a client from your ServiceBuilder: + +.. code-block:: php + + $client = $builder->get('foo'); + + // You can also use the ServiceBuilder object as an array + $client = $builder['foo']; + +Creating throwaway clients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get a "throwaway" client (a client that is not persisted by the ServiceBuilder) by passing ``true`` in the +second argument of ``ServiceBuilder::get()``. This allows you to create a client that will not be returned by other +parts of your code that use the service builder. Instead of passing ``true``, you can pass an array of configuration +settings that will override the configuration settings specified in the service builder. + +.. code-block:: php + + // Get a throwaway client and overwrite the "custom" setting of the client + $foo = $builder->get('foo', array( + 'custom' => 'in this world there are rules' + )); + +Getting raw configuration settings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get the raw configuration settings provided to the service builder for a specific service using the +``getData($name)`` method of a service builder. This method will null if the service was not found in the service +builder or an array of configuration settings if the service was found. + +.. code-block:: php + + $data = $builder->getData('foo'); + echo $data['key'] . "\n"; + echo $data['secret'] . "\n"; + echo $data['custom'] . "\n"; + +Adding a plugin to all clients +------------------------------ + +You can add a plugin to all clients created by a service builder using the ``addGlobalPlugin($plugin)`` method of a +service builder and passing a ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` object. The service builder +will then attach each global plugin to every client as it is created. This allows you to, for example, add a LogPlugin +to every request created by a service builder for easy debugging. + +.. code-block:: php + + use Guzzle\Plugin\Log\LogPlugin; + + // Add a debug log plugin to every client as it is created + $builder->addGlobalPlugin(LogPlugin::getDebugPlugin()); + + $foo = $builder->get('foo'); + $foo->get('/')->send(); + // Should output all of the data sent over the wire + +.. _service-builder-events: + +Events emitted from a service builder +------------------------------------- + +A ``Guzzle\Service\Builder\ServiceBuilder`` object emits the following events: + ++-------------------------------+--------------------------------------------+-----------------------------------------+ +| Event name | Description | Event data | ++===============================+============================================+=========================================+ +| service_builder.create_client | Called when a client is created | * client: The created client object | ++-------------------------------+--------------------------------------------+-----------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Service\Builder\ServiceBuilder; + + $builder = ServiceBuilder::factory('/path/to/config.json'); + + // Add an event listener to print out each client client as it is created + $builder->getEventDispatcher()->addListener('service_builder.create_client', function (Event $e) { + echo 'Client created: ' . get_class($e['client']) . "\n"; + }); + + $foo = $builder->get('foo'); + // Should output the class used for the "foo" client diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst new file mode 100644 index 000000000..7ec771e1c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst @@ -0,0 +1,659 @@ +====================== +The web service client +====================== + +The ``Guzzle\Service`` namespace contains various abstractions that help to make it easier to interact with a web +service API, including commands, service descriptions, and resource iterators. + +In this chapter, we'll build a simple `Twitter API client `_. + +Creating a client +================= + +A class that extends from ``Guzzle\Service\Client`` or implements ``Guzzle\Service\ClientInterface`` must implement a +``factory()`` method in order to be used with a :doc:`service builder `. + +Factory method +-------------- + +You can use the ``factory()`` method of a client directly if you do not need a service builder. + +.. code-block:: php + + use mtdowling\TwitterClient; + + // Create a client and pass an array of configuration data + $twitter = TwitterClient::factory(array( + 'consumer_key' => '****', + 'consumer_secret' => '****', + 'token' => '****', + 'token_secret' => '****' + )); + +.. note:: + + If you'd like to follow along, here's how to get your Twitter API credentials: + + 1. Visit https://dev.twitter.com/apps + 2. Click on an application that you've created + 3. Click on the "OAuth tool" tab + 4. Copy all of the settings under "OAuth Settings" + +Implementing a factory method +----------------------------- + +Creating a client and its factory method is pretty simple. You just need to implement ``Guzzle\Service\ClientInterface`` +or extend from ``Guzzle\Service\Client``. + +.. code-block:: php + + namespace mtdowling; + + use Guzzle\Common\Collection; + use Guzzle\Plugin\Oauth\OauthPlugin; + use Guzzle\Service\Client; + use Guzzle\Service\Description\ServiceDescription; + + /** + * A simple Twitter API client + */ + class TwitterClient extends Client + { + public static function factory($config = array()) + { + // Provide a hash of default client configuration options + $default = array('base_url' => 'https://api.twitter.com/1.1'); + + // The following values are required when creating the client + $required = array( + 'base_url', + 'consumer_key', + 'consumer_secret', + 'token', + 'token_secret' + ); + + // Merge in default settings and validate the config + $config = Collection::fromConfig($config, $default, $required); + + // Create a new Twitter client + $client = new self($config->get('base_url'), $config); + + // Ensure that the OauthPlugin is attached to the client + $client->addSubscriber(new OauthPlugin($config->toArray())); + + return $client; + } + } + +Service Builder +--------------- + +A service builder is used to easily create web service clients, provides a simple configuration driven approach to +creating clients, and allows you to share configuration settings across multiple clients. You can find out more about +Guzzle's service builder in :doc:`using-the-service-builder`. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + // Create a service builder and provide client configuration data + $builder = ServiceBuilder::factory('/path/to/client_config.json'); + + // Get the client from the service builder by name + $twitter = $builder->get('twitter'); + +The above example assumes you have JSON data similar to the following stored in "/path/to/client_config.json": + +.. code-block:: json + + { + "services": { + "twitter": { + "class": "mtdowling\\TwitterClient", + "params": { + "consumer_key": "****", + "consumer_secret": "****", + "token": "****", + "token_secret": "****" + } + } + } + } + +.. note:: + + A service builder becomes much more valuable when using multiple web service clients in a single application or + if you need to utilize the same client with varying configuration settings (e.g. multiple accounts). + +Commands +======== + +Commands are a concept in Guzzle that helps to hide the underlying implementation of an API by providing an easy to use +parameter driven object for each action of an API. A command is responsible for accepting an array of configuration +parameters, serializing an HTTP request, and parsing an HTTP response. Following the +`command pattern `_, commands in Guzzle offer a greater level of +flexibility when implementing and utilizing a web service client. + +Executing commands +------------------ + +You must explicitly execute a command after creating a command using the ``getCommand()`` method. A command has an +``execute()`` method that may be called, or you can use the ``execute()`` method of a client object and pass in the +command object. Calling either of these execute methods will return the result value of the command. The result value is +the result of parsing the HTTP response with the ``process()`` method. + +.. code-block:: php + + // Get a command from the client and pass an array of parameters + $command = $twitter->getCommand('getMentions', array( + 'count' => 5 + )); + + // Other parameters can be set on the command after it is created + $command['trim_user'] = false; + + // Execute the command using the command object. + // The result value contains an array of JSON data from the response + $result = $command->execute(); + + // You can retrieve the result of the command later too + $result = $command->getResult(). + +Command object also contains methods that allow you to inspect the HTTP request and response that was utilized with +the command. + +.. code-block:: php + + $request = $command->getRequest(); + $response = $command->getResponse(); + +.. note:: + + The format and notation used to retrieve commands from a client can be customized by injecting a custom command + factory, ``Guzzle\Service\Command\Factory\FactoryInterface``, on the client using ``$client->setCommandFactory()``. + +Executing with magic methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When using method missing magic methods with a command, the command will be executed right away and the result of the +command is returned. + +.. code-block:: php + + $jsonData = $twitter->getMentions(array( + 'count' => 5, + 'trim_user' => true + )); + +Creating commands +----------------- + +Commands are created using either the ``getCommand()`` method of a client or a magic missing method of a client. Using +the ``getCommand()`` method allows you to create a command without executing it, allowing for customization of the +command or the request serialized by the command. + +When a client attempts to create a command, it uses the client's ``Guzzle\Service\Command\Factory\FactoryInterface``. +By default, Guzzle will utilize a command factory that first looks for a concrete class for a particular command +(concrete commands) followed by a command defined by a service description (operation commands). We'll learn more about +concrete commands and operation commands later in this chapter. + +.. code-block:: php + + // Get a command from the twitter client. + $command = $twitter->getCommand('getMentions'); + $result = $command->execute(); + +Unless you've skipped ahead, running the above code will throw an exception. + + PHP Fatal error: Uncaught exception 'Guzzle\Common\Exception\InvalidArgumentException' with message + 'Command was not found matching getMentions' + +This exception was thrown because the "getMentions" command has not yet been implemented. Let's implement one now. + +Concrete commands +~~~~~~~~~~~~~~~~~ + +Commands can be created in one of two ways: create a concrete command class that extends +``Guzzle\Service\Command\AbstractCommand`` or +:doc:`create an OperationCommand based on a service description `. The recommended +approach is to use a service description to define your web service, but you can use concrete commands when custom +logic must be implemented for marshaling or unmarshaling a HTTP message. + +Commands are the method in which you abstract away the underlying format of the requests that need to be sent to take +action on a web service. Commands in Guzzle are meant to be built by executing a series of setter methods on a command +object. Commands are only validated right before they are executed. A ``Guzzle\Service\Client`` object is responsible +for executing commands. Commands created for your web service must implement +``Guzzle\Service\Command\CommandInterface``, but it's easier to extend the ``Guzzle\Service\Command\AbstractCommand`` +class, implement the ``build()`` method, and optionally implement the ``process()`` method. + +Serializing requests +^^^^^^^^^^^^^^^^^^^^ + +The ``build()`` method of a command is responsible for using the arguments of the command to build and serialize a +HTTP request and set the request on the ``$request`` property of the command object. This step is usually taken care of +for you when using a service description driven command that uses the default +``Guzzle\Service\Command\OperationCommand``. You may wish to implement the process method yourself when you aren't +using a service description or need to implement more complex request serialization. + +.. important:::: + + When implementing a custom ``build()`` method, be sure to set the class property of ``$this->request`` to an + instantiated and ready to send request. + +The following example shows how to implement the ``getMentions`` +`Twitter API `_ method using a concrete command. + +.. code-block:: php + + namespace mtdowling\Twitter\Command; + + use Guzzle\Service\Command\AbstractCommand; + + class GetMentions extends AbstractCommand + { + protected function build() + { + // Create the request property of the command + $this->request = $this->client->get('statuses/mentions_timeline.json'); + + // Grab the query object of the request because we will use it for + // serializing command parameters on the request + $query = $this->request->getQuery(); + + if ($this['count']) { + $query->set('count', $this['count']); + } + + if ($this['since_id']) { + $query->set('since_id', $this['since_id']); + } + + if ($this['max_id']) { + $query->set('max_id', $this['max_id']); + } + + if ($this['trim_user'] !== null) { + $query->set('trim_user', $this['trim_user'] ? 'true' : 'false'); + } + + if ($this['contributor_details'] !== null) { + $query->set('contributor_details', $this['contributor_details'] ? 'true' : 'false'); + } + + if ($this['include_entities'] !== null) { + $query->set('include_entities', $this['include_entities'] ? 'true' : 'false'); + } + } + } + +By default, a client will attempt to find concrete command classes under the ``Command`` namespace of a client. First +the client will attempt to find an exact match for the name of the command to the name of the command class. If an +exact match is not found, the client will calculate a class name using inflection. This is calculated based on the +folder hierarchy of a command and converting the CamelCased named commands into snake_case. Here are some examples on +how the command names are calculated: + +#. ``Foo\Command\JarJar`` **->** jar_jar +#. ``Foo\Command\Test`` **->** test +#. ``Foo\Command\People\GetCurrentPerson`` **->** people.get_current_person + +Notice how any sub-namespace beneath ``Command`` is converted from ``\`` to ``.`` (a period). CamelCasing is converted +to lowercased snake_casing (e.g. JarJar == jar_jar). + +Parsing responses +^^^^^^^^^^^^^^^^^ + +The ``process()`` method of a command is responsible for converting an HTTP response into something more useful. For +example, a service description operation that has specified a model object in the ``responseClass`` attribute of the +operation will set a ``Guzzle\Service\Resource\Model`` object as the result of the command. This behavior can be +completely modified as needed-- even if you are using operations and responseClass models. Simply implement a custom +``process()`` method that sets the ``$this->result`` class property to whatever you choose. You can reuse parts of the +default Guzzle response parsing functionality or get inspiration from existing code by using +``Guzzle\Service\Command\OperationResponseParser`` and ``Guzzle\Service\Command\DefaultResponseParser`` classes. + +If you do not implement a custom ``process()`` method and are not using a service description, then Guzzle will attempt +to guess how a response should be processed based on the Content-Type header of the response. Because the Twitter API +sets a ``Content-Type: application/json`` header on this response, we do not need to implement any custom response +parsing. + +Operation commands +~~~~~~~~~~~~~~~~~~ + +Operation commands are commands in which the serialization of an HTTP request and the parsing of an HTTP response are +driven by a Guzzle service description. Because request serialization, validation, and response parsing are +described using a DSL, creating operation commands is a much faster process than writing concrete commands. + +Creating operation commands for our Twitter client can remove a great deal of redundancy from the previous concrete +command, and allows for a deeper runtime introspection of the API. Here's an example service description we can use to +create the Twitter API client: + +.. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "description": "Twitter REST API client", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "summary": "Returns the 20 most recent mentions for the authenticating user.", + "responseClass": "GetMentionsOutput", + "parameters": { + "count": { + "description": "Specifies the number of tweets to try and retrieve", + "type": "integer", + "location": "query" + }, + "since_id": { + "description": "Returns results with an ID greater than the specified ID", + "type": "integer", + "location": "query" + }, + "max_id": { + "description": "Returns results with an ID less than or equal to the specified ID.", + "type": "integer", + "location": "query" + }, + "trim_user": { + "description": "Limits the amount of data returned for each user", + "type": "boolean", + "location": "query" + }, + "contributor_details": { + "description": "Adds more data to contributor elements", + "type": "boolean", + "location": "query" + }, + "include_entities": { + "description": "The entities node will be disincluded when set to false.", + "type": "boolean", + "location": "query" + } + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +If you're lazy, you can define the API in a less descriptive manner using ``additionalParameters``. +``additionalParameters`` define the serialization and validation rules of parameters that are not explicitly defined +in a service description. + +.. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "description": "Twitter REST API client", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "summary": "Returns the 20 most recent mentions for the authenticating user.", + "responseClass": "GetMentionsOutput", + "additionalParameters": { + "location": "query" + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +You should attach the service description to the client at the end of the client's factory method: + +.. code-block:: php + + // ... + class TwitterClient extends Client + { + public static function factory($config = array()) + { + // ... same code as before ... + + // Set the service description + $client->setDescription(ServiceDescription::factory('path/to/twitter.json')); + + return $client; + } + } + +The client can now use operations defined in the service description instead of requiring you to create concrete +command classes. Feel free to delete the concrete command class we created earlier. + +.. code-block:: php + + $jsonData = $twitter->getMentions(array( + 'count' => 5, + 'trim_user' => true + )); + +Executing commands in parallel +------------------------------ + +Much like HTTP requests, Guzzle allows you to send multiple commands in parallel. You can send commands in parallel by +passing an array of command objects to a client's ``execute()`` method. The client will serialize each request and +send them all in parallel. If an error is encountered during the transfer, then a +``Guzzle\Service\Exception\CommandTransferException`` is thrown, which allows you to retrieve a list of commands that +succeeded and a list of commands that failed. + +.. code-block:: php + + use Guzzle\Service\Exception\CommandTransferException; + + $commands = array(); + $commands[] = $twitter->getCommand('getMentions'); + $commands[] = $twitter->getCommand('otherCommandName'); + // etc... + + try { + $result = $client->execute($commands); + foreach ($result as $command) { + echo $command->getName() . ': ' . $command->getResponse()->getStatusCode() . "\n"; + } + } catch (CommandTransferException $e) { + // Get an array of the commands that succeeded + foreach ($e->getSuccessfulCommands() as $command) { + echo $command->getName() . " succeeded\n"; + } + // Get an array of the commands that failed + foreach ($e->getFailedCommands() as $command) { + echo $command->getName() . " failed\n"; + } + } + +.. note:: + + All commands executed from a client using an array must originate from the same client. + +Special command options +----------------------- + +Guzzle exposes several options that help to control how commands are validated, serialized, and parsed. +Command options can be specified when creating a command or in the ``command.params`` parameter in the +``Guzzle\Service\Client``. + +=========================== ============================================================================================ +command.request_options Option used to add :ref:`Request options ` to the request created by a + command +command.hidden_params An array of the names of parameters ignored by the ``additionalParameters`` parameter schema +command.disable_validation Set to true to disable JSON schema validation of the command's input parameters +command.response_processing Determines how the default response parser will parse the command. One of "raw" no parsing, + "model" (the default method used to parse commands using response models defined in service + descriptions) +command.headers (deprecated) Option used to specify custom headers. Use ``command.request_options`` instead +command.on_complete (deprecated) Option used to add an onComplete method to a command. Use + ``command.after_send`` event instead +command.response_body (deprecated) Option used to change the entity body used to store a response. + Use ``command.request_options`` instead +=========================== ============================================================================================ + +Advanced client configuration +============================= + +Default command parameters +-------------------------- + +When creating a client object, you can specify default command parameters to pass into all commands. Any key value pair +present in the ``command.params`` settings of a client will be added as default parameters to any command created +by the client. + +.. code-block:: php + + $client = new Guzzle\Service\Client(array( + 'command.params' => array( + 'default_1' => 'foo', + 'another' => 'bar' + ) + )); + +Magic methods +------------- + +Client objects will, by default, attempt to create and execute commands when a missing method is invoked on a client. +This powerful concept applies to both concrete commands and operation commands powered by a service description. This +makes it appear to the end user that you have defined actual methods on a client object, when in fact, the methods are +invoked using PHP's magic ``__call`` method. + +The ``__call`` method uses the ``getCommand()`` method of a client, which uses the client's internal +``Guzzle\Service\Command\Factory\FactoryInterface`` object. The default command factory allows you to instantiate +operations defined in a client's service description. The method in which a client determines which command to +execute is defined as follows: + +1. The client will first try to find a literal match for an operation in the service description. +2. If the literal match is not found, the client will try to uppercase the first character of the operation and find + the match again. +3. If a match is still not found, the command factory will inflect the method name from CamelCase to snake_case and + attempt to find a matching command. +4. If a command still does not match, an exception is thrown. + +.. code-block:: php + + // Use the magic method + $result = $twitter->getMentions(); + + // This is exactly the same as: + $result = $twitter->getCommand('getMentions')->execute(); + +You can disable magic methods on a client by passing ``false`` to the ``enableMagicMethod()`` method. + +Custom command factory +---------------------- + +A client by default uses the ``Guzzle\Service\Command\Factory\CompositeFactory`` which allows multiple command +factories to attempt to create a command by a certain name. The default CompositeFactory uses a ``ConcreteClassFactory`` +and a ``ServiceDescriptionFactory`` if a service description is specified on a client. You can specify a custom +command factory if your client requires custom command creation logic using the ``setCommandFactory()`` method of +a client. + +Custom resource Iterator factory +-------------------------------- + +Resource iterators can be retrieved from a client using the ``getIterator($name)`` method of a client. This method uses +a client's internal ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object. A client by default uses a +``Guzzle\Service\Resource\ResourceIteratorClassFactory`` to attempt to find concrete classes that implement resource +iterators. The default factory will first look for matching iterators in the ``Iterator`` subdirectory of the client +followed by the ``Model`` subdirectory of a client. Use the ``setResourceIteratorFactory()`` method of a client to +specify a custom resource iterator factory. + +Plugins and events +================== + +``Guzzle\Service\Client`` exposes various events that allow you to hook in custom logic. A client object owns a +``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling +``$client->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or +event subscribers (classes that listen to specific events of a dispatcher). + +.. _service-client-events: + +Events emitted from a Service Client +------------------------------------ + +A ``Guzzle\Service\Client`` object emits the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| client.command.create | The client created a command object | * client: Client object | +| | | * command: Command object | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.before_prepare | Before a command is validated and built. | * command: Command being prepared | +| | This is also before a request is created. | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.after_prepare | After a command instantiates and | * command: Command that was prepared | +| | configures its request object. | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.before_send | The client is about to execute a prepared | * command: Command to execute | +| | command | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.after_send | The client successfully completed | * command: The command that was executed | +| | executing a command | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.parse_response | Called when ``responseType`` is ``class`` | * command: The command with a response | +| | and the response is about to be parsed. | about to be parsed. | ++------------------------------+--------------------------------------------+------------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Service\Client; + + $client = new Client(); + + // create an event listener that operates on request objects + $client->getEventDispatcher()->addListener('command.after_prepare', function (Event $event) { + $command = $event['command']; + $request = $command->getRequest(); + + // do something with request + }); + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Common\Client; + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + class EventSubscriber implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + return array( + 'client.command.create' => 'onCommandCreate', + 'command.parse_response' => 'onParseResponse' + ); + } + + public function onCommandCreate(Event $event) + { + $client = $event['client']; + $command = $event['command']; + // operate on client and command + } + + public function onParseResponse(Event $event) + { + $command = $event['command']; + // operate on the command + } + } + + $client = new Client(); + + $client->addSubscriber(new EventSubscriber()); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phar-stub.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phar-stub.php new file mode 100644 index 000000000..cc2b53f4f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phar-stub.php @@ -0,0 +1,16 @@ +registerNamespaces(array( + 'Guzzle' => 'phar://guzzle.phar/src', + 'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher', + 'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib', + 'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src' +)); +$classLoader->register(); + +__HALT_COMPILER(); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/build.properties.dist b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/build.properties.dist new file mode 100644 index 000000000..c60d3d9cf --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/build.properties.dist @@ -0,0 +1,16 @@ +# you may need to update this if you're working on a fork. +guzzle.remote=git@github.com:guzzle/guzzle.git + +# github credentials -- only used by GitHub API calls to create subtree repos +github.basicauth=username:password +# for the subtree split and testing +github.org=guzzle + +# your git path +cmd.git=git + +# your composer command +cmd.composer=composer + +# test server start +cmd.testserver=node diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/dependencies.xml b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/dependencies.xml new file mode 100644 index 000000000..e40e037c2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/dependencies.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + using git at ${cmd.git} + + + + found git at ${cmd.git} + + + + + + + + + + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/deploy.xml b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/deploy.xml new file mode 100644 index 000000000..109e5ec4f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/imports/deploy.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + On branch ${head} + + + + + + + + + + working directory clean + + + ${git.status} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChangeLog Match: ${version.changelog} + Guzzle\Common\Version Match: ${version.version} + + + + releasing: phing -Dnew.version=3.0.x -Dhead=master release + -- + + + + + + + + + + + + + + + BEGINNING RELEASE FOR ${new.version} + + + + + + + + + + + + + + + + + + + + + + + + Tip: to create a new release, do: phing -Dnew.version=[TAG] -Dhead=[BRANCH] release + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php new file mode 100644 index 000000000..3b7040982 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php @@ -0,0 +1,152 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; + +class ComposerLintTask extends Task +{ + protected $dir = null; + protected $file = null; + protected $passthru = false; + protected $composer = null; + + /** + * The setter for the dir + * + * @param string $str Directory to crawl recursively for composer files + */ + public function setDir($str) + { + $this->dir = $str; + } + + /** + * The setter for the file + * + * @param string $str Individual file to validate + */ + public function setFile($str) + { + $this->file = $str; + } + + /** + * Whether to use PHP's passthru() function instead of exec() + * + * @param boolean $passthru If passthru shall be used + */ + public function setPassthru($passthru) + { + $this->passthru = (bool) $passthru; + } + + /** + * Composer to execute. If unset, will attempt composer.phar in project + * basedir, and if that fails, will attempt global composer + * installation. + * + * @param string $str Individual file to validate + */ + public function setComposer($str) + { + $this->file = $str; + } + + /** + * The init method: do init steps + */ + public function init() + { + // nothing needed here + } + + /** + * The main entry point + */ + public function main() + { + if ($this->composer === null) { + $this->findComposer(); + } + + $files = array(); + if (!empty($this->file) && file_exists($this->file)) { + $files[] = $this->file; + } + + if (!empty($this->dir)) { + $found = $this->findFiles(); + foreach ($found as $file) { + $files[] = $this->dir . DIRECTORY_SEPARATOR . $file; + } + } + + foreach ($files as $file) { + + $cmd = $this->composer . ' validate ' . $file; + $cmd = escapeshellcmd($cmd); + + if ($this->passthru) { + $retval = null; + passthru($cmd, $retval); + if ($retval == 1) { + throw new BuildException('invalid composer.json'); + } + } else { + $out = array(); + $retval = null; + exec($cmd, $out, $retval); + if ($retval == 1) { + $err = join("\n", $out); + throw new BuildException($err); + } else { + $this->log($out[0]); + } + } + + } + + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findFiles() + { + $ds = new DirectoryScanner(); + $ds->setBasedir($this->dir); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + return $ds->getIncludedFiles(); + } + + /** + * Find composer installation + * + */ + protected function findComposer() + { + $basedir = $this->project->getBasedir(); + $php = $this->project->getProperty('php.interpreter'); + + if (file_exists($basedir . '/composer.phar')) { + $this->composer = "$php $basedir/composer.phar"; + } else { + $out = array(); + exec('which composer', $out); + if (empty($out)) { + throw new BuildException( + 'Could not determine composer location.' + ); + } + $this->composer = $out[0]; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php new file mode 100644 index 000000000..f72a6b5d0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php @@ -0,0 +1,338 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; +require_once 'PEAR/PackageFileManager2.php'; +require_once 'PEAR/PackageFileManager/File.php'; +require_once 'PEAR/Packager.php'; + +class GuzzlePearPharPackageTask extends Task +{ + private $version; + private $deploy = true; + private $makephar = true; + + private $subpackages = array(); + + public function setVersion($str) + { + $this->version = $str; + } + + public function getVersion() + { + return $this->version; + } + + public function setDeploy($deploy) + { + $this->deploy = (bool) $deploy; + } + + public function getDeploy() + { + return $this->deploy; + } + + public function setMakephar($makephar) + { + $this->makephar = (bool) $makephar; + } + + public function getMakephar() + { + return $this->makephar; + } + + private $basedir; + private $guzzleinfo; + private $changelog_release_date; + private $changelog_notes = '-'; + + public function main() + { + $this->basedir = $this->getProject()->getBasedir(); + + if (!is_dir((string) $this->basedir.'/.subsplit')) { + throw new BuildException('PEAR packaging requires .subsplit directory'); + } + + // main composer file + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/composer.json'); + $this->guzzleinfo = json_decode($composer_file, true); + + // make sure we have a target + $pearwork = (string) $this->basedir . '/build/pearwork'; + if (!is_dir($pearwork)) { + mkdir($pearwork, 0777, true); + } + $pearlogs = (string) $this->basedir . '/build/artifacts/logs'; + if (!is_dir($pearlogs)) { + mkdir($pearlogs, 0777, true); + } + + $version = $this->getVersion(); + $this->grabChangelog(); + if ($version[0] == '2') { + $this->log('building single PEAR package'); + $this->buildSinglePackage(); + } else { + // $this->log("building PEAR subpackages"); + // $this->createSubPackages(); + // $this->log("building PEAR bundle package"); + $this->buildSinglePackage(); + } + + if ($this->getMakephar()) { + $this->log("building PHAR"); + $this->getProject()->executeTarget('package-phar'); + } + + if ($this->getDeploy()) { + $this->doDeployment(); + } + } + + public function doDeployment() + { + $basedir = (string) $this->basedir; + $this->log('beginning PEAR/PHAR deployment'); + + chdir($basedir . '/build/pearwork'); + if (!is_dir('./channel')) { + mkdir('./channel'); + } + + // Pull the PEAR channel down locally + passthru('aws s3 sync s3://pear.guzzlephp.org ./channel'); + + // add PEAR packages + foreach (scandir('./') as $file) { + if (substr($file, -4) == '.tgz') { + passthru('pirum add ./channel ' . $file); + } + } + + // if we have a new phar, add it + if ($this->getMakephar() && file_exists($basedir . '/build/artifacts/guzzle.phar')) { + rename($basedir . '/build/artifacts/guzzle.phar', './channel/guzzle.phar'); + } + + // Sync up with the S3 bucket + chdir($basedir . '/build/pearwork/channel'); + passthru('aws s3 sync . s3://pear.guzzlephp.org'); + } + + public function buildSinglePackage() + { + $v = $this->getVersion(); + $apiversion = $v[0] . '.0.0'; + + $opts = array( + 'packagedirectory' => (string) $this->basedir . '/.subsplit/src/', + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json'), + 'baseinstalldir' => '/', + 'packagefile' => 'package.xml' + //'outputdirectory' => (string) $this->basedir . '/build/pearwork/' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->addRole('md', 'doc'); + $pfm->addRole('pem', 'php'); + $pfm->setPackage('Guzzle'); + $pfm->setSummary("Object-oriented PHP HTTP Client for PHP 5.3+"); + $pfm->setDescription($this->guzzleinfo['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion($apiversion); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->addExtensionDep('required', 'curl'); + $pfm->setPearinstallerDep('1.4.6'); + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + if (!empty($this->subpackages)) { + foreach ($this->subpackages as $package) { + $pkg = dirname($package); + $pkg = str_replace('/', '_', $pkg); + $pfm->addConflictingPackageDepWithChannel($pkg, 'guzzlephp.org/pear', false, $apiversion); + } + } + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package.log', $log); + chdir($startdir); + } + + public function createSubPackages() + { + $this->findComponents(); + + foreach ($this->subpackages as $package) { + $baseinstalldir = dirname($package); + $dir = (string) $this->basedir.'/.subsplit/src/' . $baseinstalldir; + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/src/'. $package); + $package_info = json_decode($composer_file, true); + $this->log('building ' . $package_info['target-dir'] . ' subpackage'); + $this->buildSubPackage($dir, $baseinstalldir, $package_info); + } + } + + public function buildSubPackage($dir, $baseinstalldir, $info) + { + $package = str_replace('/', '_', $baseinstalldir); + $opts = array( + 'packagedirectory' => $dir, + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json', '*package.xml'), + 'baseinstalldir' => '/' . $info['target-dir'], + 'packagefile' => 'package.xml' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->setPackage($package); + $pfm->setSummary($info['description']); + $pfm->setDescription($info['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion('3.0.0'); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->setPearinstallerDep('1.4.6'); + + foreach ($info['require'] as $type => $version) { + if ($type == 'php') { + continue; + } + if ($type == 'symfony/event-dispatcher') { + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + } + if ($type == 'ext-curl') { + $pfm->addExtensionDep('required', 'curl'); + } + if (substr($type, 0, 6) == 'guzzle') { + $gdep = str_replace('/', ' ', $type); + $gdep = ucwords($gdep); + $gdep = str_replace(' ', '_', $gdep); + $pfm->addPackageDepWithChannel('required', $gdep, 'guzzlephp.org/pear', $this->getVersion()); + } + } + + // can't have main Guzzle package AND sub-packages + $pfm->addConflictingPackageDepWithChannel('Guzzle', 'guzzlephp.org/pear', false, $apiversion); + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'/package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'/package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package_'.$package.'.log', $log); + chdir($startdir); + } + + public function findComponents() + { + $ds = new DirectoryScanner(); + $ds->setBasedir((string) $this->basedir.'/.subsplit/src'); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + $files = $ds->getIncludedFiles(); + $this->subpackages = $files; + } + + public function grabChangelog() + { + $cl = file((string) $this->basedir.'/.subsplit/CHANGELOG.md'); + $notes = ''; + $in_version = false; + $release_date = null; + + foreach ($cl as $line) { + $line = trim($line); + if (preg_match('/^\* '.$this->getVersion().' \(([0-9\-]+)\)$/', $line, $matches)) { + $release_date = $matches[1]; + $in_version = true; + continue; + } + if ($in_version && empty($line) && empty($notes)) { + continue; + } + if ($in_version && ! empty($line)) { + $notes .= $line."\n"; + } + if ($in_version && empty($line) && !empty($notes)) { + $in_version = false; + } + } + $this->changelog_release_date = $release_date; + + if (! empty($notes)) { + $this->changelog_notes = $notes; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php new file mode 100644 index 000000000..5d56a5bd5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php @@ -0,0 +1,385 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +// base - base of tree to split out +// subIndicatorFile - composer.json, package.xml? +class GuzzleSubSplitTask extends GitBaseTask +{ + /** + * What git repository to pull from and publish to + */ + protected $remote = null; + + /** + * Publish for comma-separated heads instead of all heads + */ + protected $heads = null; + + /** + * Publish for comma-separated tags instead of all tags + */ + protected $tags = null; + + /** + * Base of the tree RELATIVE TO .subsplit working dir + */ + protected $base = null; + + /** + * The presence of this file will indicate that the directory it resides + * in is at the top level of a split. + */ + protected $subIndicatorFile = 'composer.json'; + + /** + * Do everything except actually send the update. + */ + protected $dryRun = null; + + /** + * Do not sync any heads. + */ + protected $noHeads = false; + + /** + * Do not sync any tags. + */ + protected $noTags = false; + + /** + * The splits we found in the heads + */ + protected $splits; + + public function setRemote($str) + { + $this->remote = $str; + } + + public function getRemote() + { + return $this->remote; + } + + public function setHeads($str) + { + $this->heads = explode(',', $str); + } + + public function getHeads() + { + return $this->heads; + } + + public function setTags($str) + { + $this->tags = explode(',', $str); + } + + public function getTags() + { + return $this->tags; + } + + public function setBase($str) + { + $this->base = $str; + } + + public function getBase() + { + return $this->base; + } + + public function setSubIndicatorFile($str) + { + $this->subIndicatorFile = $str; + } + + public function getSubIndicatorFile() + { + return $this->subIndicatorFile; + } + + public function setDryRun($bool) + { + $this->dryRun = (bool) $bool; + } + + public function getDryRun() + { + return $this->dryRun; + } + + public function setNoHeads($bool) + { + $this->noHeads = (bool) $bool; + } + + public function getNoHeads() + { + return $this->noHeads; + } + + public function setNoTags($bool) + { + $this->noTags = (bool) $bool; + } + + public function getNoTags() + { + return $this->noTags; + } + + /** + * GitClient from VersionControl_Git + */ + protected $client = null; + + /** + * The main entry point + */ + public function main() + { + $repo = $this->getRepository(); + if (empty($repo)) { + throw new BuildException('"repository" is a required parameter'); + } + + $remote = $this->getRemote(); + if (empty($remote)) { + throw new BuildException('"remote" is a required parameter'); + } + + chdir($repo); + $this->client = $this->getGitClient(false, $repo); + + // initalized yet? + if (!is_dir('.subsplit')) { + $this->subsplitInit(); + } else { + // update + $this->subsplitUpdate(); + } + + // find all splits based on heads requested + $this->findSplits(); + + // check that GitHub has the repos + $this->verifyRepos(); + + // execute the subsplits + $this->publish(); + } + + public function publish() + { + $this->log('DRY RUN ONLY FOR NOW'); + $base = $this->getBase(); + $base = rtrim($base, '/') . '/'; + $org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + + $splits = array(); + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + $splits[] = $base . $component . ':git@github.com:'. $org.'/'.$meta['repo']; + } + + $cmd = 'git subsplit publish '; + $cmd .= escapeshellarg(implode(' ', $splits)); + + if ($this->getNoHeads()) { + $cmd .= ' --no-heads'; + } else { + $cmd .= ' --heads='.$head; + } + + if ($this->getNoTags()) { + $cmd .= ' --no-tags'; + } else { + if ($this->getTags()) { + $cmd .= ' --tags=' . escapeshellarg(implode(' ', $this->getTags())); + } + } + + passthru($cmd); + } + } + + /** + * Runs `git subsplit update` + */ + public function subsplitUpdate() + { + $repo = $this->getRepository(); + $this->log('git-subsplit update...'); + $cmd = $this->client->getCommand('subsplit'); + $cmd->addArgument('update'); + try { + $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit update failed'. $e); + } + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar update --dev'); + chdir($repo); + } + + /** + * Runs `git subsplit init` based on the remote repository. + */ + public function subsplitInit() + { + $remote = $this->getRemote(); + $cmd = $this->client->getCommand('subsplit'); + $this->log('running git-subsplit init ' . $remote); + + $cmd->setArguments(array( + 'init', + $remote + )); + + try { + $output = $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit init failed'. $e); + } + $this->log(trim($output), Project::MSG_INFO); + $repo = $this->getRepository(); + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar install --dev'); + chdir($repo); + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findSplits() + { + $this->log("checking heads for subsplits"); + $repo = $this->getRepository(); + $base = $this->getBase(); + + $splits = array(); + $heads = $this->getHeads(); + + if (!empty($base)) { + $base = '/' . ltrim($base, '/'); + } else { + $base = '/'; + } + + chdir($repo . '/.subsplit'); + foreach ($heads as $head) { + $splits[$head] = array(); + + // check each head requested *BEFORE* the actual subtree split command gets it + passthru("git checkout '$head'"); + $ds = new DirectoryScanner(); + $ds->setBasedir($repo . '/.subsplit' . $base); + $ds->setIncludes(array('**/'.$this->subIndicatorFile)); + $ds->scan(); + $files = $ds->getIncludedFiles(); + + // Process the files we found + foreach ($files as $file) { + $pkg = file_get_contents($repo . '/.subsplit' . $base .'/'. $file); + $pkg_json = json_decode($pkg, true); + $name = $pkg_json['name']; + $component = str_replace('/composer.json', '', $file); + // keep this for split cmd + $tmpreponame = explode('/', $name); + $reponame = $tmpreponame[1]; + $splits[$head][$component]['repo'] = $reponame; + $nscomponent = str_replace('/', '\\', $component); + $splits[$head][$component]['desc'] = "[READ ONLY] Subtree split of $nscomponent: " . $pkg_json['description']; + } + } + + // go back to how we found it + passthru("git checkout master"); + chdir($repo); + $this->splits = $splits; + } + + /** + * Based on list of repositories we determined we *should* have, talk + * to GitHub and make sure they're all there. + * + */ + protected function verifyRepos() + { + $this->log('verifying GitHub target repos'); + $github_org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + $github_creds = $this->getOwningTarget()->getProject()->getProperty('github.basicauth'); + + if ($github_creds == 'username:password') { + $this->log('Skipping GitHub repo checks. Update github.basicauth in build.properties to verify repos.', 1); + return; + } + + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos?type=all'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + curl_close($ch); + $repos = json_decode($result, true); + $existing_repos = array(); + + // parse out the repos we found on GitHub + foreach ($repos as $repo) { + $tmpreponame = explode('/', $repo['full_name']); + $reponame = $tmpreponame[1]; + $existing_repos[$reponame] = $repo['description']; + } + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + + $reponame = $meta['repo']; + + if (!isset($existing_repos[$reponame])) { + $this->log("Creating missing repo $reponame"); + $payload = array( + 'name' => $reponame, + 'description' => $meta['desc'], + 'homepage' => 'http://www.guzzlephp.org/', + 'private' => true, + 'has_issues' => false, + 'has_wiki' => false, + 'has_downloads' => true, + 'auto_init' => false + ); + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + echo "Response code: ".curl_getinfo($ch, CURLINFO_HTTP_CODE)."\n"; + curl_close($ch); + } else { + $this->log("Repo $reponame exists", 2); + } + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/phpunit.xml.dist b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phpunit.xml.dist new file mode 100644 index 000000000..208fdc08e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/phpunit.xml.dist @@ -0,0 +1,48 @@ + + + + + + ./tests/Guzzle/Tests + + + + + + + + + + ./src/Guzzle + + ./src/Guzzle + ./src/Guzzle/Common/Exception/GuzzleException.php + ./src/Guzzle/Http/Exception/HttpException.php + ./src/Guzzle/Http/Exception/ServerErrorResponseException.php + ./src/Guzzle/Http/Exception/ClientErrorResponseException.php + ./src/Guzzle/Http/Exception/TooManyRedirectsException.php + ./src/Guzzle/Http/Exception/CouldNotRewindStreamException.php + ./src/Guzzle/Common/Exception/BadMethodCallException.php + ./src/Guzzle/Common/Exception/InvalidArgumentException.php + ./src/Guzzle/Common/Exception/RuntimeException.php + ./src/Guzzle/Common/Exception/UnexpectedValueException.php + ./src/Guzzle/Service/Exception/ClientNotFoundException.php + ./src/Guzzle/Service/Exception/CommandException.php + ./src/Guzzle/Service/Exception/DescriptionBuilderException.php + ./src/Guzzle/Service/Exception/ServiceBuilderException.php + ./src/Guzzle/Service/Exception/ServiceNotFoundException.php + ./src/Guzzle/Service/Exception/ValidationException.php + ./src/Guzzle/Service/Exception/JsonException.php + + + + + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php new file mode 100644 index 000000000..0625d71c3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php @@ -0,0 +1,66 @@ +decoratedBatch = $decoratedBatch; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + * @codeCoverageIgnore + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->decoratedBatch, $method), $args); + } + + public function add($item) + { + $this->decoratedBatch->add($item); + + return $this; + } + + public function flush() + { + return $this->decoratedBatch->flush(); + } + + public function isEmpty() + { + return $this->decoratedBatch->isEmpty(); + } + + /** + * Trace the decorators associated with the batch + * + * @return array + */ + public function getDecorators() + { + $found = array($this); + if (method_exists($this->decoratedBatch, 'getDecorators')) { + $found = array_merge($found, $this->decoratedBatch->getDecorators()); + } + + return $found; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php new file mode 100644 index 000000000..4d41c54f8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php @@ -0,0 +1,92 @@ +transferStrategy = $transferStrategy; + $this->divisionStrategy = $divisionStrategy; + $this->queue = new \SplQueue(); + $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE); + $this->dividedBatches = array(); + } + + public function add($item) + { + $this->queue->enqueue($item); + + return $this; + } + + public function flush() + { + $this->createBatches(); + + $items = array(); + foreach ($this->dividedBatches as $batchIndex => $dividedBatch) { + while ($dividedBatch->valid()) { + $batch = $dividedBatch->current(); + $dividedBatch->next(); + try { + $this->transferStrategy->transfer($batch); + $items = array_merge($items, $batch); + } catch (\Exception $e) { + throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy); + } + } + // Keep the divided batch down to a minimum in case of a later exception + unset($this->dividedBatches[$batchIndex]); + } + + return $items; + } + + public function isEmpty() + { + return count($this->queue) == 0 && count($this->dividedBatches) == 0; + } + + /** + * Create batches for any queued items + */ + protected function createBatches() + { + if (count($this->queue)) { + if ($batches = $this->divisionStrategy->createBatches($this->queue)) { + // Convert arrays into iterators + if (is_array($batches)) { + $batches = new \ArrayIterator($batches); + } + $this->dividedBatches[] = $batches; + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php new file mode 100644 index 000000000..ea99b4dd0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php @@ -0,0 +1,199 @@ + 'Guzzle\Batch\BatchRequestTransfer', + 'command' => 'Guzzle\Batch\BatchCommandTransfer' + ); + + /** + * Create a new instance of the BatchBuilder + * + * @return BatchBuilder + */ + public static function factory() + { + return new self(); + } + + /** + * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}. + * + * @param $threshold Number of items to allow in the queue before a flush + * + * @return BatchBuilder + */ + public function autoFlushAt($threshold) + { + $this->autoFlush = $threshold; + + return $this; + } + + /** + * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}. + * + * @return BatchBuilder + */ + public function keepHistory() + { + $this->history = true; + + return $this; + } + + /** + * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer + * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator. + * + * @return BatchBuilder + */ + public function bufferExceptions() + { + $this->exceptionBuffering = true; + + return $this; + } + + /** + * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator. + * + * @param mixed $callable Callable function to notify + * + * @return BatchBuilder + * @throws InvalidArgumentException if the argument is not callable + */ + public function notify($callable) + { + $this->afterFlush = $callable; + + return $this; + } + + /** + * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer} + * object as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of requests + * + * @return BatchBuilder + */ + public function transferRequests($batchSize = 50) + { + $className = self::$mapping['request']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Configures the batch to transfer batches commands. Associates as + * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of commands + * + * @return BatchBuilder + */ + public function transferCommands($batchSize = 50) + { + $className = self::$mapping['command']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Specify the strategy used to divide the queue into an array of batches + * + * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches + * + * @return BatchBuilder + */ + public function createBatchesWith(BatchDivisorInterface $divisorStrategy) + { + $this->divisorStrategy = $divisorStrategy; + + return $this; + } + + /** + * Specify the strategy used to transport the items when flush is called + * + * @param BatchTransferInterface $transferStrategy How items are transferred + * + * @return BatchBuilder + */ + public function transferWith(BatchTransferInterface $transferStrategy) + { + $this->transferStrategy = $transferStrategy; + + return $this; + } + + /** + * Create and return the instantiated batch + * + * @return BatchInterface + * @throws RuntimeException if no transfer strategy has been specified + */ + public function build() + { + if (!$this->transferStrategy) { + throw new RuntimeException('No transfer strategy has been specified'); + } + + if (!$this->divisorStrategy) { + throw new RuntimeException('No divisor strategy has been specified'); + } + + $batch = new Batch($this->transferStrategy, $this->divisorStrategy); + + if ($this->exceptionBuffering) { + $batch = new ExceptionBufferingBatch($batch); + } + + if ($this->afterFlush) { + $batch = new NotifyingBatch($batch, $this->afterFlush); + } + + if ($this->autoFlush) { + $batch = new FlushingBatch($batch, $this->autoFlush); + } + + if ($this->history) { + $batch = new HistoryBatch($batch); + } + + return $batch; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php new file mode 100644 index 000000000..e0a2d9568 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php @@ -0,0 +1,39 @@ +callable = $callable; + $this->context = $context; + } + + public function createBatches(\SplQueue $queue) + { + return call_user_func($this->callable, $queue, $this->context); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php new file mode 100644 index 000000000..9cbf1aba4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php @@ -0,0 +1,40 @@ +callable = $callable; + $this->context = $context; + } + + public function transfer(array $batch) + { + return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php new file mode 100644 index 000000000..d55ac7d1f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php @@ -0,0 +1,75 @@ +batchSize = $batchSize; + } + + /** + * Creates batches by grouping commands by their associated client + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof CommandInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, new \ArrayObject(array($item))); + } else { + $groups[$client]->append($item); + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if (empty($batch)) { + return; + } + + // Get the client of the first found command + $client = reset($batch)->getClient(); + + // Keep a list of all commands with invalid clients + $invalid = array_filter($batch, function ($command) use ($client) { + return $command->getClient() !== $client; + }); + + if (!empty($invalid)) { + throw new InconsistentClientTransferException($invalid); + } + + $client->execute($batch); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php new file mode 100644 index 000000000..0214f05f4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php @@ -0,0 +1,18 @@ +batchSize = $batchSize; + } + + /** + * Creates batches of requests by grouping requests by their associated curl multi object. + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + // Create batches by client objects + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof RequestInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, array($item)); + } else { + $current = $groups[$client]; + $current[] = $item; + $groups[$client] = $current; + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if ($batch) { + reset($batch)->getClient()->send($batch); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php new file mode 100644 index 000000000..67f90a581 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php @@ -0,0 +1,47 @@ +size = $size; + } + + /** + * Set the size of each batch + * + * @param int $size Size of each batch + * + * @return BatchSizeDivisor + */ + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + /** + * Get the size of each batch + * + * @return int + */ + public function getSize() + { + return $this->size; + } + + public function createBatches(\SplQueue $queue) + { + return array_chunk(iterator_to_array($queue, false), $this->size); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php new file mode 100644 index 000000000..2e0b60dad --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php @@ -0,0 +1,16 @@ +batch = $batch; + $this->transferredItems = $transferredItems; + $this->transferStrategy = $transferStrategy; + $this->divisorStrategy = $divisorStrategy; + parent::__construct( + 'Exception encountered while transferring batch: ' . $exception->getMessage(), + $exception->getCode(), + $exception + ); + } + + /** + * Get the batch that we being sent when the exception occurred + * + * @return array + */ + public function getBatch() + { + return $this->batch; + } + + /** + * Get the items transferred at the point in which the exception was encountered + * + * @return array + */ + public function getTransferredItems() + { + return $this->transferredItems; + } + + /** + * Get the transfer strategy + * + * @return TransferStrategy + */ + public function getTransferStrategy() + { + return $this->transferStrategy; + } + + /** + * Get the divisor strategy + * + * @return DivisorStrategy + */ + public function getDivisorStrategy() + { + return $this->divisorStrategy; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php new file mode 100644 index 000000000..d7a892885 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php @@ -0,0 +1,50 @@ +decoratedBatch->isEmpty()) { + try { + $transferredItems = $this->decoratedBatch->flush(); + } catch (BatchTransferException $e) { + $this->exceptions[] = $e; + $transferredItems = $e->getTransferredItems(); + } + $items = array_merge($items, $transferredItems); + } + + return $items; + } + + /** + * Get the buffered exceptions + * + * @return array Array of BatchTransferException objects + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Clear the buffered exceptions + */ + public function clearExceptions() + { + $this->exceptions = array(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php new file mode 100644 index 000000000..367b68427 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php @@ -0,0 +1,60 @@ +threshold = $threshold; + parent::__construct($decoratedBatch); + } + + /** + * Set the auto-flush threshold + * + * @param int $threshold The auto-flush threshold + * + * @return FlushingBatch + */ + public function setThreshold($threshold) + { + $this->threshold = $threshold; + + return $this; + } + + /** + * Get the auto-flush threshold + * + * @return int + */ + public function getThreshold() + { + return $this->threshold; + } + + public function add($item) + { + $this->decoratedBatch->add($item); + if (++$this->currentTotal >= $this->threshold) { + $this->currentTotal = 0; + $this->decoratedBatch->flush(); + } + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php new file mode 100644 index 000000000..e345fdc34 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php @@ -0,0 +1,39 @@ +history[] = $item; + $this->decoratedBatch->add($item); + + return $this; + } + + /** + * Get the batch history + * + * @return array + */ + public function getHistory() + { + return $this->history; + } + + /** + * Clear the batch history + */ + public function clearHistory() + { + $this->history = array(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php new file mode 100644 index 000000000..96d04daa8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php @@ -0,0 +1,38 @@ +callable = $callable; + parent::__construct($decoratedBatch); + } + + public function flush() + { + $items = $this->decoratedBatch->flush(); + call_user_func($this->callable, $items); + + return $items; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json new file mode 100644 index 000000000..12404d381 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json @@ -0,0 +1,31 @@ +{ + "name": "guzzle/batch", + "description": "Guzzle batch component for batching requests, commands, or custom transfers", + "homepage": "http://guzzlephp.org/", + "keywords": ["batch", "HTTP", "REST", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Batch": "" } + }, + "suggest": { + "guzzle/http": "self.version", + "guzzle/service": "self.version" + }, + "target-dir": "Guzzle/Batch", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php new file mode 100644 index 000000000..a5c527167 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php @@ -0,0 +1,21 @@ +cache; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php new file mode 100644 index 000000000..94e623463 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php @@ -0,0 +1,117 @@ +newInstanceArgs($args); + } + } catch (\Exception $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php new file mode 100644 index 000000000..970c9e228 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php @@ -0,0 +1,55 @@ +callables = $callables; + } + + public function contains($id, array $options = null) + { + return call_user_func($this->callables['contains'], $id, $options); + } + + public function delete($id, array $options = null) + { + return call_user_func($this->callables['delete'], $id, $options); + } + + public function fetch($id, array $options = null) + { + return call_user_func($this->callables['fetch'], $id, $options); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php new file mode 100644 index 000000000..e1aaf9f81 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->contains($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->delete($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->fetch($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($id, $data, $lifeTime !== false ? $lifeTime : 0); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php new file mode 100644 index 000000000..68bd4af97 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php @@ -0,0 +1,31 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->test($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->remove($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->load($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($data, $id, array(), $lifeTime); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php new file mode 100644 index 000000000..1fc18a555 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->hasItem($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->removeItem($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->getItem($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->setItem($id, $data); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json new file mode 100644 index 000000000..a5d999bd6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/cache", + "description": "Guzzle cache adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Cache": "" } + }, + "target-dir": "Guzzle/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php new file mode 100644 index 000000000..d1e842b1c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php @@ -0,0 +1,49 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php new file mode 100644 index 000000000..5cb1535d0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php @@ -0,0 +1,403 @@ +data = $data; + } + + /** + * Create a new collection from an array, validate the keys, and add default values where missing + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return self + * @throws InvalidArgumentException if a parameter is missing + */ + public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array()) + { + $data = $config + $defaults; + + if ($missing = array_diff($required, array_keys($data))) { + throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing)); + } + + return new self($data); + } + + public function count() + { + return count($this->data); + } + + public function getIterator() + { + return new \ArrayIterator($this->data); + } + + public function toArray() + { + return $this->data; + } + + /** + * Removes all key value pairs + * + * @return Collection + */ + public function clear() + { + $this->data = array(); + + return $this; + } + + /** + * Get all or a subset of matching key value pairs + * + * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs + * + * @return array Returns an array of all matching key value pairs + */ + public function getAll(array $keys = null) + { + return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data; + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * + * @return mixed|null Value of the key or NULL + */ + public function get($key) + { + return isset($this->data[$key]) ? $this->data[$key] : null; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + * + * @return Collection Returns a reference to the object + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Add a value to a key. If a key of the same name has already been added, the key value will be converted into an + * array and the new value will be pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + * + * @return Collection Returns a reference to the object. + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } elseif (is_array($this->data[$key])) { + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + + return $this; + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + * + * @return Collection + */ + public function remove($key) + { + unset($this->data[$key]); + + return $this; + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Case insensitive search the keys in the collection + * + * @param string $key Key to search for + * + * @return bool|string Returns false if not found, otherwise returns the key + */ + public function keySearch($key) + { + foreach (array_keys($this->data) as $k) { + if (!strcasecmp($k, $key)) { + return $k; + } + } + + return false; + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data); + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + * + * @return Collection Returns a reference to the object + */ + public function replace(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * @param Collection|array $data Associative array of key value pair data + * + * @return Collection Returns a reference to the object. + */ + public function merge($data) + { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + + return $this; + } + + /** + * Over write key value pairs in this collection with all of the data from an array or collection. + * + * @param array|\Traversable $data Values to override over this config + * + * @return self + */ + public function overwriteWith($data) + { + if (is_array($data)) { + $this->data = $data + $this->data; + } elseif ($data instanceof Collection) { + $this->data = $data->toArray() + $this->data; + } else { + foreach ($data as $key => $value) { + $this->data[$key] = $value; + } + } + + return $this; + } + + /** + * Returns a Collection containing all the elements of the collection after applying the callback function to each + * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a + * modified value + * + * @param \Closure $closure Closure to apply + * @param array $context Context to pass to the closure + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function map(\Closure $closure, array $context = array(), $static = true) + { + $collection = $static ? new static() : new self(); + foreach ($this as $key => $value) { + $collection->add($key, $closure($key, $value, $context)); + } + + return $collection; + } + + /** + * Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns + * true, the current value from input is returned into the result Collection. The Closure must accept three + * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value. + * + * @param \Closure $closure Closure evaluation function + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function filter(\Closure $closure, $static = true) + { + $collection = ($static) ? new static() : new self(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection->add($key, $value); + } + } + + return $collection; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + /** + * Set a value into a nested array key. Keys will be created as needed to set the value. + * + * @param string $path Path to set + * @param mixed $value Value to set at the key + * + * @return self + * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value + */ + public function setPath($path, $value) + { + $current =& $this->data; + $queue = explode('/', $path); + while (null !== ($key = array_shift($queue))) { + if (!is_array($current)) { + throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array"); + } elseif (!$queue) { + $current[$key] = $value; + } elseif (isset($current[$key])) { + $current =& $current[$key]; + } else { + $current[$key] = array(); + $current =& $current[$key]; + } + } + + return $this; + } + + /** + * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays) + * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This + * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path. + * + * @param string $path Path to traverse and retrieve a value from + * @param string $separator Character used to add depth to the search + * @param mixed $data Optional data to descend into (used when wildcards are encountered) + * + * @return mixed|null + */ + public function getPath($path, $separator = '/', $data = null) + { + if ($data === null) { + $data =& $this->data; + } + + $path = is_array($path) ? $path : explode($separator, $path); + while (null !== ($part = array_shift($path))) { + if (!is_array($data)) { + return null; + } elseif (isset($data[$part])) { + $data =& $data[$part]; + } elseif ($part != '*') { + return null; + } else { + // Perform a wildcard search by diverging and merging paths + $result = array(); + foreach ($data as $value) { + if (!$path) { + $result = array_merge_recursive($result, (array) $value); + } elseif (null !== ($test = $this->getPath($path, $separator, $value))) { + $result = array_merge_recursive($result, (array) $test); + } + } + return $result; + } + } + + return $data; + } + + /** + * Inject configuration settings into an input string + * + * @param string $input Input to inject + * + * @return string + * @deprecated + */ + public function inject($input) + { + Version::warn(__METHOD__ . ' is deprecated'); + $replace = array(); + foreach ($this->data as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + return strtr($input, $replace); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php new file mode 100644 index 000000000..fad76a9b8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php @@ -0,0 +1,52 @@ +context = $context; + } + + public function getIterator() + { + return new \ArrayIterator($this->context); + } + + public function offsetGet($offset) + { + return isset($this->context[$offset]) ? $this->context[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->context[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->context[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->context[$offset]); + } + + public function toArray() + { + return $this->context; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php new file mode 100644 index 000000000..08d1c7256 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php @@ -0,0 +1,5 @@ +shortMessage = $message; + } + + /** + * Set all of the exceptions + * + * @param array $exceptions Array of exceptions + * + * @return self + */ + public function setExceptions(array $exceptions) + { + $this->exceptions = array(); + foreach ($exceptions as $exception) { + $this->add($exception); + } + + return $this; + } + + /** + * Add exceptions to the collection + * + * @param ExceptionCollection|\Exception $e Exception to add + * + * @return ExceptionCollection; + */ + public function add($e) + { + $this->exceptions[] = $e; + if ($this->message) { + $this->message .= "\n"; + } + + $this->message .= $this->getExceptionMessage($e, 0); + + return $this; + } + + /** + * Get the total number of request exceptions + * + * @return int + */ + public function count() + { + return count($this->exceptions); + } + + /** + * Allows array-like iteration over the request exceptions + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->exceptions); + } + + /** + * Get the first exception in the collection + * + * @return \Exception + */ + public function getFirst() + { + return $this->exceptions ? $this->exceptions[0] : null; + } + + private function getExceptionMessage(\Exception $e, $depth = 0) + { + static $sp = ' '; + $prefix = $depth ? str_repeat($sp, $depth) : ''; + $message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n"; + + if ($e instanceof self) { + if ($e->shortMessage) { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n"; + } + foreach ($e as $ee) { + $message .= "\n" . $this->getExceptionMessage($ee, $depth + 1); + } + } else { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n"; + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n"; + } + + return str_replace(getcwd(), '.', $message); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php new file mode 100644 index 000000000..458e6f2ea --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php @@ -0,0 +1,8 @@ +=5.3.2", + "symfony/event-dispatcher": ">=2.1" + }, + "autoload": { + "psr-0": { "Guzzle\\Common": "" } + }, + "target-dir": "Guzzle/Common", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php new file mode 100644 index 000000000..5005a887c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php @@ -0,0 +1,221 @@ +body = $body; + } + + public function __toString() + { + return (string) $this->body; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->body, $method), $args); + } + + public function close() + { + return $this->body->close(); + } + + public function setRewindFunction($callable) + { + $this->body->setRewindFunction($callable); + + return $this; + } + + public function rewind() + { + return $this->body->rewind(); + } + + public function compress($filter = 'zlib.deflate') + { + return $this->body->compress($filter); + } + + public function uncompress($filter = 'zlib.inflate') + { + return $this->body->uncompress($filter); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->body->getContentType(); + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + $hash = Stream::getHash($this, 'md5', $rawOutput); + + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } + + public function getContentEncoding() + { + return $this->body->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->body->getMetaData($key); + } + + public function getStream() + { + return $this->body->getStream(); + } + + public function setStream($stream, $size = 0) + { + $this->body->setStream($stream, $size); + + return $this; + } + + public function detachStream() + { + $this->body->detachStream(); + + return $this; + } + + public function getWrapper() + { + return $this->body->getWrapper(); + } + + public function getWrapperData() + { + return $this->body->getWrapperData(); + } + + public function getStreamType() + { + return $this->body->getStreamType(); + } + + public function getUri() + { + return $this->body->getUri(); + } + + public function getSize() + { + return $this->body->getSize(); + } + + public function isReadable() + { + return $this->body->isReadable(); + } + + public function isRepeatable() + { + return $this->isSeekable() && $this->isReadable(); + } + + public function isWritable() + { + return $this->body->isWritable(); + } + + public function isConsumed() + { + return $this->body->isConsumed(); + } + + /** + * Alias of isConsumed() + * {@inheritdoc} + */ + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->body->isLocal(); + } + + public function isSeekable() + { + return $this->body->isSeekable(); + } + + public function setSize($size) + { + $this->body->setSize($size); + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->body->seek($offset, $whence); + } + + public function read($length) + { + return $this->body->read($length); + } + + public function write($string) + { + return $this->body->write($string); + } + + public function readLine($maxLength = null) + { + return $this->body->readLine($maxLength); + } + + public function ftell() + { + return $this->body->ftell(); + } + + public function getCustomData($key) + { + return $this->body->getCustomData($key); + } + + public function setCustomData($key, $value) + { + $this->body->setCustomData($key, $value); + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php new file mode 100644 index 000000000..c65c13650 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php @@ -0,0 +1,229 @@ +remoteStream = $body; + $this->body = new EntityBody(fopen('php://temp', 'r+')); + } + + /** + * Will give the contents of the buffer followed by the exhausted remote stream. + * + * Warning: Loads the entire stream into memory + * + * @return string + */ + public function __toString() + { + $pos = $this->ftell(); + $this->rewind(); + + $str = ''; + while (!$this->isConsumed()) { + $str .= $this->read(16384); + } + + $this->seek($pos); + + return $str; + } + + public function getSize() + { + return max($this->body->getSize(), $this->remoteStream->getSize()); + } + + /** + * {@inheritdoc} + * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->ftell(); + } else { + throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations'); + } + + // You cannot skip ahead past where you've read from the remote stream + if ($byte > $this->body->getSize()) { + throw new RuntimeException( + "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes" + ); + } + + return $this->body->seek($byte); + } + + public function rewind() + { + return $this->seek(0); + } + + /** + * Does not support custom rewind functions + * + * @throws RuntimeException + */ + public function setRewindFunction($callable) + { + throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions'); + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->body->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have been filled from the remote stream, + // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This + // mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->body->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want to skip bytes from being read from + // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length. + $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->body->write($string); + } + + /** + * {@inheritdoc} + * @link http://php.net/manual/en/function.fgets.php + */ + public function readLine($maxLength = null) + { + $buffer = ''; + $size = 0; + while (!$this->isConsumed()) { + $byte = $this->read(1); + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte == PHP_EOL || ++$size == $maxLength - 1) { + break; + } + } + + return $buffer; + } + + public function isConsumed() + { + return $this->body->isConsumed() && $this->remoteStream->isConsumed(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + return $this->remoteStream->close() && $this->body->close(); + } + + public function setStream($stream, $size = 0) + { + $this->remoteStream->setStream($stream, $size); + } + + public function getContentType() + { + return $this->remoteStream->getContentType(); + } + + public function getContentEncoding() + { + return $this->remoteStream->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->remoteStream->getMetaData($key); + } + + public function getStream() + { + return $this->remoteStream->getStream(); + } + + public function getWrapper() + { + return $this->remoteStream->getWrapper(); + } + + public function getWrapperData() + { + return $this->remoteStream->getWrapperData(); + } + + public function getStreamType() + { + return $this->remoteStream->getStreamType(); + } + + public function getUri() + { + return $this->remoteStream->getUri(); + } + + /** + * Always retrieve custom data from the remote stream + * {@inheritdoc} + */ + public function getCustomData($key) + { + return $this->remoteStream->getCustomData($key); + } + + /** + * Always set custom data on the remote stream + * {@inheritdoc} + */ + public function setCustomData($key, $value) + { + $this->remoteStream->setCustomData($key, $value); + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php new file mode 100644 index 000000000..3d7298dcd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php @@ -0,0 +1,524 @@ +setConfig($config ?: new Collection()); + $this->initSsl(); + $this->setBaseUrl($baseUrl); + $this->defaultHeaders = new Collection(); + $this->setRequestFactory(RequestFactory::getInstance()); + $this->userAgent = $this->getDefaultUserAgent(); + if (!$this->config[self::DISABLE_REDIRECTS]) { + $this->addSubscriber(new RedirectPlugin()); + } + } + + final public function setConfig($config) + { + if ($config instanceof Collection) { + $this->config = $config; + } elseif (is_array($config)) { + $this->config = new Collection($config); + } else { + throw new InvalidArgumentException('Config must be an array or Collection'); + } + + return $this; + } + + final public function getConfig($key = false) + { + return $key ? $this->config[$key] : $this->config; + } + + /** + * Set a default request option on the client that will be used as a default for each request + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * @param mixed $value Value to set + * + * @return $this + */ + public function setDefaultOption($keyOrPath, $value) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + $this->config->setPath($keyOrPath, $value); + + return $this; + } + + /** + * Retrieve a default request option from the client + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * + * @return mixed|null + */ + public function getDefaultOption($keyOrPath) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + + return $this->config->getPath($keyOrPath); + } + + final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2) + { + $opts = $this->config[self::CURL_OPTIONS] ?: array(); + + if ($certificateAuthority === true) { + // use bundled CA bundle, set secure defaults + $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem'; + $opts[CURLOPT_SSL_VERIFYPEER] = true; + $opts[CURLOPT_SSL_VERIFYHOST] = 2; + } elseif ($certificateAuthority === false) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_SSL_VERIFYPEER] = false; + $opts[CURLOPT_SSL_VERIFYHOST] = 0; + } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) { + throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean'); + } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) { + throw new InvalidArgumentException('verifyHost must be 0, 1 or 2'); + } else { + $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer; + $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost; + if (is_file($certificateAuthority)) { + unset($opts[CURLOPT_CAPATH]); + $opts[CURLOPT_CAINFO] = $certificateAuthority; + } elseif (is_dir($certificateAuthority)) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_CAPATH] = $certificateAuthority; + } else { + throw new RuntimeException( + 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority + ); + } + } + + $this->config->set(self::CURL_OPTIONS, $opts); + + return $this; + } + + public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) + { + if (!$uri) { + $url = $this->getBaseUrl(); + } else { + if (!is_array($uri)) { + $templateVars = null; + } else { + list($uri, $templateVars) = $uri; + } + if (strpos($uri, '://')) { + // Use absolute URLs as-is + $url = $this->expandTemplate($uri, $templateVars); + } else { + $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); + } + } + + // If default headers are provided, then merge them under any explicitly provided headers for the request + if (count($this->defaultHeaders)) { + if (!$headers) { + $headers = $this->defaultHeaders->toArray(); + } elseif (is_array($headers)) { + $headers += $this->defaultHeaders->toArray(); + } elseif ($headers instanceof Collection) { + $headers = $headers->toArray() + $this->defaultHeaders->toArray(); + } + } + + return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); + } + + public function getBaseUrl($expand = true) + { + return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; + } + + public function setBaseUrl($url) + { + $this->baseUrl = $url; + + return $this; + } + + public function setUserAgent($userAgent, $includeDefault = false) + { + if ($includeDefault) { + $userAgent .= ' ' . $this->getDefaultUserAgent(); + } + $this->userAgent = $userAgent; + + return $this; + } + + /** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ + public function getDefaultUserAgent() + { + return 'Guzzle/' . Version::VERSION + . ' curl/' . CurlVersion::getInstance()->get('version') + . ' PHP/' . PHP_VERSION; + } + + public function get($uri = null, $headers = null, $options = array()) + { + // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded + return is_array($options) + ? $this->createRequest('GET', $uri, $headers, null, $options) + : $this->createRequest('GET', $uri, $headers, $options); + } + + public function head($uri = null, $headers = null, array $options = array()) + { + return $this->createRequest('HEAD', $uri, $headers, null, $options); + } + + public function delete($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('DELETE', $uri, $headers, $body, $options); + } + + public function put($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PUT', $uri, $headers, $body, $options); + } + + public function patch($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PATCH', $uri, $headers, $body, $options); + } + + public function post($uri = null, $headers = null, $postBody = null, array $options = array()) + { + return $this->createRequest('POST', $uri, $headers, $postBody, $options); + } + + public function options($uri = null, array $options = array()) + { + return $this->createRequest('OPTIONS', $uri, $options); + } + + public function send($requests) + { + if (!($requests instanceof RequestInterface)) { + return $this->sendMultiple($requests); + } + + try { + /** @var $requests RequestInterface */ + $this->getCurlMulti()->add($requests)->send(); + return $requests->getResponse(); + } catch (ExceptionCollection $e) { + throw $e->getFirst(); + } + } + + /** + * Set a curl multi object to be used internally by the client for transferring requests. + * + * @param CurlMultiInterface $curlMulti Multi object + * + * @return self + */ + public function setCurlMulti(CurlMultiInterface $curlMulti) + { + $this->curlMulti = $curlMulti; + + return $this; + } + + /** + * @return CurlMultiInterface|CurlMultiProxy + */ + public function getCurlMulti() + { + if (!$this->curlMulti) { + $this->curlMulti = new CurlMultiProxy( + self::MAX_HANDLES, + $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT + ); + } + + return $this->curlMulti; + } + + public function setRequestFactory(RequestFactoryInterface $factory) + { + $this->requestFactory = $factory; + + return $this; + } + + /** + * Set the URI template expander to use with the client + * + * @param UriTemplateInterface $uriTemplate URI template expander + * + * @return self + */ + public function setUriTemplate(UriTemplateInterface $uriTemplate) + { + $this->uriTemplate = $uriTemplate; + + return $this; + } + + /** + * Expand a URI template while merging client config settings into the template variables + * + * @param string $template Template to expand + * @param array $variables Variables to inject + * + * @return string + */ + protected function expandTemplate($template, array $variables = null) + { + $expansionVars = $this->getConfig()->toArray(); + if ($variables) { + $expansionVars = $variables + $expansionVars; + } + + return $this->getUriTemplate()->expand($template, $expansionVars); + } + + /** + * Get the URI template expander used by the client + * + * @return UriTemplateInterface + */ + protected function getUriTemplate() + { + if (!$this->uriTemplate) { + $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template'); + } + + return $this->uriTemplate; + } + + /** + * Send multiple requests in parallel + * + * @param array $requests Array of RequestInterface objects + * + * @return array Returns an array of Response objects + */ + protected function sendMultiple(array $requests) + { + $curlMulti = $this->getCurlMulti(); + foreach ($requests as $request) { + $curlMulti->add($request); + } + $curlMulti->send(); + + /** @var $request RequestInterface */ + $result = array(); + foreach ($requests as $request) { + $result[] = $request->getResponse(); + } + + return $result; + } + + /** + * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. + * + * @param RequestInterface $request Request to prepare for the client + * @param array $options Options to apply to the request + * + * @return RequestInterface + */ + protected function prepareRequest(RequestInterface $request, array $options = array()) + { + $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); + + if ($curl = $this->config[self::CURL_OPTIONS]) { + $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); + } + + if ($params = $this->config[self::REQUEST_PARAMS]) { + Version::warn('request.params is deprecated. Use request.options to add default request options.'); + $request->getParams()->overwriteWith($params); + } + + if ($this->userAgent && !$request->hasHeader('User-Agent')) { + $request->setHeader('User-Agent', $this->userAgent); + } + + if ($defaults = $this->config[self::REQUEST_OPTIONS]) { + $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); + } + + if ($options) { + $this->requestFactory->applyOptions($request, $options); + } + + $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); + + return $request; + } + + /** + * Initializes SSL settings + */ + protected function initSsl() + { + $authority = $this->config[self::SSL_CERT_AUTHORITY]; + + if ($authority === 'system') { + return; + } + + if ($authority === null) { + $authority = true; + } + + if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') { + $authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem'); + } + + $this->setSslVerification($authority); + } + + /** + * @deprecated + */ + public function getDefaultHeaders() + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options'); + return $this->defaultHeaders; + } + + /** + * @deprecated + */ + public function setDefaultHeaders($headers) + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options'); + if ($headers instanceof Collection) { + $this->defaultHeaders = $headers; + } elseif (is_array($headers)) { + $this->defaultHeaders = new Collection($headers); + } else { + throw new InvalidArgumentException('Headers must be an array or Collection'); + } + + return $this; + } + + /** + * @deprecated + */ + public function preparePharCacert($md5Check = true) + { + return sys_get_temp_dir() . '/guzzle-cacert.pem'; + } + + /** + * Copies the phar cacert from a phar into the temp directory. + * + * @param string $pharCacertPath Path to the phar cacert. For example: + * 'phar://aws.phar/Guzzle/Http/Resources/cacert.pem' + * + * @return string Returns the path to the extracted cacert file. + * @throws \RuntimeException Throws if the phar cacert cannot be found or + * the file cannot be copied to the temp dir. + */ + public static function extractPharCacert($pharCacertPath) + { + // Copy the cacert.pem file from the phar if it is not in the temp + // folder. + $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem'; + + if (!file_exists($pharCacertPath)) { + throw new \RuntimeException("Could not find $pharCacertPath"); + } + + if (!file_exists($certFile) || + filesize($certFile) != filesize($pharCacertPath) + ) { + if (!copy($pharCacertPath, $certFile)) { + throw new \RuntimeException( + "Could not copy {$pharCacertPath} to {$certFile}: " + . var_export(error_get_last(), true) + ); + } + } + + return $certFile; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php new file mode 100644 index 000000000..10e4de2ab --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php @@ -0,0 +1,223 @@ +getCurlOptions(); + $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io')); + $tempContentLength = null; + $method = $request->getMethod(); + $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING); + + // Prepare url + $url = (string)$request->getUrl(); + if(($pos = strpos($url, '#')) !== false ){ + // strip fragment from url + $url = substr($url, 0, $pos); + } + + // Array of default cURL options. + $curlOptions = array( + CURLOPT_URL => $url, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_PORT => $request->getPort(), + CURLOPT_HTTPHEADER => array(), + CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), + CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), + CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' + ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, + // Verifies the authenticity of the peer's certificate + CURLOPT_SSL_VERIFYPEER => 1, + // Certificate must indicate that the server is the server to which you meant to connect + CURLOPT_SSL_VERIFYHOST => 2 + ); + + if (defined('CURLOPT_PROTOCOLS')) { + // Allow only HTTP and HTTPS protocols + $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + // Add CURLOPT_ENCODING if Accept-Encoding header is provided + if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { + $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; + // Let cURL set the Accept-Encoding header, prevents duplicate values + $request->removeHeader('Accept-Encoding'); + } + + // Enable curl debug information if the 'debug' param was set + if ($requestCurlOptions->get('debug')) { + $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); + // @codeCoverageIgnoreStart + if (false === $curlOptions[CURLOPT_STDERR]) { + throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); + } + // @codeCoverageIgnoreEnd + $curlOptions[CURLOPT_VERBOSE] = true; + } + + // Specify settings according to the HTTP method + if ($method == 'GET') { + $curlOptions[CURLOPT_HTTPGET] = true; + } elseif ($method == 'HEAD') { + $curlOptions[CURLOPT_NOBODY] = true; + // HEAD requests do not use a write function + unset($curlOptions[CURLOPT_WRITEFUNCTION]); + } elseif (!($request instanceof EntityEnclosingRequest)) { + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + } else { + + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + + // Handle sending raw bodies in a request + if ($request->getBody()) { + // You can send the body as a string using curl's CURLOPT_POSTFIELDS + if ($bodyAsString) { + $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + // Allow curl to add the Content-Length for us to account for the times when + // POST redirects are followed by GET requests + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + } + // Remove the curl generated Content-Type header if none was set manually + if (!$request->hasHeader('Content-Type')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } else { + $curlOptions[CURLOPT_UPLOAD] = true; + // Let cURL handle setting the Content-Length header + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; + } + // Add a callback for curl to read data to send with the request only if a body was specified + $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); + // Attempt to seek to the start of the stream + $request->getBody()->seek(0); + } + + } else { + + // Special handling for POST specific fields and files + $postFields = false; + if (count($request->getPostFiles())) { + $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode(); + foreach ($request->getPostFiles() as $key => $data) { + $prefixKeys = count($data) > 1; + foreach ($data as $index => $file) { + // Allow multiple files in the same key + $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; + $postFields[$fieldKey] = $file->getCurlValue(); + } + } + } elseif (count($request->getPostFields())) { + $postFields = (string) $request->getPostFields()->useUrlEncoding(true); + } + + if ($postFields !== false) { + if ($method == 'POST') { + unset($curlOptions[CURLOPT_CUSTOMREQUEST]); + $curlOptions[CURLOPT_POST] = true; + } + $curlOptions[CURLOPT_POSTFIELDS] = $postFields; + $request->removeHeader('Content-Length'); + } + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + } + + // If a Content-Length header was specified but we want to allow curl to set one for us + if (null !== $tempContentLength) { + $request->removeHeader('Content-Length'); + } + + // Set custom cURL options + foreach ($requestCurlOptions->toArray() as $key => $value) { + if (is_numeric($key)) { + $curlOptions[$key] = $value; + } + } + + // Do not set an Accept header by default + if (!isset($curlOptions[CURLOPT_ENCODING])) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + + // Add any custom headers to the request. Empty headers will cause curl to not send the header at all. + foreach ($request->getHeaderLines() as $line) { + $curlOptions[CURLOPT_HTTPHEADER][] = $line; + } + + // Add the content-length header back if it was temporarily removed + if (null !== $tempContentLength) { + $request->setHeader('Content-Length', $tempContentLength); + } + + // Apply the options to a new cURL handle. + $handle = curl_init(); + + // Enable the progress function if the 'progress' param was set + if ($requestCurlOptions->get('progress')) { + // Wrap the function in a function that provides the curl handle to the mediator's progress function + // Using this rather than injecting the handle into the mediator prevents a circular reference + $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) { + $args = func_get_args(); + $args[] = $handle; + + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + + call_user_func_array(array($mediator, 'progress'), $args); + }; + $curlOptions[CURLOPT_NOPROGRESS] = false; + } + + curl_setopt_array($handle, $curlOptions); + + return new static($handle, $curlOptions); + } + + /** + * Construct a new CurlHandle object that wraps a cURL handle + * + * @param resource $handle Configured cURL handle resource + * @param Collection|array $options Curl options to use with the handle + * + * @throws InvalidArgumentException + */ + public function __construct($handle, $options) + { + if (!is_resource($handle)) { + throw new InvalidArgumentException('Invalid handle provided'); + } + if (is_array($options)) { + $this->options = new Collection($options); + } elseif ($options instanceof Collection) { + $this->options = $options; + } else { + throw new InvalidArgumentException('Expected array or Collection'); + } + $this->handle = $handle; + } + + /** + * Destructor + */ + public function __destruct() + { + $this->close(); + } + + /** + * Close the curl handle + */ + public function close() + { + if (is_resource($this->handle)) { + curl_close($this->handle); + } + $this->handle = null; + } + + /** + * Check if the handle is available and still OK + * + * @return bool + */ + public function isAvailable() + { + return is_resource($this->handle); + } + + /** + * Get the last error that occurred on the cURL handle + * + * @return string + */ + public function getError() + { + return $this->isAvailable() ? curl_error($this->handle) : ''; + } + + /** + * Get the last error number that occurred on the cURL handle + * + * @return int + */ + public function getErrorNo() + { + if ($this->errorNo) { + return $this->errorNo; + } + + return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK; + } + + /** + * Set the curl error number + * + * @param int $error Error number to set + * + * @return CurlHandle + */ + public function setErrorNo($error) + { + $this->errorNo = $error; + + return $this; + } + + /** + * Get cURL curl_getinfo data + * + * @param int $option Option to retrieve. Pass null to retrieve all data as an array. + * + * @return array|mixed + */ + public function getInfo($option = null) + { + if (!is_resource($this->handle)) { + return null; + } + + if (null !== $option) { + return curl_getinfo($this->handle, $option) ?: null; + } + + return curl_getinfo($this->handle) ?: array(); + } + + /** + * Get the stderr output + * + * @param bool $asResource Set to TRUE to get an fopen resource + * + * @return string|resource|null + */ + public function getStderr($asResource = false) + { + $stderr = $this->getOptions()->get(CURLOPT_STDERR); + if (!$stderr) { + return null; + } + + if ($asResource) { + return $stderr; + } + + fseek($stderr, 0); + $e = stream_get_contents($stderr); + fseek($stderr, 0, SEEK_END); + + return $e; + } + + /** + * Get the URL that this handle is connecting to + * + * @return Url + */ + public function getUrl() + { + return Url::factory($this->options->get(CURLOPT_URL)); + } + + /** + * Get the wrapped curl handle + * + * @return resource|null Returns the cURL handle or null if it was closed + */ + public function getHandle() + { + return $this->isAvailable() ? $this->handle : null; + } + + /** + * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl + * handle after it is created. + * + * @return Collection + */ + public function getOptions() + { + return $this->options; + } + + /** + * Update a request based on the log messages of the CurlHandle + * + * @param RequestInterface $request Request to update + */ + public function updateRequestFromTransfer(RequestInterface $request) + { + if (!$request->getResponse()) { + return; + } + + // Update the transfer stats of the response + $request->getResponse()->setInfo($this->getInfo()); + + if (!$log = $this->getStderr(true)) { + return; + } + + // Parse the cURL stderr output for outgoing requests + $headers = ''; + fseek($log, 0); + while (($line = fgets($log)) !== false) { + if ($line && $line[0] == '>') { + $headers = substr(trim($line), 2) . "\r\n"; + while (($line = fgets($log)) !== false) { + if ($line[0] == '*' || $line[0] == '<') { + break; + } else { + $headers .= trim($line) . "\r\n"; + } + } + } + } + + // Add request headers to the request exactly as they were sent + if ($headers) { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers); + if (!empty($parsed['headers'])) { + $request->setHeaders(array()); + foreach ($parsed['headers'] as $name => $value) { + $request->setHeader($name, $value); + } + } + if (!empty($parsed['version'])) { + $request->setProtocolVersion($parsed['version']); + } + } + } + + /** + * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere + * + * @param array|Collection $config The configuration we want to parse + * + * @return array + */ + public static function parseCurlConfig($config) + { + $curlOptions = array(); + foreach ($config as $key => $value) { + if (is_string($key) && defined($key)) { + // Convert constants represented as string to constant int values + $key = constant($key); + } + if (is_string($value) && defined($value)) { + $value = constant($value); + } + $curlOptions[$key] = $value; + } + + return $curlOptions; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php new file mode 100644 index 000000000..9e4e63722 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php @@ -0,0 +1,423 @@ + array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), + CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), + CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), + CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') + ); + + /** @var float */ + protected $selectTimeout; + + public function __construct($selectTimeout = 1.0) + { + $this->selectTimeout = $selectTimeout; + $this->multiHandle = curl_multi_init(); + // @codeCoverageIgnoreStart + if ($this->multiHandle === false) { + throw new CurlException('Unable to create multi handle'); + } + // @codeCoverageIgnoreEnd + $this->reset(); + } + + public function __destruct() + { + if (is_resource($this->multiHandle)) { + curl_multi_close($this->multiHandle); + } + } + + public function add(RequestInterface $request) + { + $this->requests[] = $request; + // If requests are currently transferring and this is async, then the + // request must be prepared now as the send() method is not called. + $this->beforeSend($request); + $this->dispatch(self::ADD_REQUEST, array('request' => $request)); + + return $this; + } + + public function all() + { + return $this->requests; + } + + public function remove(RequestInterface $request) + { + $this->removeHandle($request); + if (($index = array_search($request, $this->requests, true)) !== false) { + $request = $this->requests[$index]; + unset($this->requests[$index]); + $this->requests = array_values($this->requests); + $this->dispatch(self::REMOVE_REQUEST, array('request' => $request)); + return true; + } + + return false; + } + + public function reset($hard = false) + { + // Remove each request + if ($this->requests) { + foreach ($this->requests as $request) { + $this->remove($request); + } + } + + $this->handles = new \SplObjectStorage(); + $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array(); + } + + public function send() + { + $this->perform(); + $exceptions = $this->exceptions; + $successful = $this->successful; + $this->reset(); + + if ($exceptions) { + $this->throwMultiException($exceptions, $successful); + } + } + + public function count() + { + return count($this->requests); + } + + /** + * Build and throw a MultiTransferException + * + * @param array $exceptions Exceptions encountered + * @param array $successful Successful requests + * @throws MultiTransferException + */ + protected function throwMultiException(array $exceptions, array $successful) + { + $multiException = new MultiTransferException('Errors during multi transfer'); + + while ($e = array_shift($exceptions)) { + $multiException->addFailedRequestWithException($e['request'], $e['exception']); + } + + // Add successful requests + foreach ($successful as $request) { + if (!$multiException->containsRequest($request)) { + $multiException->addSuccessfulRequest($request); + } + } + + throw $multiException; + } + + /** + * Prepare for sending + * + * @param RequestInterface $request Request to prepare + * @throws \Exception on error preparing the request + */ + protected function beforeSend(RequestInterface $request) + { + try { + $state = $request->setState(RequestInterface::STATE_TRANSFER); + if ($state == RequestInterface::STATE_TRANSFER) { + $this->addHandle($request); + } else { + // Requests might decide they don't need to be sent just before + // transfer (e.g. CachePlugin) + $this->remove($request); + if ($state == RequestInterface::STATE_COMPLETE) { + $this->successful[] = $request; + } + } + } catch (\Exception $e) { + // Queue the exception to be thrown when sent + $this->removeErroredRequest($request, $e); + } + } + + private function addHandle(RequestInterface $request) + { + $handle = $this->createCurlHandle($request)->getHandle(); + $this->checkCurlResult( + curl_multi_add_handle($this->multiHandle, $handle) + ); + } + + /** + * Create a curl handle for a request + * + * @param RequestInterface $request Request + * + * @return CurlHandle + */ + protected function createCurlHandle(RequestInterface $request) + { + $wrapper = CurlHandle::factory($request); + $this->handles[$request] = $wrapper; + $this->resourceHash[(int) $wrapper->getHandle()] = $request; + + return $wrapper; + } + + /** + * Get the data from the multi handle + */ + protected function perform() + { + $event = new Event(array('curl_multi' => $this)); + + while ($this->requests) { + // Notify each request as polling + $blocking = $total = 0; + foreach ($this->requests as $request) { + ++$total; + $event['request'] = $request; + $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event); + // The blocking variable just has to be non-falsey to block the loop + if ($request->getParams()->hasKey(self::BLOCKING)) { + ++$blocking; + } + } + if ($blocking == $total) { + // Sleep to prevent eating CPU because no requests are actually pending a select call + usleep(500); + } else { + $this->executeHandles(); + } + } + } + + /** + * Execute and select curl handles + */ + private function executeHandles() + { + // The first curl_multi_select often times out no matter what, but is usually required for fast transfers + $selectTimeout = 0.001; + $active = false; + do { + while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM); + $this->checkCurlResult($mrc); + $this->processMessages(); + if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) { + // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141 + usleep(150); + } + $selectTimeout = $this->selectTimeout; + } while ($active); + } + + /** + * Process any received curl multi messages + */ + private function processMessages() + { + while ($done = curl_multi_info_read($this->multiHandle)) { + $request = $this->resourceHash[(int) $done['handle']]; + try { + $this->processResponse($request, $this->handles[$request], $done); + $this->successful[] = $request; + } catch (\Exception $e) { + $this->removeErroredRequest($request, $e); + } + } + } + + /** + * Remove a request that encountered an exception + * + * @param RequestInterface $request Request to remove + * @param \Exception $e Exception encountered + */ + protected function removeErroredRequest(RequestInterface $request, \Exception $e = null) + { + $this->exceptions[] = array('request' => $request, 'exception' => $e); + $this->remove($request); + $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions)); + } + + /** + * Check for errors and fix headers of a request based on a curl response + * + * @param RequestInterface $request Request to process + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @throws CurlException on Curl error + */ + protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) + { + // Set the transfer stats on the response + $handle->updateRequestFromTransfer($request); + // Check if a cURL exception occurred, and if so, notify things + $curlException = $this->isCurlException($request, $handle, $curl); + + // Always remove completed curl handles. They can be added back again + // via events if needed (e.g. ExponentialBackoffPlugin) + $this->removeHandle($request); + + if (!$curlException) { + if ($this->validateResponseWasSet($request)) { + $state = $request->setState( + RequestInterface::STATE_COMPLETE, + array('handle' => $handle) + ); + // Only remove the request if it wasn't resent as a result of + // the state change + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + } + return; + } + + // Set the state of the request to an error + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException)); + // Allow things to ignore the error if possible + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + + // The error was not handled, so fail + if ($state == RequestInterface::STATE_ERROR) { + /** @var CurlException $curlException */ + throw $curlException; + } + } + + /** + * Remove a curl handle from the curl multi object + * + * @param RequestInterface $request Request that owns the handle + */ + protected function removeHandle(RequestInterface $request) + { + if (isset($this->handles[$request])) { + $handle = $this->handles[$request]; + curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); + unset($this->handles[$request]); + unset($this->resourceHash[(int) $handle->getHandle()]); + $handle->close(); + } + } + + /** + * Check if a cURL transfer resulted in what should be an exception + * + * @param RequestInterface $request Request to check + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @return CurlException|bool + */ + private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) + { + if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) { + return false; + } + + $handle->setErrorNo($curl['result']); + $e = new CurlException(sprintf('[curl] %s: %s [url] %s', + $handle->getErrorNo(), $handle->getError(), $handle->getUrl())); + $e->setCurlHandle($handle) + ->setRequest($request) + ->setCurlInfo($handle->getInfo()) + ->setError($handle->getError(), $handle->getErrorNo()); + + return $e; + } + + /** + * Throw an exception for a cURL multi response if needed + * + * @param int $code Curl response code + * @throws CurlException + */ + private function checkCurlResult($code) + { + if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { + throw new CurlException(isset($this->multiErrors[$code]) + ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" + : 'Unexpected cURL error: ' . $code + ); + } + } + + /** + * @link https://github.com/guzzle/guzzle/issues/710 + */ + private function validateResponseWasSet(RequestInterface $request) + { + if ($request->getResponse()) { + return true; + } + + $body = $request instanceof EntityEnclosingRequestInterface + ? $request->getBody() + : null; + + if (!$body) { + $rex = new RequestException( + 'No response was received for a request with no body. This' + . ' could mean that you are saturating your network.' + ); + $rex->setRequest($request); + $this->removeErroredRequest($request, $rex); + } elseif (!$body->isSeekable() || !$body->seek(0)) { + // Nothing we can do with this. Sorry! + $rex = new RequestException( + 'The connection was unexpectedly closed. The request would' + . ' have been retried, but attempting to rewind the' + . ' request body failed.' + ); + $rex->setRequest($request); + $this->removeErroredRequest($request, $rex); + } else { + $this->remove($request); + // Add the request back to the batch to retry automatically. + $this->requests[] = $request; + $this->addHandle($request); + } + + return false; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php new file mode 100644 index 000000000..0ead75735 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php @@ -0,0 +1,58 @@ +maxHandles = $maxHandles; + $this->selectTimeout = $selectTimeout; + // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel. + // These two statements autoload classes before a system runs out of file descriptors so that you can get back + // valuable error messages if you run out. + class_exists('Guzzle\Http\Message\Response'); + class_exists('Guzzle\Http\Exception\CurlException'); + } + + public function add(RequestInterface $request) + { + $this->queued[] = $request; + + return $this; + } + + public function all() + { + $requests = $this->queued; + foreach ($this->handles as $handle) { + $requests = array_merge($requests, $handle->all()); + } + + return $requests; + } + + public function remove(RequestInterface $request) + { + foreach ($this->queued as $i => $r) { + if ($request === $r) { + unset($this->queued[$i]); + return true; + } + } + + foreach ($this->handles as $handle) { + if ($handle->remove($request)) { + return true; + } + } + + return false; + } + + public function reset($hard = false) + { + $this->queued = array(); + $this->groups = array(); + foreach ($this->handles as $handle) { + $handle->reset(); + } + if ($hard) { + $this->handles = array(); + } + + return $this; + } + + public function send() + { + if ($this->queued) { + $group = $this->getAvailableHandle(); + // Add this handle to a list of handles than is claimed + $this->groups[] = $group; + while ($request = array_shift($this->queued)) { + $group->add($request); + } + try { + $group->send(); + array_pop($this->groups); + $this->cleanupHandles(); + } catch (\Exception $e) { + // Remove the group and cleanup if an exception was encountered and no more requests in group + if (!$group->count()) { + array_pop($this->groups); + $this->cleanupHandles(); + } + throw $e; + } + } + } + + public function count() + { + return count($this->all()); + } + + /** + * Get an existing available CurlMulti handle or create a new one + * + * @return CurlMulti + */ + protected function getAvailableHandle() + { + // Grab a handle that is not claimed + foreach ($this->handles as $h) { + if (!in_array($h, $this->groups, true)) { + return $h; + } + } + + // All are claimed, so create one + $handle = new CurlMulti($this->selectTimeout); + $handle->setEventDispatcher($this->getEventDispatcher()); + $this->handles[] = $handle; + + return $handle; + } + + /** + * Trims down unused CurlMulti handles to limit the number of open connections + */ + protected function cleanupHandles() + { + if ($diff = max(0, count($this->handles) - $this->maxHandles)) { + for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) { + if (!count($this->handles[$i])) { + unset($this->handles[$i]); + $diff--; + } + } + $this->handles = array_values($this->handles); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php new file mode 100644 index 000000000..c3f99dd25 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php @@ -0,0 +1,66 @@ +version) { + $this->version = curl_version(); + } + + return $this->version; + } + + /** + * Get a specific type of curl information + * + * @param string $type Version information to retrieve. This value is one of: + * - version_number: cURL 24 bit version number + * - version: cURL version number, as a string + * - ssl_version_number: OpenSSL 24 bit version number + * - ssl_version: OpenSSL version number, as a string + * - libz_version: zlib version number, as a string + * - host: Information about the host where cURL was built + * - features: A bitmask of the CURL_VERSION_XXX constants + * - protocols: An array of protocols names supported by cURL + * + * @return string|float|bool if the $type is found, and false if not found + */ + public function get($type) + { + $version = $this->getAll(); + + return isset($version[$type]) ? $version[$type] : false; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php new file mode 100644 index 000000000..5d1a0cd87 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php @@ -0,0 +1,147 @@ +request = $request; + $this->emitIo = $emitIo; + } + + /** + * Receive a response header from curl + * + * @param resource $curl Curl handle + * @param string $header Received header + * + * @return int + */ + public function receiveResponseHeader($curl, $header) + { + static $normalize = array("\r", "\n"); + $length = strlen($header); + $header = str_replace($normalize, '', $header); + + if (strpos($header, 'HTTP/') === 0) { + + $startLine = explode(' ', $header, 3); + $code = $startLine[1]; + $status = isset($startLine[2]) ? $startLine[2] : ''; + + // Only download the body of the response to the specified response + // body when a successful response is received. + if ($code >= 200 && $code < 300) { + $body = $this->request->getResponseBody(); + } else { + $body = EntityBody::factory(); + } + + $response = new Response($code, null, $body); + $response->setStatus($code, $status); + $this->request->startResponse($response); + + $this->request->dispatch('request.receive.status_line', array( + 'request' => $this, + 'line' => $header, + 'status_code' => $code, + 'reason_phrase' => $status + )); + + } elseif ($pos = strpos($header, ':')) { + $this->request->getResponse()->addHeader( + trim(substr($header, 0, $pos)), + trim(substr($header, $pos + 1)) + ); + } + + return $length; + } + + /** + * Received a progress notification + * + * @param int $downloadSize Total download size + * @param int $downloaded Amount of bytes downloaded + * @param int $uploadSize Total upload size + * @param int $uploaded Amount of bytes uploaded + * @param resource $handle CurlHandle object + */ + public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null) + { + $this->request->dispatch('curl.callback.progress', array( + 'request' => $this->request, + 'handle' => $handle, + 'download_size' => $downloadSize, + 'downloaded' => $downloaded, + 'upload_size' => $uploadSize, + 'uploaded' => $uploaded + )); + } + + /** + * Write data to the response body of a request + * + * @param resource $curl Curl handle + * @param string $write Data that was received + * + * @return int + */ + public function writeResponseBody($curl, $write) + { + if ($this->emitIo) { + $this->request->dispatch('curl.callback.write', array( + 'request' => $this->request, + 'write' => $write + )); + } + + if ($response = $this->request->getResponse()) { + return $response->getBody()->write($write); + } else { + // Unexpected data received before response headers - abort transfer + return 0; + } + } + + /** + * Read data from the request body and send it to curl + * + * @param resource $ch Curl handle + * @param resource $fd File descriptor + * @param int $length Amount of data to read + * + * @return string + */ + public function readRequestBody($ch, $fd, $length) + { + if (!($body = $this->request->getBody())) { + return ''; + } + + $read = (string) $body->read($length); + if ($this->emitIo) { + $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); + } + + return $read; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php new file mode 100644 index 000000000..b60d170f0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php @@ -0,0 +1,201 @@ +rewindFunction = $callable; + + return $this; + } + + public function rewind() + { + return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind(); + } + + /** + * Create a new EntityBody from a string + * + * @param string $string String of data + * + * @return EntityBody + */ + public static function fromString($string) + { + $stream = fopen('php://temp', 'r+'); + if ($string !== '') { + fwrite($stream, $string); + rewind($stream); + } + + return new static($stream); + } + + public function compress($filter = 'zlib.deflate') + { + $result = $this->handleCompression($filter); + $this->contentEncoding = $result ? $filter : false; + + return $result; + } + + public function uncompress($filter = 'zlib.inflate') + { + $offsetStart = 0; + + // When inflating gzipped data, the first 10 bytes must be stripped + // if a gzip header is present + if ($filter == 'zlib.inflate') { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") { + $offsetStart = 10; + } + } + + $this->contentEncoding = false; + + return $this->handleCompression($filter, $offsetStart); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null; + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + if ($hash = self::getHash($this, 'md5', $rawOutput)) { + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } else { + return false; + } + } + + /** + * Calculate the MD5 hash of an entity body + * + * @param EntityBodyInterface $body Entity body to calculate the hash for + * @param bool $rawOutput Whether or not to use raw output + * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) + * + * @return bool|string Returns an MD5 string on success or FALSE on failure + * @deprecated This will be deprecated soon + * @codeCoverageIgnore + */ + public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false) + { + Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()'); + return $body->getContentMd5($rawOutput, $base64Encode); + } + + public function setStreamFilterContentEncoding($streamFilterContentEncoding) + { + $this->contentEncoding = $streamFilterContentEncoding; + + return $this; + } + + public function getContentEncoding() + { + return strtr($this->contentEncoding, array( + 'zlib.deflate' => 'gzip', + 'bzip2.compress' => 'compress' + )) ?: false; + } + + protected function handleCompression($filter, $offsetStart = 0) + { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + + $handle = fopen('php://temp', 'r+'); + $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); + if (!$filter) { + return false; + } + + // Seek to the offset start if possible + $this->seek($offsetStart); + while ($data = fread($this->stream, 8096)) { + fwrite($handle, $data); + } + + fclose($this->stream); + $this->stream = $handle; + stream_filter_remove($filter); + $stat = fstat($this->stream); + $this->size = $stat['size']; + $this->rebuildCache(); + $this->seek(0); + + // Remove any existing rewind function as the underlying stream has been replaced + $this->rewindFunction = null; + + return true; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php new file mode 100644 index 000000000..e640f5785 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php @@ -0,0 +1,73 @@ +isClientError()) { + $label = 'Client error response'; + $class = __NAMESPACE__ . '\\ClientErrorResponseException'; + } elseif ($response->isServerError()) { + $label = 'Server error response'; + $class = __NAMESPACE__ . '\\ServerErrorResponseException'; + } else { + $label = 'Unsuccessful response'; + $class = __CLASS__; + } + + $message = $label . PHP_EOL . implode(PHP_EOL, array( + '[status code] ' . $response->getStatusCode(), + '[reason phrase] ' . $response->getReasonPhrase(), + '[url] ' . $request->getUrl(), + )); + + $e = new $class($message); + $e->setResponse($response); + $e->setRequest($request); + + return $e; + } + + /** + * Set the response that caused the exception + * + * @param Response $response Response to set + */ + public function setResponse(Response $response) + { + $this->response = $response; + } + + /** + * Get the response that caused the exception + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php new file mode 100644 index 000000000..04d7ddc05 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php @@ -0,0 +1,8 @@ +curlError = $error; + $this->curlErrorNo = $number; + + return $this; + } + + /** + * Set the associated curl handle + * + * @param CurlHandle $handle Curl handle + * + * @return self + */ + public function setCurlHandle(CurlHandle $handle) + { + $this->handle = $handle; + + return $this; + } + + /** + * Get the associated cURL handle + * + * @return CurlHandle|null + */ + public function getCurlHandle() + { + return $this->handle; + } + + /** + * Get the associated cURL error message + * + * @return string|null + */ + public function getError() + { + return $this->curlError; + } + + /** + * Get the associated cURL error number + * + * @return int|null + */ + public function getErrorNo() + { + return $this->curlErrorNo; + } + + /** + * Returns curl information about the transfer + * + * @return array + */ + public function getCurlInfo() + { + return $this->curlInfo; + } + + /** + * Set curl transfer information + * + * @param array $info Array of curl transfer information + * + * @return self + * @link http://php.net/manual/en/function.curl-getinfo.php + */ + public function setCurlInfo(array $info) + { + $this->curlInfo = $info; + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php new file mode 100644 index 000000000..ee87295d3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php @@ -0,0 +1,10 @@ +successfulRequests, $this->failedRequests); + } + + /** + * Add to the array of successful requests + * + * @param RequestInterface $request Successful request + * + * @return self + */ + public function addSuccessfulRequest(RequestInterface $request) + { + $this->successfulRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests + * + * @param RequestInterface $request Failed request + * + * @return self + */ + public function addFailedRequest(RequestInterface $request) + { + $this->failedRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests and associate with exceptions + * + * @param RequestInterface $request Failed request + * @param \Exception $exception Exception to add and associate with + * + * @return self + */ + public function addFailedRequestWithException(RequestInterface $request, \Exception $exception) + { + $this->add($exception) + ->addFailedRequest($request) + ->exceptionForRequest[spl_object_hash($request)] = $exception; + + return $this; + } + + /** + * Get the Exception that caused the given $request to fail + * + * @param RequestInterface $request Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedRequest(RequestInterface $request) + { + $oid = spl_object_hash($request); + + return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null; + } + + /** + * Set all of the successful requests + * + * @param array Array of requests + * + * @return self + */ + public function setSuccessfulRequests(array $requests) + { + $this->successfulRequests = $requests; + + return $this; + } + + /** + * Set all of the failed requests + * + * @param array Array of requests + * + * @return self + */ + public function setFailedRequests(array $requests) + { + $this->failedRequests = $requests; + + return $this; + } + + /** + * Get an array of successful requests sent in the multi transfer + * + * @return array + */ + public function getSuccessfulRequests() + { + return $this->successfulRequests; + } + + /** + * Get an array of failed requests sent in the multi transfer + * + * @return array + */ + public function getFailedRequests() + { + return $this->failedRequests; + } + + /** + * Check if the exception object contains a request + * + * @param RequestInterface $request Request to check + * + * @return bool + */ + public function containsRequest(RequestInterface $request) + { + return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php new file mode 100644 index 000000000..274df2cb1 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php @@ -0,0 +1,39 @@ +request = $request; + + return $this; + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php new file mode 100644 index 000000000..f0f7cfe48 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php @@ -0,0 +1,8 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + /** + * {@inheritdoc} + * @codeCoverageIgnore + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + public function read($length) + { + $event = array( + 'body' => $this, + 'length' => $length, + 'read' => $this->body->read($length) + ); + $this->dispatch('body.read', $event); + + return $event['read']; + } + + public function write($string) + { + $event = array( + 'body' => $this, + 'write' => $string, + 'result' => $this->body->write($string) + ); + $this->dispatch('body.write', $event); + + return $event['result']; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php new file mode 100644 index 000000000..0d066ffce --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php @@ -0,0 +1,220 @@ +params = new Collection(); + $this->headerFactory = new HeaderFactory(); + $this->headers = new HeaderCollection(); + } + + /** + * Set the header factory to use to create headers + * + * @param HeaderFactoryInterface $factory + * + * @return self + */ + public function setHeaderFactory(HeaderFactoryInterface $factory) + { + $this->headerFactory = $factory; + + return $this; + } + + public function getParams() + { + return $this->params; + } + + public function addHeader($header, $value) + { + if (isset($this->headers[$header])) { + $this->headers[$header]->add($value); + } elseif ($value instanceof HeaderInterface) { + $this->headers[$header] = $value; + } else { + $this->headers[$header] = $this->headerFactory->createHeader($header, $value); + } + + return $this; + } + + public function addHeaders(array $headers) + { + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function getHeader($header) + { + return $this->headers[$header]; + } + + public function getHeaders() + { + return $this->headers; + } + + public function getHeaderLines() + { + $headers = array(); + foreach ($this->headers as $value) { + $headers[] = $value->getName() . ': ' . $value; + } + + return $headers; + } + + public function setHeader($header, $value) + { + unset($this->headers[$header]); + $this->addHeader($header, $value); + + return $this; + } + + public function setHeaders(array $headers) + { + $this->headers->clear(); + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function hasHeader($header) + { + return isset($this->headers[$header]); + } + + public function removeHeader($header) + { + unset($this->headers[$header]); + + return $this; + } + + /** + * @deprecated Use $message->getHeader()->parseParams() + * @codeCoverageIgnore + */ + public function getTokenizedHeader($header, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()'); + if ($this->hasHeader($header)) { + $data = new Collection(); + foreach ($this->getHeader($header)->parseParams() as $values) { + foreach ($values as $key => $value) { + if ($value === '') { + $data->set($data->count(), $key); + } else { + $data->add($key, $value); + } + } + } + return $data; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setTokenizedHeader($header, $data, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated.'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + return null; + } + + return $header->getDirective($directive); + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + return $header->hasDirective($directive); + } else { + return false; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function addCacheControlDirective($directive, $value = true) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + $this->addHeader('Cache-Control', ''); + $header = $this->getHeader('Cache-Control'); + } + + $header->addDirective($directive, $value); + + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function removeCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + $header->removeDirective($directive); + } + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php new file mode 100644 index 000000000..212850a25 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php @@ -0,0 +1,247 @@ +postFields = new QueryString(); + parent::__construct($method, $url, $headers); + } + + /** + * @return string + */ + public function __toString() + { + // Only attempt to include the POST data if it's only fields + if (count($this->postFields) && empty($this->postFiles)) { + return parent::__toString() . (string) $this->postFields; + } + + return parent::__toString() . $this->body; + } + + public function setState($state, array $context = array()) + { + parent::setState($state, $context); + if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) { + $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding'); + } + + return $this->state; + } + + public function setBody($body, $contentType = null) + { + $this->body = EntityBody::factory($body); + + // Auto detect the Content-Type from the path of the request if possible + if ($contentType === null && !$this->hasHeader('Content-Type')) { + $contentType = $this->body->getContentType(); + } + + if ($contentType) { + $this->setHeader('Content-Type', $contentType); + } + + // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects. + if (!$this->body->isSeekable() && $this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + + // Set the Content-Length header if it can be determined + $size = $this->body->getContentLength(); + if ($size !== null && $size !== false) { + $this->setHeader('Content-Length', $size); + if ($size > $this->expectCutoff) { + $this->setHeader('Expect', '100-Continue'); + } + } elseif (!$this->hasHeader('Content-Length')) { + if ('1.1' == $this->protocolVersion) { + $this->setHeader('Transfer-Encoding', 'chunked'); + } else { + throw new RequestException( + 'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0' + ); + } + } + + return $this; + } + + public function getBody() + { + return $this->body; + } + + /** + * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header. + * + * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data) + * + * @return self + */ + public function setExpectHeaderCutoff($size) + { + $this->expectCutoff = $size; + if ($size === false || !$this->body) { + $this->removeHeader('Expect'); + } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) { + $this->setHeader('Expect', '100-Continue'); + } + + return $this; + } + + public function configureRedirects($strict = false, $maxRedirects = 5) + { + $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict); + if ($maxRedirects == 0) { + $this->getParams()->set(RedirectPlugin::DISABLE, true); + } else { + $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects); + } + + return $this; + } + + public function getPostField($field) + { + return $this->postFields->get($field); + } + + public function getPostFields() + { + return $this->postFields; + } + + public function setPostField($key, $value) + { + $this->postFields->set($key, $value); + $this->processPostFields(); + + return $this; + } + + public function addPostFields($fields) + { + $this->postFields->merge($fields); + $this->processPostFields(); + + return $this; + } + + public function removePostField($field) + { + $this->postFields->remove($field); + $this->processPostFields(); + + return $this; + } + + public function getPostFiles() + { + return $this->postFiles; + } + + public function getPostFile($fieldName) + { + return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; + } + + public function removePostFile($fieldName) + { + unset($this->postFiles[$fieldName]); + $this->processPostFields(); + + return $this; + } + + public function addPostFile($field, $filename = null, $contentType = null, $postname = null) + { + $data = null; + + if ($field instanceof PostFileInterface) { + $data = $field; + } elseif (is_array($filename)) { + // Allow multiple values to be set in a single key + foreach ($filename as $file) { + $this->addPostFile($field, $file, $contentType); + } + return $this; + } elseif (!is_string($filename)) { + throw new RequestException('The path to a file must be a string'); + } elseif (!empty($filename)) { + // Adding an empty file will cause cURL to error out + $data = new PostFile($field, $filename, $contentType, $postname); + } + + if ($data) { + if (!isset($this->postFiles[$data->getFieldName()])) { + $this->postFiles[$data->getFieldName()] = array($data); + } else { + $this->postFiles[$data->getFieldName()][] = $data; + } + $this->processPostFields(); + } + + return $this; + } + + public function addPostFiles(array $files) + { + foreach ($files as $key => $file) { + if ($file instanceof PostFileInterface) { + $this->addPostFile($file, null, null, false); + } elseif (is_string($file)) { + // Convert non-associative array keys into 'file' + if (is_numeric($key)) { + $key = 'file'; + } + $this->addPostFile($key, $file, null, false); + } else { + throw new RequestException('File must be a string or instance of PostFileInterface'); + } + } + + return $this; + } + + /** + * Determine what type of request should be sent based on post fields + */ + protected function processPostFields() + { + if (!$this->postFiles) { + $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED); + } else { + $this->setHeader('Content-Type', self::MULTIPART); + if ($this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php new file mode 100644 index 000000000..49ad4595d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php @@ -0,0 +1,137 @@ + filenames where filename can be a string or PostFileInterface + * + * @return self + */ + public function addPostFiles(array $files); + + /** + * Configure how redirects are handled for the request + * + * @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most + * browsers with follow a 301-302 redirect for a POST request with a GET request. This is + * the default behavior of Guzzle. Enable strict redirects to redirect these responses + * with a POST rather than a GET request. + * @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects. + * + * @return self + */ + public function configureRedirects($strict = false, $maxRedirects = 5); +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php new file mode 100644 index 000000000..50597b2a6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php @@ -0,0 +1,182 @@ +header = trim($header); + $this->glue = $glue; + + foreach ((array) $values as $value) { + foreach ((array) $value as $v) { + $this->values[] = $v; + } + } + } + + public function __toString() + { + return implode($this->glue . ' ', $this->toArray()); + } + + public function add($value) + { + $this->values[] = $value; + + return $this; + } + + public function getName() + { + return $this->header; + } + + public function setName($name) + { + $this->header = $name; + + return $this; + } + + public function setGlue($glue) + { + $this->glue = $glue; + + return $this; + } + + public function getGlue() + { + return $this->glue; + } + + /** + * Normalize the header to be a single header with an array of values. + * + * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into + * multiple entries in the header. + * + * @return self + */ + public function normalize() + { + $values = $this->toArray(); + + for ($i = 0, $total = count($values); $i < $total; $i++) { + if (strpos($values[$i], $this->glue) !== false) { + // Explode on glue when the glue is not inside of a comma + foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) { + $values[] = trim($v); + } + unset($values[$i]); + } + } + + $this->values = array_values($values); + + return $this; + } + + public function hasValue($searchValue) + { + return in_array($searchValue, $this->toArray()); + } + + public function removeValue($searchValue) + { + $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) { + return $value != $searchValue; + })); + + return $this; + } + + public function toArray() + { + return $this->values; + } + + public function count() + { + return count($this->toArray()); + } + + public function getIterator() + { + return new \ArrayIterator($this->toArray()); + } + + public function parseParams() + { + $params = $matches = array(); + $callback = array($this, 'trimHeader'); + + // Normalize the header into a single array and iterate over all values + foreach ($this->normalize()->toArray() as $val) { + $part = array(); + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + continue; + } + $pieces = array_map($callback, $matches[0]); + $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : ''; + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasExactHeader($header) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this->header == $header; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function raw() + { + Version::warn(__METHOD__ . ' is deprecated. Use toArray()'); + return $this->toArray(); + } + + /** + * Trim a header by removing excess spaces and wrapping quotes + * + * @param $str + * + * @return string + */ + protected function trimHeader($str) + { + static $trimmed = "\"' \n\t"; + + return trim($str, $trimmed); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php new file mode 100644 index 000000000..77789e51f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php @@ -0,0 +1,121 @@ +directives = null; + } + + public function removeValue($searchValue) + { + parent::removeValue($searchValue); + $this->directives = null; + } + + /** + * Check if a specific cache control directive exists + * + * @param string $param Directive to retrieve + * + * @return bool + */ + public function hasDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]); + } + + /** + * Get a specific cache control directive + * + * @param string $param Directive to retrieve + * + * @return string|bool|null + */ + public function getDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]) ? $directives[$param] : null; + } + + /** + * Add a cache control directive + * + * @param string $param Directive to add + * @param string $value Value to set + * + * @return self + */ + public function addDirective($param, $value) + { + $directives = $this->getDirectives(); + $directives[$param] = $value; + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Remove a cache control directive by name + * + * @param string $param Directive to remove + * + * @return self + */ + public function removeDirective($param) + { + $directives = $this->getDirectives(); + unset($directives[$param]); + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Get an associative array of cache control directives + * + * @return array + */ + public function getDirectives() + { + if ($this->directives === null) { + $this->directives = array(); + foreach ($this->parseParams() as $collection) { + foreach ($collection as $key => $value) { + $this->directives[$key] = $value === '' ? true : $value; + } + } + } + + return $this->directives; + } + + /** + * Updates the header value based on the parsed directives + * + * @param array $directives Array of cache control directives + */ + protected function updateFromDirectives(array $directives) + { + $this->directives = $directives; + $this->values = array(); + + foreach ($directives as $key => $value) { + $this->values[] = $value === true ? $key : "{$key}={$value}"; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php new file mode 100644 index 000000000..8c7f6aefb --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php @@ -0,0 +1,108 @@ +headers = $headers; + } + + public function __clone() + { + foreach ($this->headers as &$header) { + $header = clone $header; + } + } + + /** + * Clears the header collection + */ + public function clear() + { + $this->headers = array(); + } + + /** + * Set a header on the collection + * + * @param HeaderInterface $header Header to add + * + * @return self + */ + public function add(HeaderInterface $header) + { + $this->headers[strtolower($header->getName())] = $header; + + return $this; + } + + /** + * Get an array of header objects + * + * @return array + */ + public function getAll() + { + return $this->headers; + } + + /** + * Alias of offsetGet + */ + public function get($key) + { + return $this->offsetGet($key); + } + + public function count() + { + return count($this->headers); + } + + public function offsetExists($offset) + { + return isset($this->headers[strtolower($offset)]); + } + + public function offsetGet($offset) + { + $l = strtolower($offset); + + return isset($this->headers[$l]) ? $this->headers[$l] : null; + } + + public function offsetSet($offset, $value) + { + $this->add($value); + } + + public function offsetUnset($offset) + { + unset($this->headers[strtolower($offset)]); + } + + public function getIterator() + { + return new \ArrayIterator($this->headers); + } + + public function toArray() + { + $result = array(); + foreach ($this->headers as $header) { + $result[$header->getName()] = $header->toArray(); + } + + return $result; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php new file mode 100644 index 000000000..0273be52f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php @@ -0,0 +1,26 @@ + 'Guzzle\Http\Message\Header\CacheControl', + 'link' => 'Guzzle\Http\Message\Header\Link', + ); + + public function createHeader($header, $value = null) + { + $lowercase = strtolower($header); + + return isset($this->mapping[$lowercase]) + ? new $this->mapping[$lowercase]($header, $value) + : new Header($header, $value); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php new file mode 100644 index 000000000..9457cf64a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php @@ -0,0 +1,19 @@ +", "rel=\"{$rel}\""); + + foreach ($params as $k => $v) { + $values[] = "{$k}=\"{$v}\""; + } + + return $this->add(implode('; ', $values)); + } + + /** + * Check if a specific link exists for a given rel attribute + * + * @param string $rel rel value + * + * @return bool + */ + public function hasLink($rel) + { + return $this->getLink($rel) !== null; + } + + /** + * Get a specific link for a given rel attribute + * + * @param string $rel Rel value + * + * @return array|null + */ + public function getLink($rel) + { + foreach ($this->getLinks() as $link) { + if (isset($link['rel']) && $link['rel'] == $rel) { + return $link; + } + } + + return null; + } + + /** + * Get an associative array of links + * + * For example: + * Link: ; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg" + * + * + * var_export($response->getLinks()); + * array( + * array( + * 'url' => 'http:/.../front.jpeg', + * 'rel' => 'back', + * 'type' => 'image/jpeg', + * ) + * ) + * + * + * @return array + */ + public function getLinks() + { + $links = $this->parseParams(); + + foreach ($links as &$link) { + $key = key($link); + unset($link[$key]); + $link['url'] = trim($key, '<> '); + } + + return $links; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php new file mode 100644 index 000000000..62bcd4391 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php @@ -0,0 +1,102 @@ +fieldName = $fieldName; + $this->setFilename($filename); + $this->postname = $postname ? $postname : basename($filename); + $this->contentType = $contentType ?: $this->guessContentType(); + } + + public function setFieldName($name) + { + $this->fieldName = $name; + + return $this; + } + + public function getFieldName() + { + return $this->fieldName; + } + + public function setFilename($filename) + { + // Remove leading @ symbol + if (strpos($filename, '@') === 0) { + $filename = substr($filename, 1); + } + + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + + $this->filename = $filename; + + return $this; + } + + public function setPostname($postname) + { + $this->postname = $postname; + + return $this; + } + + public function getFilename() + { + return $this->filename; + } + + public function getPostname() + { + return $this->postname; + } + + public function setContentType($type) + { + $this->contentType = $type; + + return $this; + } + + public function getContentType() + { + return $this->contentType; + } + + public function getCurlValue() + { + // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax + // See: https://wiki.php.net/rfc/curl-file-upload + if (function_exists('curl_file_create')) { + return curl_file_create($this->filename, $this->contentType, $this->postname); + } + + // Use the old style if using an older version of PHP + $value = "@{$this->filename};filename=" . $this->postname; + if ($this->contentType) { + $value .= ';type=' . $this->contentType; + } + + return $value; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCurlString() + { + Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()'); + return $this->getCurlValue(); + } + + /** + * Determine the Content-Type of the file + */ + protected function guessContentType() + { + return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream'; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php new file mode 100644 index 000000000..7f0779d1e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php @@ -0,0 +1,83 @@ +method = strtoupper($method); + $this->curlOptions = new Collection(); + $this->setUrl($url); + + if ($headers) { + // Special handling for multi-value headers + foreach ($headers as $key => $value) { + // Deal with collisions with Host and Authorization + if ($key == 'host' || $key == 'Host') { + $this->setHeader($key, $value); + } elseif ($value instanceof HeaderInterface) { + $this->addHeader($key, $value); + } else { + foreach ((array) $value as $v) { + $this->addHeader($key, $v); + } + } + } + } + + $this->setState(self::STATE_NEW); + } + + public function __clone() + { + if ($this->eventDispatcher) { + $this->eventDispatcher = clone $this->eventDispatcher; + } + $this->curlOptions = clone $this->curlOptions; + $this->params = clone $this->params; + $this->url = clone $this->url; + $this->response = $this->responseBody = null; + $this->headers = clone $this->headers; + + $this->setState(RequestInterface::STATE_NEW); + $this->dispatch('request.clone', array('request' => $this)); + } + + /** + * Get the HTTP request as a string + * + * @return string + */ + public function __toString() + { + return $this->getRawHeaders() . "\r\n\r\n"; + } + + /** + * Default method that will throw exceptions if an unsuccessful response is received. + * + * @param Event $event Received + * @throws BadResponseException if the response is not successful + */ + public static function onRequestError(Event $event) + { + $e = BadResponseException::factory($event['request'], $event['response']); + $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray()); + throw $e; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getClient() + { + return $this->client; + } + + public function getRawHeaders() + { + $protocolVersion = $this->protocolVersion ?: '1.1'; + + return trim($this->method . ' ' . $this->getResource()) . ' ' + . strtoupper(str_replace('https', 'http', $this->url->getScheme())) + . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines()); + } + + public function setUrl($url) + { + if ($url instanceof Url) { + $this->url = $url; + } else { + $this->url = Url::factory($url); + } + + // Update the port and host header + $this->setPort($this->url->getPort()); + + if ($this->url->getUsername() || $this->url->getPassword()) { + $this->setAuth($this->url->getUsername(), $this->url->getPassword()); + // Remove the auth info from the URL + $this->url->setUsername(null); + $this->url->setPassword(null); + } + + return $this; + } + + public function send() + { + if (!$this->client) { + throw new RuntimeException('A client must be set on the request'); + } + + return $this->client->send($this); + } + + public function getResponse() + { + return $this->response; + } + + public function getQuery($asString = false) + { + return $asString + ? (string) $this->url->getQuery() + : $this->url->getQuery(); + } + + public function getMethod() + { + return $this->method; + } + + public function getScheme() + { + return $this->url->getScheme(); + } + + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + + return $this; + } + + public function getHost() + { + return $this->url->getHost(); + } + + public function setHost($host) + { + $this->url->setHost($host); + $this->setPort($this->url->getPort()); + + return $this; + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function setProtocolVersion($protocol) + { + $this->protocolVersion = $protocol; + + return $this; + } + + public function getPath() + { + return '/' . ltrim($this->url->getPath(), '/'); + } + + public function setPath($path) + { + $this->url->setPath($path); + + return $this; + } + + public function getPort() + { + return $this->url->getPort(); + } + + public function setPort($port) + { + $this->url->setPort($port); + + // Include the port in the Host header if it is not the default port for the scheme of the URL + $scheme = $this->url->getScheme(); + if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port); + } else { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost()); + } + + return $this; + } + + public function getUsername() + { + return $this->username; + } + + public function getPassword() + { + return $this->password; + } + + public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) + { + static $authMap = array( + 'basic' => CURLAUTH_BASIC, + 'digest' => CURLAUTH_DIGEST, + 'ntlm' => CURLAUTH_NTLM, + 'any' => CURLAUTH_ANY + ); + + // If we got false or null, disable authentication + if (!$user) { + $this->password = $this->username = null; + $this->removeHeader('Authorization'); + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + return $this; + } + + if (!is_numeric($scheme)) { + $scheme = strtolower($scheme); + if (!isset($authMap[$scheme])) { + throw new InvalidArgumentException($scheme . ' is not a valid authentication type'); + } + $scheme = $authMap[$scheme]; + } + + $this->username = $user; + $this->password = $password; + + // Bypass CURL when using basic auth to promote connection reuse + if ($scheme == CURLAUTH_BASIC) { + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password)); + } else { + $this->getCurlOptions() + ->set(CURLOPT_HTTPAUTH, $scheme) + ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + + return $this; + } + + public function getResource() + { + $resource = $this->getPath(); + if ($query = (string) $this->url->getQuery()) { + $resource .= '?' . $query; + } + + return $resource; + } + + public function getUrl($asObject = false) + { + return $asObject ? clone $this->url : (string) $this->url; + } + + public function getState() + { + return $this->state; + } + + public function setState($state, array $context = array()) + { + $oldState = $this->state; + $this->state = $state; + + switch ($state) { + case self::STATE_NEW: + $this->response = null; + break; + case self::STATE_TRANSFER: + if ($oldState !== $state) { + // Fix Content-Length and Transfer-Encoding collisions + if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) { + $this->removeHeader('Transfer-Encoding'); + } + $this->dispatch('request.before_send', array('request' => $this)); + } + break; + case self::STATE_COMPLETE: + if ($oldState !== $state) { + $this->processResponse($context); + $this->responseBody = null; + } + break; + case self::STATE_ERROR: + if (isset($context['exception'])) { + $this->dispatch('request.exception', array( + 'request' => $this, + 'response' => isset($context['response']) ? $context['response'] : $this->response, + 'exception' => isset($context['exception']) ? $context['exception'] : null + )); + } + } + + return $this->state; + } + + public function getCurlOptions() + { + return $this->curlOptions; + } + + public function startResponse(Response $response) + { + $this->state = self::STATE_TRANSFER; + $response->setEffectiveUrl((string) $this->getUrl()); + $this->response = $response; + + return $this; + } + + public function setResponse(Response $response, $queued = false) + { + $response->setEffectiveUrl((string) $this->url); + + if ($queued) { + $ed = $this->getEventDispatcher(); + $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) { + $e['request']->setResponse($response); + $ed->removeListener('request.before_send', $f); + }, -9999); + } else { + $this->response = $response; + // If a specific response body is specified, then use it instead of the response's body + if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) { + $this->getResponseBody()->write((string) $this->response->getBody()); + } else { + $this->responseBody = $this->response->getBody(); + } + $this->setState(self::STATE_COMPLETE); + } + + return $this; + } + + public function setResponseBody($body) + { + // Attempt to open a file for writing if a string was passed + if (is_string($body)) { + // @codeCoverageIgnoreStart + if (!($body = fopen($body, 'w+'))) { + throw new InvalidArgumentException('Could not open ' . $body . ' for writing'); + } + // @codeCoverageIgnoreEnd + } + + $this->responseBody = EntityBody::factory($body); + + return $this; + } + + public function getResponseBody() + { + if ($this->responseBody === null) { + $this->responseBody = EntityBody::factory()->setCustomData('default', true); + } + + return $this->responseBody; + } + + /** + * Determine if the response body is repeatable (readable + seekable) + * + * @return bool + * @deprecated Use getResponseBody()->isSeekable() + * @codeCoverageIgnore + */ + public function isResponseBodyRepeatable() + { + Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()'); + return !$this->responseBody ? true : $this->responseBody->isRepeatable(); + } + + public function getCookies() + { + if ($cookie = $this->getHeader('Cookie')) { + $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie); + return $data['cookies']; + } + + return array(); + } + + public function getCookie($name) + { + $cookies = $this->getCookies(); + + return isset($cookies[$name]) ? $cookies[$name] : null; + } + + public function addCookie($name, $value) + { + if (!$this->hasHeader('Cookie')) { + $this->setHeader('Cookie', "{$name}={$value}"); + } else { + $this->getHeader('Cookie')->add("{$name}={$value}"); + } + + // Always use semicolons to separate multiple cookie headers + $this->getHeader('Cookie')->setGlue(';'); + + return $this; + } + + public function removeCookie($name) + { + if ($cookie = $this->getHeader('Cookie')) { + foreach ($cookie as $cookieValue) { + if (strpos($cookieValue, $name . '=') === 0) { + $cookie->removeValue($cookieValue); + } + } + } + + return $this; + } + + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) + { + $this->eventDispatcher = $eventDispatcher; + $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255); + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->setEventDispatcher(new EventDispatcher()); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + $context['request'] = $this; + + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + /** + * Get an array containing the request and response for event notifications + * + * @return array + */ + protected function getEventArray() + { + return array( + 'request' => $this, + 'response' => $this->response + ); + } + + /** + * Process a received response + * + * @param array $context Contextual information + * @throws RequestException|BadResponseException on unsuccessful responses + */ + protected function processResponse(array $context = array()) + { + if (!$this->response) { + // If no response, then processResponse shouldn't have been called + $e = new RequestException('Error completing request'); + $e->setRequest($this); + throw $e; + } + + $this->state = self::STATE_COMPLETE; + + // A request was sent, but we don't know if we'll send more or if the final response will be successful + $this->dispatch('request.sent', $this->getEventArray() + $context); + + // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin) + if ($this->state == RequestInterface::STATE_COMPLETE) { + + // The request completed, so the HTTP transaction is complete + $this->dispatch('request.complete', $this->getEventArray()); + + // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by + // modifying the Event object in your listeners or calling setResponse() on the request + if ($this->response->isError()) { + $event = new Event($this->getEventArray()); + $this->getEventDispatcher()->dispatch('request.error', $event); + // Allow events of request.error to quietly change the response + if ($event['response'] !== $this->response) { + $this->response = $event['response']; + } + } + + // If a successful response was received, dispatch an event + if ($this->response->isSuccessful()) { + $this->dispatch('request.success', $this->getEventArray()); + } + } + } + + /** + * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy + * @codeCoverageIgnore + */ + public function canCache() + { + Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.'); + if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) { + $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy(); + return $canCache->canCacheRequest($this); + } else { + return false; + } + } + + /** + * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now) + * @codeCoverageIgnore + */ + public function setIsRedirect($isRedirect) + { + $this->isRedirect = $isRedirect; + + return $this; + } + + /** + * @deprecated Use the history plugin + * @codeCoverageIgnore + */ + public function isRedirect() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.'); + return $this->isRedirect; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php new file mode 100644 index 000000000..ba00a7676 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php @@ -0,0 +1,359 @@ +methods = array_flip(get_class_methods(__CLASS__)); + } + + public function fromMessage($message) + { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message); + + if (!$parsed) { + return false; + } + + $request = $this->fromParts($parsed['method'], $parsed['request_url'], + $parsed['headers'], $parsed['body'], $parsed['protocol'], + $parsed['version']); + + // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST + // requests. This factory method should accurately reflect the message, so here we are removing the Expect + // header if one was not supplied in the message. + if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) { + $request->removeHeader('Expect'); + } + + return $request; + } + + public function fromParts( + $method, + array $urlParts, + $headers = null, + $body = null, + $protocol = 'HTTP', + $protocolVersion = '1.1' + ) { + return $this->create($method, Url::buildUrl($urlParts), $headers, $body) + ->setProtocolVersion($protocolVersion); + } + + public function create($method, $url, $headers = null, $body = null, array $options = array()) + { + $method = strtoupper($method); + + if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE') { + // Handle non-entity-enclosing request methods + $request = new $this->requestClass($method, $url, $headers); + if ($body) { + // The body is where the response body will be stored + $type = gettype($body); + if ($type == 'string' || $type == 'resource' || $type == 'object') { + $request->setResponseBody($body); + } + } + } else { + // Create an entity enclosing request by default + $request = new $this->entityEnclosingRequestClass($method, $url, $headers); + if ($body || $body === '0') { + // Add POST fields and files to an entity enclosing request if an array is used + if (is_array($body) || $body instanceof Collection) { + // Normalize PHP style cURL uploads with a leading '@' symbol + foreach ($body as $key => $value) { + if (is_string($value) && substr($value, 0, 1) == '@') { + $request->addPostFile($key, $value); + unset($body[$key]); + } + } + // Add the fields if they are still present and not all files + $request->addPostFields($body); + } else { + // Add a raw entity body body to the request + $request->setBody($body, (string) $request->getHeader('Content-Type')); + if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') { + $request->removeHeader('Content-Length'); + } + } + } + } + + if ($options) { + $this->applyOptions($request, $options); + } + + return $request; + } + + /** + * Clone a request while changing the method. Emulates the behavior of + * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method. + * + * @param RequestInterface $request Request to clone + * @param string $method Method to set + * + * @return RequestInterface + */ + public function cloneRequestWithMethod(RequestInterface $request, $method) + { + // Create the request with the same client if possible + if ($request->getClient()) { + $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders()); + } else { + $cloned = $this->create($method, $request->getUrl(), $request->getHeaders()); + } + + $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray()); + $cloned->setEventDispatcher(clone $request->getEventDispatcher()); + // Ensure that that the Content-Length header is not copied if changing to GET or HEAD + if (!($cloned instanceof EntityEnclosingRequestInterface)) { + $cloned->removeHeader('Content-Length'); + } elseif ($request instanceof EntityEnclosingRequestInterface) { + $cloned->setBody($request->getBody()); + } + $cloned->getParams()->replace($request->getParams()->toArray()); + $cloned->dispatch('request.clone', array('request' => $cloned)); + + return $cloned; + } + + public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE) + { + // Iterate over each key value pair and attempt to apply a config using function visitors + foreach ($options as $key => $value) { + $method = "visit_{$key}"; + if (isset($this->methods[$method])) { + $this->{$method}($request, $value, $flags); + } + } + } + + protected function visit_headers(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('headers value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge headers in but do not overwrite existing values + foreach ($value as $key => $header) { + if (!$request->hasHeader($key)) { + $request->setHeader($key, $header); + } + } + } else { + $request->addHeaders($value); + } + } + + protected function visit_body(RequestInterface $request, $value, $flags) + { + if ($request instanceof EntityEnclosingRequestInterface) { + $request->setBody($value); + } else { + throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request'); + } + } + + protected function visit_allow_redirects(RequestInterface $request, $value, $flags) + { + if ($value === false) { + $request->getParams()->set(RedirectPlugin::DISABLE, true); + } + } + + protected function visit_auth(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('auth value must be an array'); + } + + $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic'); + } + + protected function visit_query(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('query value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge query string values in but do not overwrite existing values + $query = $request->getQuery(); + $query->overwriteWith(array_diff_key($value, $query->toArray())); + } else { + $request->getQuery()->overwriteWith($value); + } + } + + protected function visit_cookies(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('cookies value must be an array'); + } + + foreach ($value as $name => $v) { + $request->addCookie($name, $v); + } + } + + protected function visit_events(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('events value must be an array'); + } + + foreach ($value as $name => $method) { + if (is_array($method)) { + $request->getEventDispatcher()->addListener($name, $method[0], $method[1]); + } else { + $request->getEventDispatcher()->addListener($name, $method); + } + } + } + + protected function visit_plugins(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('plugins value must be an array'); + } + + foreach ($value as $plugin) { + $request->addSubscriber($plugin); + } + } + + protected function visit_exceptions(RequestInterface $request, $value, $flags) + { + if ($value === false || $value === 0) { + $dispatcher = $request->getEventDispatcher(); + foreach ($dispatcher->getListeners('request.error') as $listener) { + if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') { + $dispatcher->removeListener('request.error', $listener); + break; + } + } + } + } + + protected function visit_save_to(RequestInterface $request, $value, $flags) + { + $request->setResponseBody($value); + } + + protected function visit_params(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('params value must be an array'); + } + + $request->getParams()->overwriteWith($value); + } + + protected function visit_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_TIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value); + } + } + + protected function visit_connect_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value); + } + } + + protected function visit_debug(RequestInterface $request, $value, $flags) + { + if ($value) { + $request->getCurlOptions()->set(CURLOPT_VERBOSE, true); + } + } + + protected function visit_verify(RequestInterface $request, $value, $flags) + { + $curl = $request->getCurlOptions(); + if ($value === true || is_string($value)) { + $curl[CURLOPT_SSL_VERIFYHOST] = 2; + $curl[CURLOPT_SSL_VERIFYPEER] = true; + if ($value !== true) { + $curl[CURLOPT_CAINFO] = $value; + } + } elseif ($value === false) { + unset($curl[CURLOPT_CAINFO]); + $curl[CURLOPT_SSL_VERIFYHOST] = 0; + $curl[CURLOPT_SSL_VERIFYPEER] = false; + } + } + + protected function visit_proxy(RequestInterface $request, $value, $flags) + { + $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags); + } + + protected function visit_cert(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value); + } + } + + protected function visit_ssl_key(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php new file mode 100644 index 000000000..6088f10e9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php @@ -0,0 +1,105 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Reserved for WebDAV advanced collections expired proposal', + 426 => 'Upgrade required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ); + + /** @var EntityBodyInterface The response body */ + protected $body; + + /** @var string The reason phrase of the response (human readable code) */ + protected $reasonPhrase; + + /** @var string The status code of the response */ + protected $statusCode; + + /** @var array Information about the request */ + protected $info = array(); + + /** @var string The effective URL that returned this response */ + protected $effectiveUrl; + + /** @var array Cacheable response codes (see RFC 2616:13.4) */ + protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); + + /** + * Create a new Response based on a raw response message + * + * @param string $message Response message + * + * @return self|bool Returns false on error + */ + public static function fromMessage($message) + { + $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message); + if (!$data) { + return false; + } + + $response = new static($data['code'], $data['headers'], $data['body']); + $response->setProtocol($data['protocol'], $data['version']) + ->setStatus($data['code'], $data['reason_phrase']); + + // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X) + $contentLength = (string) $response->getHeader('Content-Length'); + $actualLength = strlen($data['body']); + if (strlen($data['body']) > 0 && $contentLength != $actualLength) { + $response->setHeader('Content-Length', $actualLength); + } + + return $response; + } + + /** + * Construct the response + * + * @param string $statusCode The response status code (e.g. 200, 404, etc) + * @param ToArrayInterface|array $headers The response headers + * @param string|resource|EntityBodyInterface $body The body of the response + * + * @throws BadResponseException if an invalid response code is given + */ + public function __construct($statusCode, $headers = null, $body = null) + { + parent::__construct(); + $this->setStatus($statusCode); + $this->body = EntityBody::factory($body !== null ? $body : ''); + + if ($headers) { + if (is_array($headers)) { + $this->setHeaders($headers); + } elseif ($headers instanceof ToArrayInterface) { + $this->setHeaders($headers->toArray()); + } else { + throw new BadResponseException('Invalid headers argument received'); + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } + + public function serialize() + { + return json_encode(array( + 'status' => $this->statusCode, + 'body' => (string) $this->body, + 'headers' => $this->headers->toArray() + )); + } + + public function unserialize($serialize) + { + $data = json_decode($serialize, true); + $this->__construct($data['status'], $data['headers'], $data['body']); + } + + /** + * Get the response entity body + * + * @param bool $asString Set to TRUE to return a string of the body rather than a full body object + * + * @return EntityBodyInterface|string + */ + public function getBody($asString = false) + { + return $asString ? (string) $this->body : $this->body; + } + + /** + * Set the response entity body + * + * @param EntityBodyInterface|string $body Body to set + * + * @return self + */ + public function setBody($body) + { + $this->body = EntityBody::factory($body); + + return $this; + } + + /** + * Set the protocol and protocol version of the response + * + * @param string $protocol Response protocol + * @param string $version Protocol version + * + * @return self + */ + public function setProtocol($protocol, $version) + { + $this->protocol = $protocol; + $this->protocolVersion = $version; + + return $this; + } + + /** + * Get the protocol used for the response (e.g. HTTP) + * + * @return string + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * Get the HTTP protocol version + * + * @return string + */ + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + /** + * Get a cURL transfer information + * + * @param string $key A single statistic to check + * + * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key + * is set and not found + * @link http://www.php.net/manual/en/function.curl-getinfo.php + */ + public function getInfo($key = null) + { + if ($key === null) { + return $this->info; + } elseif (array_key_exists($key, $this->info)) { + return $this->info[$key]; + } else { + return null; + } + } + + /** + * Set the transfer information + * + * @param array $info Array of cURL transfer stats + * + * @return self + */ + public function setInfo(array $info) + { + $this->info = $info; + + return $this; + } + + /** + * Set the response status + * + * @param int $statusCode Response status code to set + * @param string $reasonPhrase Response reason phrase + * + * @return self + * @throws BadResponseException when an invalid response code is received + */ + public function setStatus($statusCode, $reasonPhrase = '') + { + $this->statusCode = (int) $statusCode; + + if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } else { + $this->reasonPhrase = $reasonPhrase; + } + + return $this; + } + + /** + * Get the response status code + * + * @return integer + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Get the entire response as a string + * + * @return string + */ + public function getMessage() + { + $message = $this->getRawHeaders(); + + // Only include the body in the message if the size is < 2MB + $size = $this->body->getSize(); + if ($size < 2097152) { + $message .= (string) $this->body; + } + + return $message; + } + + /** + * Get the the raw message headers as a string + * + * @return string + */ + public function getRawHeaders() + { + $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n"; + $lines = $this->getHeaderLines(); + if (!empty($lines)) { + $headers .= implode("\r\n", $lines) . "\r\n"; + } + + return $headers . "\r\n"; + } + + /** + * Get the response reason phrase- a human readable version of the numeric + * status code + * + * @return string + */ + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + /** + * Get the Accept-Ranges HTTP header + * + * @return string Returns what partial content range types this server supports. + */ + public function getAcceptRanges() + { + return (string) $this->getHeader('Accept-Ranges'); + } + + /** + * Calculate the age of the response + * + * @return integer + */ + public function calculateAge() + { + $age = $this->getHeader('Age'); + + if ($age === null && $this->getDate()) { + $age = time() - strtotime($this->getDate()); + } + + return $age === null ? null : (int) (string) $age; + } + + /** + * Get the Age HTTP header + * + * @return integer|null Returns the age the object has been in a proxy cache in seconds. + */ + public function getAge() + { + return (string) $this->getHeader('Age'); + } + + /** + * Get the Allow HTTP header + * + * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed. + */ + public function getAllow() + { + return (string) $this->getHeader('Allow'); + } + + /** + * Check if an HTTP method is allowed by checking the Allow response header + * + * @param string $method Method to check + * + * @return bool + */ + public function isMethodAllowed($method) + { + $allow = $this->getHeader('Allow'); + if ($allow) { + foreach (explode(',', $allow) as $allowable) { + if (!strcasecmp(trim($allowable), $method)) { + return true; + } + } + } + + return false; + } + + /** + * Get the Cache-Control HTTP header + * + * @return string + */ + public function getCacheControl() + { + return (string) $this->getHeader('Cache-Control'); + } + + /** + * Get the Connection HTTP header + * + * @return string + */ + public function getConnection() + { + return (string) $this->getHeader('Connection'); + } + + /** + * Get the Content-Encoding HTTP header + * + * @return string|null + */ + public function getContentEncoding() + { + return (string) $this->getHeader('Content-Encoding'); + } + + /** + * Get the Content-Language HTTP header + * + * @return string|null Returns the language the content is in. + */ + public function getContentLanguage() + { + return (string) $this->getHeader('Content-Language'); + } + + /** + * Get the Content-Length HTTP header + * + * @return integer Returns the length of the response body in bytes + */ + public function getContentLength() + { + return (int) (string) $this->getHeader('Content-Length'); + } + + /** + * Get the Content-Location HTTP header + * + * @return string|null Returns an alternate location for the returned data (e.g /index.htm) + */ + public function getContentLocation() + { + return (string) $this->getHeader('Content-Location'); + } + + /** + * Get the Content-Disposition HTTP header + * + * @return string|null Returns the Content-Disposition header + */ + public function getContentDisposition() + { + return (string) $this->getHeader('Content-Disposition'); + } + + /** + * Get the Content-MD5 HTTP header + * + * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response. + */ + public function getContentMd5() + { + return (string) $this->getHeader('Content-MD5'); + } + + /** + * Get the Content-Range HTTP header + * + * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022). + */ + public function getContentRange() + { + return (string) $this->getHeader('Content-Range'); + } + + /** + * Get the Content-Type HTTP header + * + * @return string Returns the mime type of this content. + */ + public function getContentType() + { + return (string) $this->getHeader('Content-Type'); + } + + /** + * Checks if the Content-Type is of a certain type. This is useful if the + * Content-Type header contains charset information and you need to know if + * the Content-Type matches a particular type. + * + * @param string $type Content type to check against + * + * @return bool + */ + public function isContentType($type) + { + return stripos($this->getHeader('Content-Type'), $type) !== false; + } + + /** + * Get the Date HTTP header + * + * @return string|null Returns the date and time that the message was sent. + */ + public function getDate() + { + return (string) $this->getHeader('Date'); + } + + /** + * Get the ETag HTTP header + * + * @return string|null Returns an identifier for a specific version of a resource, often a Message digest. + */ + public function getEtag() + { + return (string) $this->getHeader('ETag'); + } + + /** + * Get the Expires HTTP header + * + * @return string|null Returns the date/time after which the response is considered stale. + */ + public function getExpires() + { + return (string) $this->getHeader('Expires'); + } + + /** + * Get the Last-Modified HTTP header + * + * @return string|null Returns the last modified date for the requested object, in RFC 2822 format + * (e.g. Tue, 15 Nov 1994 12:45:26 GMT) + */ + public function getLastModified() + { + return (string) $this->getHeader('Last-Modified'); + } + + /** + * Get the Location HTTP header + * + * @return string|null Used in redirection, or when a new resource has been created. + */ + public function getLocation() + { + return (string) $this->getHeader('Location'); + } + + /** + * Get the Pragma HTTP header + * + * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along + * the request-response chain. + */ + public function getPragma() + { + return (string) $this->getHeader('Pragma'); + } + + /** + * Get the Proxy-Authenticate HTTP header + * + * @return string|null Authentication to access the proxy (e.g. Basic) + */ + public function getProxyAuthenticate() + { + return (string) $this->getHeader('Proxy-Authenticate'); + } + + /** + * Get the Retry-After HTTP header + * + * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a + * specified period of time. + */ + public function getRetryAfter() + { + return (string) $this->getHeader('Retry-After'); + } + + /** + * Get the Server HTTP header + * + * @return string|null A name for the server + */ + public function getServer() + { + return (string) $this->getHeader('Server'); + } + + /** + * Get the Set-Cookie HTTP header + * + * @return string|null An HTTP cookie. + */ + public function getSetCookie() + { + return (string) $this->getHeader('Set-Cookie'); + } + + /** + * Get the Trailer HTTP header + * + * @return string|null The Trailer general field value indicates that the given set of header fields is present in + * the trailer of a message encoded with chunked transfer-coding. + */ + public function getTrailer() + { + return (string) $this->getHeader('Trailer'); + } + + /** + * Get the Transfer-Encoding HTTP header + * + * @return string|null The form of encoding used to safely transfer the entity to the user + */ + public function getTransferEncoding() + { + return (string) $this->getHeader('Transfer-Encoding'); + } + + /** + * Get the Vary HTTP header + * + * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached + * response can be used rather than requesting a fresh one from the origin server. + */ + public function getVary() + { + return (string) $this->getHeader('Vary'); + } + + /** + * Get the Via HTTP header + * + * @return string|null Informs the client of proxies through which the response was sent. + */ + public function getVia() + { + return (string) $this->getHeader('Via'); + } + + /** + * Get the Warning HTTP header + * + * @return string|null A general warning about possible problems with the entity body + */ + public function getWarning() + { + return (string) $this->getHeader('Warning'); + } + + /** + * Get the WWW-Authenticate HTTP header + * + * @return string|null Indicates the authentication scheme that should be used to access the requested entity + */ + public function getWwwAuthenticate() + { + return (string) $this->getHeader('WWW-Authenticate'); + } + + /** + * Checks if HTTP Status code is a Client Error (4xx) + * + * @return bool + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) + * + * @return boolean + */ + public function isError() + { + return $this->isClientError() || $this->isServerError(); + } + + /** + * Checks if HTTP Status code is Information (1xx) + * + * @return bool + */ + public function isInformational() + { + return $this->statusCode < 200; + } + + /** + * Checks if HTTP Status code is a Redirect (3xx) + * + * @return bool + */ + public function isRedirect() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Checks if HTTP Status code is Server Error (5xx) + * + * @return bool + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Checks if HTTP Status code is Successful (2xx | 304) + * + * @return bool + */ + public function isSuccessful() + { + return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304; + } + + /** + * Check if the response can be cached based on the response headers + * + * @return bool Returns TRUE if the response can be cached or false if not + */ + public function canCache() + { + // Check if the response is cacheable based on the code + if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) { + return false; + } + + // Make sure a valid body was returned and can be cached + if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable()) + && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) { + return false; + } + + // Never cache no-store resources (this is a private cache, so private + // can be cached) + if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return $this->isFresh() || $this->getFreshness() === null || $this->canValidate(); + } + + /** + * Gets the number of seconds from the current time in which this response is still considered fresh + * + * @return int|null Returns the number of seconds + */ + public function getMaxAge() + { + if ($header = $this->getHeader('Cache-Control')) { + // s-max-age, then max-age, then Expires + if ($age = $header->getDirective('s-maxage')) { + return $age; + } + if ($age = $header->getDirective('max-age')) { + return $age; + } + } + + if ($this->getHeader('Expires')) { + return strtotime($this->getExpires()) - time(); + } + + return null; + } + + /** + * Check if the response is considered fresh. + * + * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the + * response. + * + * @return bool|null + */ + public function isFresh() + { + $fresh = $this->getFreshness(); + + return $fresh === null ? null : $fresh >= 0; + } + + /** + * Check if the response can be validated against the origin server using a conditional GET request. + * + * @return bool + */ + public function canValidate() + { + return $this->getEtag() || $this->getLastModified(); + } + + /** + * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the + * age of the response (max-age - age). + * + * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired. + * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL + * result means that no freshness information is available. + * + * @return int + */ + public function getFreshness() + { + $maxAge = $this->getMaxAge(); + $age = $this->calculateAge(); + + return $maxAge && $age ? ($maxAge - $age) : null; + } + + /** + * Parse the JSON response body and return an array + * + * @return array|string|int|bool|float + * @throws RuntimeException if the response body is not in JSON format + */ + public function json() + { + $data = json_decode((string) $this->body, true); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); + } + + return $data === null ? array() : $data; + } + + /** + * Parse the XML response body and return a \SimpleXMLElement. + * + * In order to prevent XXE attacks, this method disables loading external + * entities. If you rely on external entities, then you must parse the + * XML response manually by accessing the response body directly. + * + * @return \SimpleXMLElement + * @throws RuntimeException if the response body is not in XML format + * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + */ + public function xml() + { + $errorMessage = null; + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + try { + $xml = new \SimpleXMLElement((string) $this->body ?: '', LIBXML_NONET); + if ($error = libxml_get_last_error()) { + $errorMessage = $error->message; + } + } catch (\Exception $e) { + $errorMessage = $e->getMessage(); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + if ($errorMessage) { + throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage); + } + + return $xml; + } + + /** + * Get the redirect count of this response + * + * @return int + */ + public function getRedirectCount() + { + return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT); + } + + /** + * Set the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @param string $url The effective URL + * + * @return self + */ + public function setEffectiveUrl($url) + { + $this->effectiveUrl = $url; + + return $this; + } + + /** + * Get the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @return string + */ + public function getEffectiveUrl() + { + return $this->effectiveUrl; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getPreviousResponse() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.'); + return null; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setRequest($request) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getRequest() + { + Version::warn(__METHOD__ . ' is deprecated'); + return null; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php new file mode 100644 index 000000000..d71586a05 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php @@ -0,0 +1,962 @@ + 'text/vnd.in3d.3dml', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'application' => 'application/x-ms-application', + 'apr' => 'application/vnd.lotus-approach', + 'asa' => 'text/plain', + 'asax' => 'application/octet-stream', + 'asc' => 'application/pgp-signature', + 'ascx' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'ashx' => 'text/plain', + 'asm' => 'text/x-asm', + 'asmx' => 'text/plain', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asp' => 'text/plain', + 'aspx' => 'text/plain', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'axd' => 'text/plain', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfc' => 'application/x-coldfusion', + 'cfm' => 'application/x-coldfusion', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'cryptonote' => 'application/vnd.rig.cryptonote', + 'cs' => 'text/plain', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxt' => 'application/vnd.geonext', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hta' => 'application/octet-stream', + 'htc' => 'text/html', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/octet-stream', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/octet-stream', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/octet-stream', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/mp4', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsf' => 'application/vnd.lotus-notes', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'text/x-php', + 'phps' => 'application/x-httpd-phps', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rb' => 'text/plain', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'resx' => 'text/xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sig' => 'application/pgp-signature', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'src' => 'application/x-wais-source', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'image/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvx' => 'application/vnd.dece.unspecified', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-ms-wmz', + 'woff' => 'application/x-font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'yaml' => 'text/yaml', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'yml' => 'text/yaml', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml' + ); + + /** + * Get a singleton instance of the class + * + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get a mimetype value from a file extension + * + * @param string $extension File extension + * + * @return string|null + * + */ + public function fromExtension($extension) + { + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null; + } + + /** + * Get a mimetype from a filename + * + * @param string $filename Filename to generate a mimetype from + * + * @return string|null + */ + public function fromFilename($filename) + { + return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php new file mode 100644 index 000000000..4b4e49d05 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php @@ -0,0 +1,20 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value))); + } else { + return array($key => implode(',', $value)); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php new file mode 100644 index 000000000..1bf1730e4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php @@ -0,0 +1,22 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value)); + } else { + return array($key => $value); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php new file mode 100644 index 000000000..133ea2bd9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php @@ -0,0 +1,27 @@ + $v) { + $k = "{$key}[{$k}]"; + if (is_array($v)) { + $ret = array_merge($ret, self::aggregate($k, $v, $query)); + } else { + $ret[$query->encodeValue($k)] = $query->encodeValue($v); + } + } + + return $ret; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php new file mode 100644 index 000000000..72bee620c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php @@ -0,0 +1,22 @@ +add($key, $value); + $foundDuplicates = true; + } elseif ($paramIsPhpStyleArray) { + $q[$key] = array($value); + } else { + $q[$key] = $value; + } + } else { + // Uses false by default to represent keys with no trailing "=" sign. + $q->add($key, false); + } + } + + // Use the duplicate aggregator if duplicates were found and not using PHP style arrays + if ($foundDuplicates && !$foundPhpStyle) { + $q->setAggregator(new DuplicateAggregator()); + } + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + * @throws RuntimeException + */ + public function __toString() + { + if (!$this->data) { + return ''; + } + + $queryList = array(); + foreach ($this->prepareData($this->data) as $name => $value) { + $queryList[] = $this->convertKvp($name, $value); + } + + return implode($this->fieldSeparator, $queryList); + } + + /** + * Get the query string field separator + * + * @return string + */ + public function getFieldSeparator() + { + return $this->fieldSeparator; + } + + /** + * Get the query string value separator + * + * @return string + */ + public function getValueSeparator() + { + return $this->valueSeparator; + } + + /** + * Returns the type of URL encoding used by the query string + * + * One of: false, "RFC 3986", or "application/x-www-form-urlencoded" + * + * @return bool|string + */ + public function getUrlEncoding() + { + return $this->urlEncode; + } + + /** + * Returns true or false if using URL encoding + * + * @return bool + */ + public function isUrlEncoding() + { + return $this->urlEncode !== false; + } + + /** + * Provide a function for combining multi-valued query string parameters into a single or multiple fields + * + * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting + * deeply nested query string variables into a flattened array. + * Pass null to use the default PHP style aggregator. For legacy + * reasons, this function accepts a callable that must accepts a + * $key, $value, and query object. + * @return self + * @see \Guzzle\Http\QueryString::aggregateUsingComma() + */ + public function setAggregator(QueryAggregatorInterface $aggregator = null) + { + // Use the default aggregator if none was set + if (!$aggregator) { + if (!self::$defaultAggregator) { + self::$defaultAggregator = new PhpAggregator(); + } + $aggregator = self::$defaultAggregator; + } + + $this->aggregator = $aggregator; + + return $this; + } + + /** + * Set whether or not field names and values should be rawurlencoded + * + * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or + * form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode) + * @return self + */ + public function useUrlEncoding($encode) + { + $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode; + + return $this; + } + + /** + * Set the query string separator + * + * @param string $separator The query string separator that will separate fields + * + * @return self + */ + public function setFieldSeparator($separator) + { + $this->fieldSeparator = $separator; + + return $this; + } + + /** + * Set the query string value separator + * + * @param string $separator The query string separator that will separate values from fields + * + * @return self + */ + public function setValueSeparator($separator) + { + $this->valueSeparator = $separator; + + return $this; + } + + /** + * Returns an array of url encoded field names and values + * + * @return array + */ + public function urlEncode() + { + return $this->prepareData($this->data); + } + + /** + * URL encodes a value based on the url encoding type of the query string object + * + * @param string $value Value to encode + * + * @return string + */ + public function encodeValue($value) + { + if ($this->urlEncode == self::RFC_3986) { + return rawurlencode($value); + } elseif ($this->urlEncode == self::FORM_URLENCODED) { + return urlencode($value); + } else { + return (string) $value; + } + } + + /** + * Url encode parameter data and convert nested query strings into a flattened hash. + * + * @param array $data The data to encode + * + * @return array Returns an array of encoded values and keys + */ + protected function prepareData(array $data) + { + // If no aggregator is present then set the default + if (!$this->aggregator) { + $this->setAggregator(null); + } + + $temp = array(); + foreach ($data as $key => $value) { + if ($value === false || $value === null) { + // False and null will not include the "=". Use an empty string to include the "=". + $temp[$this->encodeValue($key)] = $value; + } elseif (is_array($value)) { + $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this)); + } else { + $temp[$this->encodeValue($key)] = $this->encodeValue($value); + } + } + + return $temp; + } + + /** + * Converts a key value pair that can contain strings, nulls, false, or arrays + * into a single string. + * + * @param string $name Name of the field + * @param mixed $value Value of the field + * @return string + */ + private function convertKvp($name, $value) + { + if ($value === self::BLANK || $value === null || $value === false) { + return $name; + } elseif (!is_array($value)) { + return $name . $this->valueSeparator . $value; + } + + $result = ''; + foreach ($value as $v) { + $result .= $this->convertKvp($name, $v) . $this->fieldSeparator; + } + + return rtrim($result, $this->fieldSeparator); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php new file mode 100644 index 000000000..ef282733b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php @@ -0,0 +1,122 @@ +setLimit($limit)->setOffset($offset); + } + + /** + * Returns only a subset of the decorated entity body when cast as a string + * {@inheritdoc} + */ + public function __toString() + { + if (!$this->body->isReadable() || + (!$this->body->isSeekable() && $this->body->isConsumed()) + ) { + return ''; + } + + $originalPos = $this->body->ftell(); + $this->body->seek($this->offset); + $data = ''; + while (!$this->feof()) { + $data .= $this->read(1048576); + } + $this->body->seek($originalPos); + + return (string) $data ?: ''; + } + + public function isConsumed() + { + return $this->body->isConsumed() || + ($this->body->ftell() >= $this->offset + $this->limit); + } + + /** + * Returns the Content-Length of the limited subset of data + * {@inheritdoc} + */ + public function getContentLength() + { + $length = $this->body->getContentLength(); + + return $length === false + ? $this->limit + : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset); + } + + /** + * Allow for a bounded seek on the read limited entity body + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + return $whence === SEEK_SET + ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset))) + : false; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @return self + */ + public function setOffset($offset) + { + $this->body->seek($offset); + $this->offset = $offset; + + return $this; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the stream + * + * @param int $limit Total number of bytes to allow to be read from the stream + * + * @return self + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + public function read($length) + { + // Check if the current position is less than the total allowed bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->body->ftell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte limit is not exceeded + return $this->body->read(min($remaining, $length)); + } else { + return false; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php new file mode 100644 index 000000000..1a824b8b7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php @@ -0,0 +1,250 @@ + array('onRequestSent', 100), + 'request.clone' => 'cleanupRequest', + 'request.before_send' => 'cleanupRequest' + ); + } + + /** + * Clean up the parameters of a request when it is cloned + * + * @param Event $event Event emitted + */ + public function cleanupRequest(Event $event) + { + $params = $event['request']->getParams(); + unset($params[self::REDIRECT_COUNT]); + unset($params[self::PARENT_REQUEST]); + } + + /** + * Called when a request receives a redirect response + * + * @param Event $event Event emitted + */ + public function onRequestSent(Event $event) + { + $response = $event['response']; + $request = $event['request']; + + // Only act on redirect requests with Location headers + if (!$response || $request->getParams()->get(self::DISABLE)) { + return; + } + + // Trace the original request based on parameter history + $original = $this->getOriginalRequest($request); + + // Terminating condition to set the effective response on the original request + if (!$response->isRedirect() || !$response->hasHeader('Location')) { + if ($request !== $original) { + // This is a terminating redirect response, so set it on the original request + $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT)); + $original->setResponse($response); + $response->setEffectiveUrl($request->getUrl()); + } + return; + } + + $this->sendRedirectRequest($original, $request, $response); + } + + /** + * Get the original request that initiated a series of redirects + * + * @param RequestInterface $request Request to get the original request from + * + * @return RequestInterface + */ + protected function getOriginalRequest(RequestInterface $request) + { + $original = $request; + // The number of redirects is held on the original request, so determine which request that is + while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) { + $original = $parent; + } + + return $original; + } + + /** + * Create a redirect request for a specific request object + * + * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do + * (e.g. redirect POST with GET). + * + * @param RequestInterface $request Request being redirected + * @param RequestInterface $original Original request + * @param int $statusCode Status code of the redirect + * @param string $location Location header of the redirect + * + * @return RequestInterface Returns a new redirect request + * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot + */ + protected function createRedirectRequest( + RequestInterface $request, + $statusCode, + $location, + RequestInterface $original + ) { + $redirectRequest = null; + $strict = $original->getParams()->get(self::STRICT_REDIRECTS); + + // Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC + // compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST. + if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) { + $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET'); + } else { + $redirectRequest = clone $request; + } + + $redirectRequest->setIsRedirect(true); + // Always use the same response body when redirecting + $redirectRequest->setResponseBody($request->getResponseBody()); + + $location = Url::factory($location); + // If the location is not absolute, then combine it with the original URL + if (!$location->isAbsolute()) { + $originalUrl = $redirectRequest->getUrl(true); + // Remove query string parameters and just take what is present on the redirect Location header + $originalUrl->getQuery()->clear(); + $location = $originalUrl->combine((string) $location, true); + } + + $redirectRequest->setUrl($location); + + // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too) + $redirectRequest->getEventDispatcher()->addListener( + 'request.before_send', + $func = function ($e) use (&$func, $request, $redirectRequest) { + $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func); + $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request); + } + ); + + // Rewind the entity body of the request if needed + if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) { + $body = $redirectRequest->getBody(); + // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails + if ($body->ftell() && !$body->rewind()) { + throw new CouldNotRewindStreamException( + 'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably ' + . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the ' + . 'entity body of the request using setRewindFunction().' + ); + } + } + + return $redirectRequest; + } + + /** + * Prepare the request for redirection and enforce the maximum number of allowed redirects per client + * + * @param RequestInterface $original Original request + * @param RequestInterface $request Request to prepare and validate + * @param Response $response The current response + * + * @return RequestInterface + */ + protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) + { + $params = $original->getParams(); + // This is a new redirect, so increment the redirect counter + $current = $params[self::REDIRECT_COUNT] + 1; + $params[self::REDIRECT_COUNT] = $current; + // Use a provided maximum value or default to a max redirect count of 5 + $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; + + // Throw an exception if the redirect count is exceeded + if ($current > $max) { + $this->throwTooManyRedirectsException($original, $max); + return false; + } else { + // Create a redirect request based on the redirect rules set on the request + return $this->createRedirectRequest( + $request, + $response->getStatusCode(), + trim($response->getLocation()), + $original + ); + } + } + + /** + * Send a redirect request and handle any errors + * + * @param RequestInterface $original The originating request + * @param RequestInterface $request The current request being redirected + * @param Response $response The response of the current request + * + * @throws BadResponseException|\Exception + */ + protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response) + { + // Validate and create a redirect request based on the original request and current response + if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) { + try { + $redirectRequest->send(); + } catch (BadResponseException $e) { + $e->getResponse(); + if (!$e->getResponse()) { + throw $e; + } + } + } + } + + /** + * Throw a too many redirects exception for a request + * + * @param RequestInterface $original Request + * @param int $max Max allowed redirects + * + * @throws TooManyRedirectsException when too many redirects have been issued + */ + protected function throwTooManyRedirectsException(RequestInterface $original, $max) + { + $original->getEventDispatcher()->addListener( + 'request.complete', + $func = function ($e) use (&$func, $original, $max) { + $original->getEventDispatcher()->removeListener('request.complete', $func); + $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders(); + throw new TooManyRedirectsException($str); + } + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem new file mode 100644 index 000000000..18ce70381 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem @@ -0,0 +1,3870 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla downloaded on: Wed Aug 13 21:49:32 2014 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl verison 1.22. +## SHA1: bf2c15b3019e696660321d2227d942936dc50aa7 +## + + +GTE CyberTrust Global Root +========================== +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg +Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG +A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz +MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL +Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 +IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u +sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql +HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW +M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF +NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +Thawte Server CA +================ +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE +AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j +b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV +BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u +c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG +A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl +/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 +1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J +GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ +GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte Premium Server CA +======================== +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE +AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl +ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU +VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 +aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ +cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh +Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ +qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm +SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf +8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t +UCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +Equifax Secure CA +================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE +ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT +B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR +fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW +8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE +CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS +spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 +zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB +BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 +70+sB3c4 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +ValiCert Class 1 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy +MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi +GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm +DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG +lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX +icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP +Orf1LXLI +-----END CERTIFICATE----- + +ValiCert Class 2 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC +CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf +ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ +SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV +UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 +W9ViH0Pd +-----END CERTIFICATE----- + +RSA Root Certificate 1 +====================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td +3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H +BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs +3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF +V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r +on+jjBXu +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Entrust.net Secure Server CA +============================ +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg +cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl +ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG +A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi +eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p +dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ +aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 +gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw +ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l +dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw +NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow +HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN +Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 +n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Equifax Secure Global eBusiness CA +================================== +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp +bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds +b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV +PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN +qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn +hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs +MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN +I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY +NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 1 +============================= +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB +LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE +ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz +IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ +1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a +IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk +MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW +Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF +AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 +lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ +KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +RSA Security 2048 v3 +==================== +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK +ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy +MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb +BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 +Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb +WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH +KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP ++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E +FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY +v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj +0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj +VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 +nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +America Online Root Certification Authority 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG +v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z +DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh +sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP +8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z +o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf +GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF +VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft +3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds +sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America Online Root Certification Authority 2 +============================================= +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en +fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 +f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO +qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN +RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 +gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn +6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid +FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 +Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj +B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY +T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p ++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg +JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy +zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO +ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh +1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf +GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff +Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP +cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA +============================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE +ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w +HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh +bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt +vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P +jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca +C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth +vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 +22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV +HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v +dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN +BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR +EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw +MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +TDC Internet Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE +ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx +NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu +ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j +xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL +znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc +5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 +otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI +AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM +VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM +MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe +UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G +CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb +O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU +Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +UTN DATACorp SGC Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ +BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa +MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w +HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy +dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys +raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo +wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA +9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv +33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud +DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 +BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD +LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 +I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx +EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP +DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +NetLock Notary (Class A) Root +============================= +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI +EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j +ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX +DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH +EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD +VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz +cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM +D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ +z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC +/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 +tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 +4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG +A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC +Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv +bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn +LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 +ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz +IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh +IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu +b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg +Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp +bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 +ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP +ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB +CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr +KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM +8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +NetLock Business (Class B) Root +=============================== +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg +VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD +VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv +bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg +VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S +o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr +1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV +HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ +RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh +dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 +ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv +c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg +YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz +Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA +bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl +IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 +YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj +cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM +43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR +stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +NetLock Express (Class C) Root +============================== +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ +BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j +ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z +W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 +euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw +DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN +RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn +YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB +IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i +aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 +ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y +emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k +IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ +UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg +YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 +xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW +gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +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 +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 1 +============================================== +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP +MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 +acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx +MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB +TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC +aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX +yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i +Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ +8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 +W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 +sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE +q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY +nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2 +============================================== +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN +MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr +dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe +LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI +x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g +QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr +5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB +AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt +Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ +hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P +9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 +UrbnBEI= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +WellsSecure Public Root Certificate Authority +============================================= +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM +F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw +NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl +bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD +VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 +iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 +i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 +bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB +K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB +AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu +cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm +lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB +i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww +GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI +K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 +bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj +qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es +E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ +tylv2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +IGC/A +===== +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD +VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE +Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy +MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI +EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT +STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 +TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW +So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy +HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd +frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ +tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB +egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC +iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK +q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q +MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI +lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF +0mBWWg== +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE +BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL +EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 +MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz +dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT +GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG +d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N +oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc +QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ +PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb +MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG +IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD +VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 +LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A +dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA +4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg +AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA +egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 +Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO +PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv +c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h +cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw +IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT +WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV +MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER +MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp +Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal +HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT +nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE +aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK +yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB +S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. +====================================== +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT +AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg +LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w +HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ +U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh +IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN +yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU +2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 +4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP +2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm +8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf +HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa +Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK +5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b +czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g +ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF +BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug +cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf +AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX +EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v +/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 +MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 +3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk +eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f +/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h +RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU +Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 2 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw +MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw +IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 +xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ +Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u +SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G +dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ +KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj +TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP +JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk +vQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 3 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw +MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W +yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo +6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ +uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk +2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE +O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 +yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 +IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal +092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc +5A== +-----END CERTIFICATE----- + +TC TrustCenter Universal CA I +============================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN +MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg +VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw +JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC +qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv +xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw +ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O +gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j +BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG +1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy +vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 +ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a +7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +ComSign Secured CA +================== +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE +AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w +NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD +QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs +49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH +7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB +kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 +9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw +AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t +U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA +j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC +AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a +BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp +FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP +51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +Buypass Class 2 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 +MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M +cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 +0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 +0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R +uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV +1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt +7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 +fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w +wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +Buypass Class 3 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 +MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx +ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 +n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia +AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c +1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 +pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA +EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 +htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj +el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 +========================================================================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +ApplicationCA - Japanese Government +=================================== +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT +SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw +MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl +cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 +fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN +wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE +jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu +nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU +WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV +BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD +vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs +o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g +/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD +io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW +dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +============================================ +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +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 +66+KAQ== +-----END CERTIFICATE----- + +CA Disig +======== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK +QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw +MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz +bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm +GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD +Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo +hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt +ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w +gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P +AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz +aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff +ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa +BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t +WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 +mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K +ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA +4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +Juur-SK +======= +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA +c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw +DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG +SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy +aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf +TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC ++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw +UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa +Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF +MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD +HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh +AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA +cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr +AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw +cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G +A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo +ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL +abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 +IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh +Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 +yyqcjg== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi +=================================================== +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz +ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 +MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 +cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u +aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY +8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y +jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI +JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk +9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG +SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d +F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq +D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 +Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================= +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +Root CA Generalitat Valenciana +============================== +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE +ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 +IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 +WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE +CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 +F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B +ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ +D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte +JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB +AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n +dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB +ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl +AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA +YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy +AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt +AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA +YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu +AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA +OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 +dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV +BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S +b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh +TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz +Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 +NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH +iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt ++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +A-Trust-nQual-03 +================ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE +Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy +a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R +dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw +RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 +ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 +c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA +zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n +yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE +SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 +iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V +cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV +eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 +ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr +sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd +JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 +ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ +Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 +dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu +c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv +bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 +aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t +L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 +fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm +N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN +Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T +tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX +e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA +2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs +HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib +D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +StartCom Certification Authority G2 +=================================== +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE +ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O +o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG +4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi +Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul +Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs +O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H +vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L +nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS +FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa +z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ +KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk +J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ +JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG +/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc +nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld +blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc +l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm +7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm +obp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2007 +================================================= +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X +DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl +a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N +YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv +KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya +KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT +rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC +AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s +Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO +Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb +BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK +poRq0Tl9 +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +PSCProcert +========== +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk +ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ +MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz +dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl +cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw +IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw +MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w +DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD +ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp +Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC +wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA +3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh +RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO +EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 +0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU +td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw +Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp +r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ +AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz +Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId +xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp +ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH +EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h +Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k +ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG +9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG +MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG +LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 +ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy +YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o +dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq +T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN +g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q +uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 +n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn +FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo +5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq +3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 +poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y +eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +China Internet Network Information Center EV Certificates Root +============================================================== +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D +aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg +Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG +A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM +PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl +cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y +jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV +98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H +klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 +KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC +7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD +glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 +0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM +7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 +5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +Swisscom Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2 +MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM +LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo +ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ +wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH +Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a +SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS +NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab +mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY +Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3 +qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu +MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO +v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ +82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz +o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs +a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx +OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW +mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o ++sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC +rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX +5OfNeOI5wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +Swisscom Root EV CA 2 +===================== +-----BEGIN CERTIFICATE----- +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 +l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +CA Disig Root R1 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy +3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 +u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 +m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk +CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa +YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 +vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL +LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX +ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is +XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ +04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B +LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM +CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb +VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 +YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS +ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix +lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N +UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ +a7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php new file mode 100644 index 000000000..dbd4c1841 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php @@ -0,0 +1,157 @@ +createRequest($method, $url, null, null, $options); + + if (isset($options['stream'])) { + if ($options['stream'] instanceof StreamRequestFactoryInterface) { + return $options['stream']->fromRequest($request); + } elseif ($options['stream'] == true) { + $streamFactory = new PhpStreamRequestFactory(); + return $streamFactory->fromRequest($request); + } + } + + return $request->send(); + } + + /** + * Send a GET request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function get($url, $options = array()) + { + return self::request('GET', $url, $options); + } + + /** + * Send a HEAD request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function head($url, $options = array()) + { + return self::request('HEAD', $url, $options); + } + + /** + * Send a DELETE request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function delete($url, $options = array()) + { + return self::request('DELETE', $url, $options); + } + + /** + * Send a POST request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function post($url, $options = array()) + { + return self::request('POST', $url, $options); + } + + /** + * Send a PUT request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function put($url, $options = array()) + { + return self::request('PUT', $url, $options); + } + + /** + * Send a PATCH request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function patch($url, $options = array()) + { + return self::request('PATCH', $url, $options); + } + + /** + * Send an OPTIONS request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function options($url, $options = array()) + { + return self::request('OPTIONS', $url, $options); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php new file mode 100644 index 000000000..6a4e77245 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php @@ -0,0 +1,554 @@ + null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + if (false === ($parts = parse_url($url))) { + throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url); + } + + $parts += $defaults; + + // Convert the query string into a QueryString object + if ($parts['query'] || 0 !== strlen($parts['query'])) { + $parts['query'] = QueryString::fromString($parts['query']); + } + + return new static($parts['scheme'], $parts['host'], $parts['user'], + $parts['pass'], $parts['port'], $parts['path'], $parts['query'], + $parts['fragment']); + } + + /** + * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided. + * + * @param array $parts Array of parse_url parts + * + * @return string + */ + public static function buildUrl(array $parts) + { + $url = $scheme = ''; + + if (isset($parts['scheme'])) { + $scheme = $parts['scheme']; + $url .= $scheme . ':'; + } + + if (isset($parts['host'])) { + $url .= '//'; + if (isset($parts['user'])) { + $url .= $parts['user']; + if (isset($parts['pass'])) { + $url .= ':' . $parts['pass']; + } + $url .= '@'; + } + + $url .= $parts['host']; + + // Only include the port if it is not the default port of the scheme + if (isset($parts['port']) + && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443)) + ) { + $url .= ':' . $parts['port']; + } + } + + // Add the path component if present + if (isset($parts['path']) && 0 !== strlen($parts['path'])) { + // Always ensure that the path begins with '/' if set and something is before the path + if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') { + $url .= '/'; + } + $url .= $parts['path']; + } + + // Add the query string if present + if (isset($parts['query'])) { + $url .= '?' . $parts['query']; + } + + // Ensure that # is only added to the url if fragment contains anything. + if (isset($parts['fragment'])) { + $url .= '#' . $parts['fragment']; + } + + return $url; + } + + /** + * Create a new URL from URL parts + * + * @param string $scheme Scheme of the URL + * @param string $host Host of the URL + * @param string $username Username of the URL + * @param string $password Password of the URL + * @param int $port Port of the URL + * @param string $path Path of the URL + * @param QueryString|array|string $query Query string of the URL + * @param string $fragment Fragment of the URL + */ + public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null) + { + $this->scheme = $scheme; + $this->host = $host; + $this->port = $port; + $this->username = $username; + $this->password = $password; + $this->fragment = $fragment; + if (!$query) { + $this->query = new QueryString(); + } else { + $this->setQuery($query); + } + $this->setPath($path); + } + + /** + * Clone the URL + */ + public function __clone() + { + $this->query = clone $this->query; + } + + /** + * Returns the URL as a URL string + * + * @return string + */ + public function __toString() + { + return self::buildUrl($this->getParts()); + } + + /** + * Get the parts of the URL as an array + * + * @return array + */ + public function getParts() + { + $query = (string) $this->query; + + return array( + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->getPath(), + 'query' => $query !== '' ? $query : null, + 'fragment' => $this->fragment, + ); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) + * + * @return Url + */ + public function setHost($host) + { + if (strpos($host, ':') === false) { + $this->host = $host; + } else { + list($host, $port) = explode(':', $host); + $this->host = $host; + $this->setPort($port); + } + + return $this; + } + + /** + * Get the host part of the URL + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the scheme part of the URL (http, https, ftp, etc) + * + * @param string $scheme Scheme to set + * + * @return Url + */ + public function setScheme($scheme) + { + if ($this->scheme == 'http' && $this->port == 80) { + $this->port = null; + } elseif ($this->scheme == 'https' && $this->port == 443) { + $this->port = null; + } + + $this->scheme = $scheme; + + return $this; + } + + /** + * Get the scheme part of the URL + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Set the port part of the URL + * + * @param int $port Port to set + * + * @return Url + */ + public function setPort($port) + { + $this->port = $port; + + return $this; + } + + /** + * Get the port part of the URl. Will return the default port for a given scheme if no port has been set. + * + * @return int|null + */ + public function getPort() + { + if ($this->port) { + return $this->port; + } elseif ($this->scheme == 'http') { + return 80; + } elseif ($this->scheme == 'https') { + return 443; + } + + return null; + } + + /** + * Set the path part of the URL + * + * @param array|string $path Path string or array of path segments + * + * @return Url + */ + public function setPath($path) + { + static $pathReplace = array(' ' => '%20', '?' => '%3F'); + if (is_array($path)) { + $path = '/' . implode('/', $path); + } + + $this->path = strtr($path, $pathReplace); + + return $this; + } + + /** + * Normalize the URL so that double slashes and relative paths are removed + * + * @return Url + */ + public function normalizePath() + { + if (!$this->path || $this->path == '/' || $this->path == '*') { + return $this; + } + + $results = array(); + $segments = $this->getPathSegments(); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif ($segment != '.' && $segment != '') { + $results[] = $segment; + } + } + + // Combine the normalized parts and add the leading slash if needed + $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results); + + // Add the trailing slash if necessary + if ($this->path != '/' && end($segments) == '') { + $this->path .= '/'; + } + + return $this; + } + + /** + * Add a relative path to the currently set path. + * + * @param string $relativePath Relative path to add + * + * @return Url + */ + public function addPath($relativePath) + { + if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) { + // Add a leading slash if needed + if ($relativePath[0] != '/') { + $relativePath = '/' . $relativePath; + } + $this->setPath(str_replace('//', '/', $this->path . $relativePath)); + } + + return $this; + } + + /** + * Get the path part of the URL + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get the path segments of the URL as an array + * + * @return array + */ + public function getPathSegments() + { + return array_slice(explode('/', $this->getPath()), 1); + } + + /** + * Set the password part of the URL + * + * @param string $password Password to set + * + * @return Url + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get the password part of the URL + * + * @return null|string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the username part of the URL + * + * @param string $username Username to set + * + * @return Url + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get the username part of the URl + * + * @return null|string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the query part of the URL as a QueryString object + * + * @return QueryString + */ + public function getQuery() + { + return $this->query; + } + + /** + * Set the query part of the URL + * + * @param QueryString|string|array $query Query to set + * + * @return Url + */ + public function setQuery($query) + { + if (is_string($query)) { + $output = null; + parse_str($query, $output); + $this->query = new QueryString($output); + } elseif (is_array($query)) { + $this->query = new QueryString($query); + } elseif ($query instanceof QueryString) { + $this->query = $query; + } + + return $this; + } + + /** + * Get the fragment part of the URL + * + * @return null|string + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * Set the fragment part of the URL + * + * @param string $fragment Fragment to set + * + * @return Url + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + + return $this; + } + + /** + * Check if this is an absolute URL + * + * @return bool + */ + public function isAbsolute() + { + return $this->scheme && $this->host; + } + + /** + * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4. + * + * @param string $url Relative URL to combine with + * @param bool $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first + * released, Guzzle used an incorrect algorithm for combining relative URL paths. In + * order to not break users, we introduced this flag to allow the merging of URLs based + * on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with + * "bar" would become "http://a.com/foo/bar". When this value is set to false, it would + * become "http://a.com/foo/baz/bar". + * @return Url + * @throws InvalidArgumentException + * @link http://tools.ietf.org/html/rfc3986#section-5.4 + */ + public function combine($url, $strictRfc3986 = false) + { + $url = self::factory($url); + + // Use the more absolute URL as the base URL + if (!$this->isAbsolute() && $url->isAbsolute()) { + $url = $url->combine($this); + } + + // Passing a URL with a scheme overrides everything + if ($buffer = $url->getScheme()) { + $this->scheme = $buffer; + $this->host = $url->getHost(); + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + // Setting a host overrides the entire rest of the URL + if ($buffer = $url->getHost()) { + $this->host = $buffer; + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + $path = $url->getPath(); + $query = $url->getQuery(); + + if (!$path) { + if (count($query)) { + $this->addQuery($query, $strictRfc3986); + } + } else { + if ($path[0] == '/') { + $this->path = $path; + } elseif ($strictRfc3986) { + $this->path .= '/../' . $path; + } else { + $this->path .= '/' . $path; + } + $this->normalizePath(); + $this->addQuery($query, $strictRfc3986); + } + + $this->fragment = $url->getFragment(); + + return $this; + } + + private function addQuery(QueryString $new, $strictRfc386) + { + if (!$strictRfc386) { + $new->merge($this->query); + } + + $this->query = $new; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json new file mode 100644 index 000000000..9384a5bf9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json @@ -0,0 +1,32 @@ +{ + "name": "guzzle/http", + "description": "HTTP libraries used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": ["http client", "http", "client", "Guzzle", "curl"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version", + "guzzle/parser": "self.version", + "guzzle/stream": "self.version" + }, + "suggest": { + "ext-curl": "*" + }, + "autoload": { + "psr-0": { "Guzzle\\Http": "" } + }, + "target-dir": "Guzzle/Http", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php new file mode 100644 index 000000000..c6997734c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php @@ -0,0 +1,38 @@ + array(), + 'camel' => array() + ); + + /** @var int Max entries per cache */ + protected $maxCacheSize; + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param int $maxCacheSize Maximum number of cached items to hold per cache + */ + public function __construct(InflectorInterface $inflector, $maxCacheSize = 500) + { + $this->decoratedInflector = $inflector; + $this->maxCacheSize = $maxCacheSize; + } + + public function snake($word) + { + if (!isset($this->cache['snake'][$word])) { + $this->pruneCache('snake'); + $this->cache['snake'][$word] = $this->decoratedInflector->snake($word); + } + + return $this->cache['snake'][$word]; + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + if (!isset($this->cache['camel'][$word])) { + $this->pruneCache('camel'); + $this->cache['camel'][$word] = $this->decoratedInflector->camel($word); + } + + return $this->cache['camel'][$word]; + } + + /** + * Prune one of the named caches by removing 20% of the cache if it is full + * + * @param string $cache Type of cache to prune + */ + protected function pruneCache($cache) + { + if (count($this->cache[$cache]) == $this->maxCacheSize) { + $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php new file mode 100644 index 000000000..db37e4fe4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php @@ -0,0 +1,59 @@ + array(), + 'camel' => array() + ); + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param array $snake Hash of pre-computed camel to snake + * @param array $camel Hash of pre-computed snake to camel + * @param bool $mirror Mirror snake and camel reflections + */ + public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false) + { + if ($mirror) { + $camel = array_merge(array_flip($snake), $camel); + $snake = array_merge(array_flip($camel), $snake); + } + + $this->decoratedInflector = $inflector; + $this->mapping = array( + 'snake' => $snake, + 'camel' => $camel + ); + } + + public function snake($word) + { + return isset($this->mapping['snake'][$word]) + ? $this->mapping['snake'][$word] + : $this->decoratedInflector->snake($word); + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + return isset($this->mapping['camel'][$word]) + ? $this->mapping['camel'][$word] + : $this->decoratedInflector->camel($word); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json new file mode 100644 index 000000000..93f9e7b72 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json @@ -0,0 +1,26 @@ +{ + "name": "guzzle/inflection", + "description": "Guzzle inflection component", + "homepage": "http://guzzlephp.org/", + "keywords": ["inflection", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Inflection": "" } + }, + "target-dir": "Guzzle/Inflection", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php new file mode 100644 index 000000000..1b6bd7e53 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php @@ -0,0 +1,19 @@ +getArrayIterator()->append($iterator); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php new file mode 100644 index 000000000..d76cdd439 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php @@ -0,0 +1,56 @@ +chunkSize = $chunkSize; + } + + public function rewind() + { + parent::rewind(); + $this->next(); + } + + public function next() + { + $this->chunk = array(); + for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) { + $this->chunk[] = parent::current(); + parent::next(); + } + } + + public function current() + { + return $this->chunk; + } + + public function valid() + { + return (bool) $this->chunk; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php new file mode 100644 index 000000000..b103367b6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php @@ -0,0 +1,36 @@ +callback = $callback; + } + + public function accept() + { + return call_user_func($this->callback, $this->current()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php new file mode 100644 index 000000000..7e586bda6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php @@ -0,0 +1,34 @@ +callback = $callback; + } + + public function current() + { + return call_user_func($this->callback, parent::current()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php new file mode 100644 index 000000000..de4ab0360 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php @@ -0,0 +1,27 @@ +getInnerIterator(); + while ($i instanceof \OuterIterator) { + $i = $i->getInnerIterator(); + } + + return call_user_func_array(array($i, $name), $args); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md new file mode 100644 index 000000000..8bb7e08e2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md @@ -0,0 +1,25 @@ +Guzzle Iterator +=============== + +Provides useful Iterators and Iterator decorators + +- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays +- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available +- MapIterator: Maps values before yielding +- MethodProxyIterator: Proxies missing method calls to the innermost iterator + +### Installing via Composer + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/iterator:~3.0 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json new file mode 100644 index 000000000..ee1737987 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/iterator", + "description": "Provides helpful iterators and iterator decorators", + "keywords": ["iterator", "guzzle"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": ">=2.8.0" + }, + "autoload": { + "psr-0": { "Guzzle\\Iterator": "/" } + }, + "target-dir": "Guzzle/Iterator", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php new file mode 100644 index 000000000..7f6271bcb --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php @@ -0,0 +1,16 @@ +log; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php new file mode 100644 index 000000000..a70fc8d42 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php @@ -0,0 +1,34 @@ +logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras); + } + + /** + * Get logged entries + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Clears logged entries + */ + public function clearLogs() + { + $this->logs = array(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php new file mode 100644 index 000000000..d4bb73f21 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php @@ -0,0 +1,23 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + call_user_func($this->log, $message, $priority, $extras); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php new file mode 100644 index 000000000..d7ac4ea7c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php @@ -0,0 +1,18 @@ +>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}"; + const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}'; + + /** + * @var string Template used to format log messages + */ + protected $template; + + /** + * @param string $template Log message template + */ + public function __construct($template = self::DEFAULT_FORMAT) + { + $this->template = $template ?: self::DEFAULT_FORMAT; + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->template = $template; + + return $this; + } + + /** + * Returns a formatted message + * + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received + * @param CurlHandle $handle Curl handle associated with the message + * @param array $customData Associative array of custom template data + * + * @return string + */ + public function format( + RequestInterface $request, + Response $response = null, + CurlHandle $handle = null, + array $customData = array() + ) { + $cache = $customData; + + return preg_replace_callback( + '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', + function (array $matches) use ($request, $response, $handle, &$cache) { + + if (array_key_exists($matches[1], $cache)) { + return $cache[$matches[1]]; + } + + $result = ''; + switch ($matches[1]) { + case 'request': + $result = (string) $request; + break; + case 'response': + $result = (string) $response; + break; + case 'req_body': + $result = $request instanceof EntityEnclosingRequestInterface + ? (string) $request->getBody() : ''; + break; + case 'res_body': + $result = $response ? $response->getBody(true) : ''; + break; + case 'ts': + $result = gmdate('c'); + break; + case 'method': + $result = $request->getMethod(); + break; + case 'url': + $result = (string) $request->getUrl(); + break; + case 'resource': + $result = $request->getResource(); + break; + case 'protocol': + $result = 'HTTP'; + break; + case 'version': + $result = $request->getProtocolVersion(); + break; + case 'host': + $result = $request->getHost(); + break; + case 'hostname': + $result = gethostname(); + break; + case 'port': + $result = $request->getPort(); + break; + case 'code': + $result = $response ? $response->getStatusCode() : ''; + break; + case 'phrase': + $result = $response ? $response->getReasonPhrase() : ''; + break; + case 'connect_time': + $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME) + ? $handle->getInfo(CURLINFO_CONNECT_TIME) + : ($response ? $response->getInfo('connect_time') : ''); + break; + case 'total_time': + $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME) + ? $handle->getInfo(CURLINFO_TOTAL_TIME) + : ($response ? $response->getInfo('total_time') : ''); + break; + case 'curl_error': + $result = $handle ? $handle->getError() : ''; + break; + case 'curl_code': + $result = $handle ? $handle->getErrorNo() : ''; + break; + case 'curl_stderr': + $result = $handle ? $handle->getStderr() : ''; + break; + default: + if (strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeader(substr($matches[1], 11)); + } elseif ($response && strpos($matches[1], 'res_header_') === 0) { + $result = $response->getHeader(substr($matches[1], 11)); + } + } + + $cache[$matches[1]] = $result; + return $result; + }, + $this->template + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php new file mode 100644 index 000000000..6afe7b62a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php @@ -0,0 +1,34 @@ + Logger::DEBUG, + LOG_INFO => Logger::INFO, + LOG_WARNING => Logger::WARNING, + LOG_ERR => Logger::ERROR, + LOG_CRIT => Logger::CRITICAL, + LOG_ALERT => Logger::ALERT + ); + + public function __construct(Logger $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->addRecord(self::$mapping[$priority], $message, $extras); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php new file mode 100644 index 000000000..38a2b600d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php @@ -0,0 +1,36 @@ + LogLevel::DEBUG, + LOG_INFO => LogLevel::INFO, + LOG_WARNING => LogLevel::WARNING, + LOG_ERR => LogLevel::ERROR, + LOG_CRIT => LogLevel::CRITICAL, + LOG_ALERT => LogLevel::ALERT + ); + + public function __construct(LoggerInterface $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log(self::$mapping[$priority], $message, $extras); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php new file mode 100644 index 000000000..0ea8e3b1d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php @@ -0,0 +1,24 @@ +log = $logObject; + Version::warn(__CLASS__ . ' is deprecated'); + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($message, $priority, $extras); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php new file mode 100644 index 000000000..863f6a1c4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php @@ -0,0 +1,21 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($priority, $message, $extras); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json new file mode 100644 index 000000000..a8213e8b4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json @@ -0,0 +1,29 @@ +{ + "name": "guzzle/log", + "description": "Guzzle log adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["log", "adapter", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Log": "" } + }, + "suggest": { + "guzzle/http": "self.version" + }, + "target-dir": "Guzzle/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php new file mode 100644 index 000000000..4349eeb38 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php @@ -0,0 +1,131 @@ + 'Domain', + 'path' => 'Path', + 'max_age' => 'Max-Age', + 'expires' => 'Expires', + 'version' => 'Version', + 'secure' => 'Secure', + 'port' => 'Port', + 'discard' => 'Discard', + 'comment' => 'Comment', + 'comment_url' => 'Comment-Url', + 'http_only' => 'HttpOnly' + ); + + public function parseCookie($cookie, $host = null, $path = null, $decode = false) + { + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return false; + } + + // Create the default return array + $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( + 'cookies' => array(), + 'data' => array(), + 'path' => null, + 'http_only' => false, + 'discard' => false, + 'domain' => $host + )); + $foundNonCookies = 0; + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + + if (count($cookieParts) == 1) { + // Can be a single value (e.g. secure, httpOnly) + $value = true; + } else { + // Be sure to strip wrapping quotes + $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); + if ($decode) { + $value = urldecode($value); + } + } + + // Only check for non-cookies when cookies have been found + if (!empty($data['cookies'])) { + foreach (self::$cookieParts as $mapValue => $search) { + if (!strcasecmp($search, $key)) { + $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; + $foundNonCookies++; + continue 2; + } + } + } + + // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a + // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data. + $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; + } + + // Calculate the expires date + if (!$data['expires'] && $data['max_age']) { + $data['expires'] = time() + (int) $data['max_age']; + } + + // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4 + // "If the attribute-value is empty or if the first character of the + // attribute-value is not %x2F ("/"): + // Let cookie-path be the default-path. + // Otherwise: + // Let cookie-path be the attribute-value." + if (!$data['path'] || substr($data['path'], 0, 1) !== '/') { + $data['path'] = $this->getDefaultPath($path); + } + + return $data; + } + + /** + * Get default cookie path according to RFC 6265 + * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match + * + * @param string $path Request uri-path + * + * @return string + */ + protected function getDefaultPath($path) { + // "The user agent MUST use an algorithm equivalent to the following algorithm + // to compute the default-path of a cookie:" + + // "2. If the uri-path is empty or if the first character of the uri-path is not + // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. + if (empty($path) || substr($path, 0, 1) !== '/') { + return '/'; + } + + // "3. If the uri-path contains no more than one %x2F ("/") character, output + // %x2F ("/") and skip the remaining step." + if ($path === "/") { + return $path; + } + + $rightSlashPos = strrpos($path, '/'); + if ($rightSlashPos === 0) { + return "/"; + } + + // "4. Output the characters of the uri-path from the first character up to, + // but not including, the right-most %x2F ("/")." + return substr($path, 0, $rightSlashPos); + + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php new file mode 100644 index 000000000..d21ffe21c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php @@ -0,0 +1,33 @@ + $requestUrl, + 'scheme' => 'http' + ); + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = null; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php new file mode 100644 index 000000000..efc1aa322 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php @@ -0,0 +1,110 @@ +parseMessage($message); + + // Parse the protocol and protocol version + if (isset($parts['start_line'][2])) { + $startParts = explode('/', $parts['start_line'][2]); + $protocol = strtoupper($startParts[0]); + $version = isset($startParts[1]) ? $startParts[1] : '1.1'; + } else { + $protocol = 'HTTP'; + $version = '1.1'; + } + + $parsed = array( + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => $protocol, + 'version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage(isset($parts['start_line'][1]) ? $parts['start_line'][1] : '' , $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = $this->parseMessage($message); + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return array( + 'protocol' => $protocol, + 'version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array + */ + protected function parseMessage($message) + { + $startLine = null; + $headers = array(); + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + $startLine = explode(' ', $line, 3); + } elseif (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = array($headers[$key], $value); + } else { + $headers[$key][] = $value; + } + } + } + + return array( + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php new file mode 100644 index 000000000..cc448088d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php @@ -0,0 +1,27 @@ + $parts->requestMethod, + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'headers' => $parts->headers, + 'body' => $parts->body + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = http_parse_message($message); + + return array( + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'code' => $parts->responseCode, + 'reason_phrase' => $parts->responseStatus, + 'headers' => $parts->headers, + 'body' => $parts->body + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php new file mode 100644 index 000000000..f8386831c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php @@ -0,0 +1,75 @@ + 'Guzzle\\Parser\\Message\\MessageParser', + 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser', + 'url' => 'Guzzle\\Parser\\Url\\UrlParser', + 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate', + ); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new static; + } + + return self::$instance; + } + + public function __construct() + { + // Use the PECL URI template parser if available + if (extension_loaded('uri_template')) { + $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate'; + } + } + + /** + * Get a parser by name from an instance + * + * @param string $name Name of the parser to retrieve + * + * @return mixed|null + */ + public function getParser($name) + { + if (!isset($this->instances[$name])) { + if (!isset($this->mapping[$name])) { + return null; + } + $class = $this->mapping[$name]; + $this->instances[$name] = new $class(); + } + + return $this->instances[$name]; + } + + /** + * Register a custom parser by name with the register + * + * @param string $name Name or handle of the parser to register + * @param mixed $parser Instantiated parser to register + */ + public function registerParser($name, $parser) + { + $this->instances[$name] = $parser; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php new file mode 100644 index 000000000..b0764e837 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php @@ -0,0 +1,26 @@ + true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true + ); + + /** @var array Delimiters */ + private static $delims = array( + ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' + ); + + /** @var array Percent encoded delimiters */ + private static $delimsPct = array( + '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D' + ); + + public function expand($template, array $variables) + { + if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template); + } + + /** + * Set the regex patten used to expand URI templates + * + * @param string $regexPattern + */ + public function setRegex($regexPattern) + { + $this->regex = $regexPattern; + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + // Check for URI operators + $operator = ''; + + if (isset(self::$operatorHash[$expression[0]])) { + $operator = $expression[0]; + $expression = substr($expression, 1); + } + + $values = explode(',', $expression); + foreach ($values as &$value) { + $value = trim($value); + $varspec = array(); + $substrPos = strpos($value, ':'); + if ($substrPos) { + $varspec['value'] = substr($value, 0, $substrPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $substrPos + 1); + } elseif (substr($value, -1) == '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $value = $varspec; + } + + return array( + 'operator' => $operator, + 'values' => $values + ); + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = array( + '+' => '%20', + '%7e' => '~' + ); + + $parsed = self::parseExpression($matches[1]); + $replacements = array(); + + $prefix = $parsed['operator']; + $joiner = $parsed['operator']; + $useQueryString = false; + if ($parsed['operator'] == '?') { + $joiner = '&'; + $useQueryString = true; + } elseif ($parsed['operator'] == '&') { + $useQueryString = true; + } elseif ($parsed['operator'] == '#') { + $joiner = ','; + } elseif ($parsed['operator'] == ';') { + $useQueryString = true; + } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') { + $joiner = ','; + $prefix = ''; + } + + foreach ($parsed['values'] as $value) { + + if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQueryString = $useQueryString; + $expanded = ''; + + if (is_array($variable)) { + + $isAssoc = $this->isAssoc($variable); + $kvp = array(); + foreach ($variable as $key => $var) { + + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] == '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested structures + $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQueryString) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQueryString = false; + } elseif ($value['modifier'] == '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode modifier with an associative array + $actuallyUseQueryString = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the explode modifier is not set, then the + // result must be a comma separated list of keys followed by their respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + + } else { + if ($value['modifier'] == ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQueryString) { + if (!$expanded && $joiner != '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } + + /** + * Removes percent encoding on reserved characters (used with + and # modifiers) + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php new file mode 100644 index 000000000..c81d51548 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php @@ -0,0 +1,21 @@ +utf8 = $utf8; + } + + public function parseUrl($url) + { + Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()'); + + static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + $parts = parse_url($url); + + // Need to handle query parsing specially for UTF-8 requirements + if ($this->utf8 && isset($parts['query'])) { + $queryPos = strpos($url, '?'); + if (isset($parts['fragment'])) { + $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1); + } else { + $parts['query'] = substr($url, $queryPos + 1); + } + } + + return $parts + $defaults; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php new file mode 100644 index 000000000..89ac4b307 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php @@ -0,0 +1,19 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Parser": "" } + }, + "target-dir": "Guzzle/Parser", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php new file mode 100644 index 000000000..ae5941873 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php @@ -0,0 +1,84 @@ + 'onBeforeSend', + 'request.exception' => 'onRequestTimeout', + 'request.sent' => 'onRequestSent', + 'curl.callback.progress' => 'onCurlProgress' + ); + } + + /** + * Event used to ensure that progress callback are emitted from the curl handle's request mediator. + * + * @param Event $event + */ + public function onBeforeSend(Event $event) + { + // Ensure that progress callbacks are dispatched + $event['request']->getCurlOptions()->set('progress', true); + } + + /** + * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to + * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with + * transmitting the request, and tell curl not download a body. + * + * @param Event $event + */ + public function onCurlProgress(Event $event) + { + if ($event['handle'] && + ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded'])) + ) { + // Timeout after 1ms + curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1); + // Even if the response is quick, tell curl not to download the body. + // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the + // request method is not converted to a HEAD request before the request was sent via curl. + if ($event['uploaded']) { + curl_setopt($event['handle'], CURLOPT_NOBODY, true); + } + } + } + + /** + * Event emitted when a curl exception occurs. Ignore the exception and set a mock response. + * + * @param Event $event + */ + public function onRequestTimeout(Event $event) + { + if ($event['exception'] instanceof CurlException) { + $event['request']->setResponse(new Response(200, array( + 'X-Guzzle-Async' => 'Did not wait for the response' + ))); + } + } + + /** + * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the + * caller that there is no body in the message. + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + // Let the caller know this was meant to be async + $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json new file mode 100644 index 000000000..dc3fc5bf8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-async", + "description": "Guzzle async request plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Async": "" } + }, + "target-dir": "Guzzle/Plugin/Async", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php new file mode 100644 index 000000000..0a8598345 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php @@ -0,0 +1,91 @@ +next = $next; + } + + /** + * Get the next backoff strategy in the chain + * + * @return AbstractBackoffStrategy|null + */ + public function getNext() + { + return $this->next; + } + + public function getBackoffPeriod( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ) { + $delay = $this->getDelay($retries, $request, $response, $e); + if ($delay === false) { + // The strategy knows that this must not be retried + return false; + } elseif ($delay === null) { + // If the strategy is deferring a decision and the next strategy will not make a decision then return false + return !$this->next || !$this->next->makesDecision() + ? false + : $this->next->getBackoffPeriod($retries, $request, $response, $e); + } elseif ($delay === true) { + // if the strategy knows that it must retry but is deferring to the next to determine the delay + if (!$this->next) { + return 0; + } else { + $next = $this->next; + while ($next->makesDecision() && $next->getNext()) { + $next = $next->getNext(); + } + return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0; + } + } else { + return $delay; + } + } + + /** + * Check if the strategy does filtering and makes decisions on whether or not to retry. + * + * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff + * decision. + * + * @return bool + */ + abstract public function makesDecision(); + + /** + * Implement the concrete strategy + * + * @param int $retries Number of retries of the request + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received. Note that there may not be a response + * @param HttpException $e Exception that was encountered if any + * + * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true + * or null to defer to the next strategy if available, and if not, return 0. + */ + abstract protected function getDelay( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ); +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php new file mode 100644 index 000000000..6ebee6c1a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php @@ -0,0 +1,40 @@ +errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1); + $this->next = $next; + } + + /** + * Get the default failure codes to retry + * + * @return array + */ + public static function getDefaultFailureCodes() + { + return static::$defaultErrorCodes; + } + + public function makesDecision() + { + return true; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php new file mode 100644 index 000000000..ec54c289e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php @@ -0,0 +1,76 @@ +logger = $logger; + $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT); + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->formatter->setTemplate($template); + + return $this; + } + + /** + * Called when a request is being retried + * + * @param Event $event Event emitted + */ + public function onRequestRetry(Event $event) + { + $this->logger->log($this->formatter->format( + $event['request'], + $event['response'], + $event['handle'], + array( + 'retries' => $event['retries'], + 'delay' => $event['delay'] + ) + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php new file mode 100644 index 000000000..99ace0538 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php @@ -0,0 +1,126 @@ +strategy = $strategy; + } + + /** + * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors + * + * @param int $maxRetries Maximum number of retries + * @param array $httpCodes HTTP response codes to retry + * @param array $curlCodes cURL error codes to retry + * + * @return self + */ + public static function getExponentialBackoff( + $maxRetries = 3, + array $httpCodes = null, + array $curlCodes = null + ) { + return new self(new TruncatedBackoffStrategy($maxRetries, + new HttpBackoffStrategy($httpCodes, + new CurlBackoffStrategy($curlCodes, + new ExponentialBackoffStrategy() + ) + ) + )); + } + + public static function getAllEvents() + { + return array(self::RETRY_EVENT); + } + + public static function getSubscribedEvents() + { + return array( + 'request.sent' => 'onRequestSent', + 'request.exception' => 'onRequestSent', + CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll' + ); + } + + /** + * Called when a request has been sent and isn't finished processing + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $exception = $event['exception']; + + $params = $request->getParams(); + $retries = (int) $params->get(self::RETRY_PARAM); + $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception); + + if ($delay !== false) { + // Calculate how long to wait until the request should be retried + $params->set(self::RETRY_PARAM, ++$retries) + ->set(self::DELAY_PARAM, microtime(true) + $delay); + // Send the request again + $request->setState(RequestInterface::STATE_TRANSFER); + $this->dispatch(self::RETRY_EVENT, array( + 'request' => $request, + 'response' => $response, + 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null, + 'retries' => $retries, + 'delay' => $delay + )); + } + } + + /** + * Called when a request is polling in the curl multi object + * + * @param Event $event + */ + public function onRequestPoll(Event $event) + { + $request = $event['request']; + $delay = $request->getParams()->get(self::DELAY_PARAM); + + // If the duration of the delay has passed, retry the request using the pool + if (null !== $delay && microtime(true) >= $delay) { + // Remove the request from the pool and then add it back again. This is required for cURL to know that we + // want to retry sending the easy handle. + $request->getParams()->remove(self::DELAY_PARAM); + // Rewind the request body if possible + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) { + $request->getBody()->seek(0); + } + $multi = $event['curl_multi']; + $multi->remove($request); + $multi->add($request); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php new file mode 100644 index 000000000..4e590dbe0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php @@ -0,0 +1,30 @@ +callback = $callback; + $this->decision = (bool) $decision; + $this->next = $next; + } + + public function makesDecision() + { + return $this->decision; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return call_user_func($this->callback, $retries, $request, $response, $e); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php new file mode 100644 index 000000000..061d2a407 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php @@ -0,0 +1,34 @@ +delay = $delay; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $this->delay; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php new file mode 100644 index 000000000..a584ed4a2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php @@ -0,0 +1,28 @@ +errorCodes[$e->getErrorNo()]) ? true : null; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php new file mode 100644 index 000000000..fb2912d50 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php @@ -0,0 +1,25 @@ +isSuccessful()) { + return false; + } else { + return isset($this->errorCodes[$response->getStatusCode()]) ? true : null; + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php new file mode 100644 index 000000000..b35e8a490 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php @@ -0,0 +1,36 @@ +step = $step; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries * $this->step; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php new file mode 100644 index 000000000..4fd73fedf --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php @@ -0,0 +1,25 @@ +errorCodes[$response->getReasonPhrase()]) ? true : null; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php new file mode 100644 index 000000000..3608f3584 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php @@ -0,0 +1,36 @@ +max = $maxRetries; + $this->next = $next; + } + + public function makesDecision() + { + return true; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries < $this->max ? null : false; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json new file mode 100644 index 000000000..91c122cb4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-backoff", + "description": "Guzzle backoff retry plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Backoff": "" } + }, + "target-dir": "Guzzle/Plugin/Backoff", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php new file mode 100644 index 000000000..7790f8844 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php @@ -0,0 +1,11 @@ + new DefaultCacheStorage($options)); + } elseif ($options instanceof CacheStorageInterface) { + $options = array('storage' => $options); + } elseif ($options) { + $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options))); + } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) { + // @codeCoverageIgnoreStart + throw new InvalidArgumentException('No cache was provided and Doctrine is not installed'); + // @codeCoverageIgnoreEnd + } + } + + $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false; + + // Add a cache storage if a cache adapter was provided + $this->storage = isset($options['storage']) + ? $options['storage'] + : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); + + if (!isset($options['can_cache'])) { + $this->canCache = new DefaultCanCacheStrategy(); + } else { + $this->canCache = is_callable($options['can_cache']) + ? new CallbackCanCacheStrategy($options['can_cache']) + : $options['can_cache']; + } + + // Use the provided revalidation strategy or the default + $this->revalidation = isset($options['revalidation']) + ? $options['revalidation'] + : new DefaultRevalidation($this->storage, $this->canCache); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -255), + 'request.sent' => array('onRequestSent', 255), + 'request.error' => array('onRequestError', 0), + 'request.exception' => array('onRequestException', 0), + ); + } + + /** + * Check if a response in cache will satisfy the request before sending + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + if (!$this->canCache->canCacheRequest($request)) { + switch ($request->getMethod()) { + case 'PURGE': + $this->purge($request); + $request->setResponse(new Response(200, array(), 'purged')); + break; + case 'PUT': + case 'POST': + case 'DELETE': + case 'PATCH': + if ($this->autoPurge) { + $this->purge($request); + } + } + return; + } + + if ($response = $this->storage->fetch($request)) { + $params = $request->getParams(); + $params['cache.lookup'] = true; + $response->setHeader( + 'Age', + time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now') + ); + // Validate that the response satisfies the request + if ($this->canResponseSatisfyRequest($request, $response)) { + if (!isset($params['cache.hit'])) { + $params['cache.hit'] = true; + } + $request->setResponse($response); + } + } + } + + /** + * If possible, store a response in cache after sending + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + + if ($request->getParams()->get('cache.hit') === null && + $this->canCache->canCacheRequest($request) && + $this->canCache->canCacheResponse($response) + ) { + $this->storage->cache($request, $response); + } + + $this->addResponseHeaders($request, $response); + } + + /** + * If possible, return a cache response on an error + * + * @param Event $event + */ + public function onRequestError(Event $event) + { + $request = $event['request']; + + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader( + 'Age', + time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now') + ); + + if ($this->canResponseSatisfyFailedRequest($request, $response)) { + $request->getParams()->set('cache.hit', 'error'); + $this->addResponseHeaders($request, $response); + $event['response'] = $response; + $event->stopPropagation(); + } + } + } + + /** + * If possible, set a cache response on a cURL exception + * + * @param Event $event + * + * @return null + */ + public function onRequestException(Event $event) + { + if (!$event['exception'] instanceof CurlException) { + return; + } + + $request = $event['request']; + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now')); + if (!$this->canResponseSatisfyFailedRequest($request, $response)) { + return; + } + $request->getParams()->set('cache.hit', 'error'); + $request->setResponse($response); + $this->addResponseHeaders($request, $response); + $event->stopPropagation(); + } + } + + /** + * Check if a cache response satisfies a request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyRequest(RequestInterface $request, Response $response) + { + $responseAge = $response->calculateAge(); + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + + // Check the request's max-age header against the age of the response + if ($reqc && $reqc->hasDirective('max-age') && + $responseAge > $reqc->getDirective('max-age')) { + return false; + } + + // Check the response's max-age header + if ($response->isFresh() === false) { + $maxStale = $reqc ? $reqc->getDirective('max-stale') : null; + if (null !== $maxStale) { + if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) { + return false; + } + } elseif ($resc && $resc->hasDirective('max-age') + && $responseAge > $resc->getDirective('max-age') + ) { + return false; + } + } + + if ($this->revalidation->shouldRevalidate($request, $response)) { + try { + return $this->revalidation->revalidate($request, $response); + } catch (CurlException $e) { + $request->getParams()->set('cache.hit', 'error'); + return $this->canResponseSatisfyFailedRequest($request, $response); + } + } + + return true; + } + + /** + * Check if a cache response satisfies a failed request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response) + { + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null; + $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null; + + if (!$requestStaleIfError && !$responseStaleIfError) { + return false; + } + + if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) { + return false; + } + + if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) { + return false; + } + + return true; + } + + /** + * Purge all cache entries for a given URL + * + * @param string $url URL to purge + */ + public function purge($url) + { + // BC compatibility with previous version that accepted a Request object + $url = $url instanceof RequestInterface ? $url->getUrl() : $url; + $this->storage->purge($url); + } + + /** + * Add the plugin's headers to a response + * + * @param RequestInterface $request Request + * @param Response $response Response to add headers to + */ + protected function addResponseHeaders(RequestInterface $request, Response $response) + { + $params = $request->getParams(); + $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache'; + if ($header = $response->getHeader('X-Cache-Lookup')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $lookup; + $response->setHeader('X-Cache-Lookup', array_unique($values)); + } else { + $response->setHeader('X-Cache-Lookup', $lookup); + } + + if ($params['cache.hit'] === true) { + $xcache = 'HIT from GuzzleCache'; + } elseif ($params['cache.hit'] == 'error') { + $xcache = 'HIT_ERROR from GuzzleCache'; + } else { + $xcache = 'MISS from GuzzleCache'; + } + + if ($header = $response->getHeader('X-Cache')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $xcache; + $response->setHeader('X-Cache', array_unique($values)); + } else { + $response->setHeader('X-Cache', $xcache); + } + + if ($response->isFresh() === false) { + $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); + if ($params['cache.hit'] === 'error') { + $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php new file mode 100644 index 000000000..f3d915458 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php @@ -0,0 +1,43 @@ +requestCallback = $requestCallback; + $this->responseCallback = $responseCallback; + } + + public function canCacheRequest(RequestInterface $request) + { + return $this->requestCallback + ? call_user_func($this->requestCallback, $request) + : parent::canCacheRequest($request); + } + + public function canCacheResponse(Response $response) + { + return $this->responseCallback + ? call_user_func($this->responseCallback, $response) + : parent::canCacheResponse($response); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php new file mode 100644 index 000000000..6e01a8e74 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php @@ -0,0 +1,30 @@ +getParams()->get(self::CACHE_KEY); + + if (!$key) { + + $cloned = clone $request; + $cloned->removeHeader('Cache-Control'); + + // Check to see how and if the key should be filtered + foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) { + $pieces = array_map('trim', explode('=', $part)); + if (isset($pieces[1])) { + foreach (array_map('trim', explode(',', $pieces[1])) as $remove) { + if ($pieces[0] == 'header') { + $cloned->removeHeader($remove); + } elseif ($pieces[0] == 'query') { + $cloned->getQuery()->remove($remove); + } + } + } + } + + $raw = (string) $cloned; + $key = 'GZ' . md5($raw); + $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw); + } + + return $key; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php new file mode 100644 index 000000000..26d7a8b27 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php @@ -0,0 +1,266 @@ +cache = CacheAdapterFactory::fromCache($cache); + $this->defaultTtl = $defaultTtl; + $this->keyPrefix = $keyPrefix; + } + + public function cache(RequestInterface $request, Response $response) + { + $currentTime = time(); + + $overrideTtl = $request->getParams()->get('cache.override_ttl'); + if ($overrideTtl) { + $ttl = $overrideTtl; + } else { + $maxAge = $response->getMaxAge(); + if ($maxAge !== null) { + $ttl = $maxAge; + } else { + $ttl = $this->defaultTtl; + } + } + + if ($cacheControl = $response->getHeader('Cache-Control')) { + $stale = $cacheControl->getDirective('stale-if-error'); + if ($stale === true) { + $ttl += $ttl; + } else if (is_numeric($stale)) { + $ttl += $stale; + } + } + + // Determine which manifest key should be used + $key = $this->getCacheKey($request); + $persistedRequest = $this->persistHeaders($request); + $entries = array(); + + if ($manifest = $this->cache->fetch($key)) { + // Determine which cache entries should still be in the cache + $vary = $response->getVary(); + foreach (unserialize($manifest) as $entry) { + // Check if the entry is expired + if ($entry[4] < $currentTime) { + continue; + } + $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : ''; + if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) { + $entries[] = $entry; + } + } + } + + // Persist the response body if needed + $bodyDigest = null; + if ($response->getBody() && $response->getBody()->getContentLength() > 0) { + $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody()); + $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl); + } + + array_unshift($entries, array( + $persistedRequest, + $this->persistHeaders($response), + $response->getStatusCode(), + $bodyDigest, + $currentTime + $ttl + )); + + $this->cache->save($key, serialize($entries)); + } + + public function delete(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if ($entries = $this->cache->fetch($key)) { + // Delete each cached body + foreach (unserialize($entries) as $entry) { + if ($entry[3]) { + $this->cache->delete($entry[3]); + } + } + $this->cache->delete($key); + } + } + + public function purge($url) + { + foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) { + $this->delete(new Request($method, $url)); + } + } + + public function fetch(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if (!($entries = $this->cache->fetch($key))) { + return null; + } + + $match = null; + $headers = $this->persistHeaders($request); + $entries = unserialize($entries); + foreach ($entries as $index => $entry) { + if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) { + $match = $entry; + break; + } + } + + if (!$match) { + return null; + } + + // Ensure that the response is not expired + $response = null; + if ($match[4] < time()) { + $response = -1; + } else { + $response = new Response($match[2], $match[1]); + if ($match[3]) { + if ($body = $this->cache->fetch($match[3])) { + $response->setBody($body); + } else { + // The response is not valid because the body was somehow deleted + $response = -1; + } + } + } + + if ($response === -1) { + // Remove the entry from the metadata and update the cache + unset($entries[$index]); + if ($entries) { + $this->cache->save($key, serialize($entries)); + } else { + $this->cache->delete($key); + } + return null; + } + + return $response; + } + + /** + * Hash a request URL into a string that returns cache metadata + * + * @param RequestInterface $request + * + * @return string + */ + protected function getCacheKey(RequestInterface $request) + { + // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth) + if ($filter = $request->getParams()->get('cache.key_filter')) { + $url = $request->getUrl(true); + foreach (explode(',', $filter) as $remove) { + $url->getQuery()->remove(trim($remove)); + } + } else { + $url = $request->getUrl(); + } + + return $this->keyPrefix . md5($request->getMethod() . ' ' . $url); + } + + /** + * Create a cache key for a response's body + * + * @param string $url URL of the entry + * @param EntityBodyInterface $body Response body + * + * @return string + */ + protected function getBodyKey($url, EntityBodyInterface $body) + { + return $this->keyPrefix . md5($url) . $body->getContentMd5(); + } + + /** + * Determines whether two Request HTTP header sets are non-varying + * + * @param string $vary Response vary header + * @param array $r1 HTTP header array + * @param array $r2 HTTP header array + * + * @return bool + */ + private function requestsMatch($vary, $r1, $r2) + { + if ($vary) { + foreach (explode(',', $vary) as $header) { + $key = trim(strtolower($header)); + $v1 = isset($r1[$key]) ? $r1[$key] : null; + $v2 = isset($r2[$key]) ? $r2[$key] : null; + if ($v1 !== $v2) { + return false; + } + } + } + + return true; + } + + /** + * Creates an array of cacheable and normalized message headers + * + * @param MessageInterface $message + * + * @return array + */ + private function persistHeaders(MessageInterface $message) + { + // Headers are excluded from the caching (see RFC 2616:13.5.1) + static $noCache = array( + 'age' => true, + 'connection' => true, + 'keep-alive' => true, + 'proxy-authenticate' => true, + 'proxy-authorization' => true, + 'te' => true, + 'trailers' => true, + 'transfer-encoding' => true, + 'upgrade' => true, + 'set-cookie' => true, + 'set-cookie2' => true + ); + + // Clone the response to not destroy any necessary headers when caching + $headers = $message->getHeaders()->getAll(); + $headers = array_diff_key($headers, $noCache); + // Cast the headers to a string + $headers = array_map(function ($h) { return (string) $h; }, $headers); + + return $headers; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php new file mode 100644 index 000000000..3ca1fbf19 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php @@ -0,0 +1,32 @@ +getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) { + return false; + } + + // Never cache requests when using no-store + if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return true; + } + + public function canCacheResponse(Response $response) + { + return $response->isSuccessful() && $response->canCache(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php new file mode 100644 index 000000000..af33234ee --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php @@ -0,0 +1,174 @@ +storage = $cache; + $this->canCache = $canCache ?: new DefaultCanCacheStrategy(); + } + + public function revalidate(RequestInterface $request, Response $response) + { + try { + $revalidate = $this->createRevalidationRequest($request, $response); + $validateResponse = $revalidate->send(); + if ($validateResponse->getStatusCode() == 200) { + return $this->handle200Response($request, $validateResponse); + } elseif ($validateResponse->getStatusCode() == 304) { + return $this->handle304Response($request, $validateResponse, $response); + } + } catch (BadResponseException $e) { + $this->handleBadResponse($e); + } + + // Other exceptions encountered in the revalidation request are ignored + // in hopes that sending a request to the origin server will fix it + return false; + } + + public function shouldRevalidate(RequestInterface $request, Response $response) + { + if ($request->getMethod() != RequestInterface::GET) { + return false; + } + + $reqCache = $request->getHeader('Cache-Control'); + $resCache = $response->getHeader('Cache-Control'); + + $revalidate = $request->getHeader('Pragma') == 'no-cache' || + ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) || + ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate'))); + + // Use the strong ETag validator if available and the response contains no Cache-Control directive + if (!$revalidate && !$resCache && $response->hasHeader('ETag')) { + $revalidate = true; + } + + return $revalidate; + } + + /** + * Handles a bad response when attempting to revalidate + * + * @param BadResponseException $e Exception encountered + * + * @throws BadResponseException + */ + protected function handleBadResponse(BadResponseException $e) + { + // 404 errors mean the resource no longer exists, so remove from + // cache, and prevent an additional request by throwing the exception + if ($e->getResponse()->getStatusCode() == 404) { + $this->storage->delete($e->getRequest()); + throw $e; + } + } + + /** + * Creates a request to use for revalidation + * + * @param RequestInterface $request Request + * @param Response $response Response to revalidate + * + * @return RequestInterface returns a revalidation request + */ + protected function createRevalidationRequest(RequestInterface $request, Response $response) + { + $revalidate = clone $request; + $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control'); + + if ($response->getLastModified()) { + $revalidate->setHeader('If-Modified-Since', $response->getLastModified()); + } + + if ($response->getEtag()) { + $revalidate->setHeader('If-None-Match', $response->getEtag()); + } + + // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations + $dispatcher = $revalidate->getEventDispatcher(); + foreach ($dispatcher->getListeners() as $eventName => $listeners) { + foreach ($listeners as $listener) { + if (is_array($listener) && $listener[0] instanceof CachePlugin) { + $dispatcher->removeListener($eventName, $listener); + } + } + } + + return $revalidate; + } + + /** + * Handles a 200 response response from revalidating. The server does not support validation, so use this response. + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle200Response(RequestInterface $request, Response $validateResponse) + { + $request->setResponse($validateResponse); + if ($this->canCache->canCacheResponse($validateResponse)) { + $this->storage->cache($request, $validateResponse); + } + + return false; + } + + /** + * Handle a 304 response and ensure that it is still valid + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * @param Response $response Original cached response + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response) + { + static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'); + + // Make sure that this response has the same ETag + if ($validateResponse->getEtag() != $response->getEtag()) { + return false; + } + + // Replace cached headers with any of these headers from the + // origin server that might be more up to date + $modified = false; + foreach ($replaceHeaders as $name) { + if ($validateResponse->hasHeader($name)) { + $modified = true; + $response->setHeader($name, $validateResponse->getHeader($name)); + } + } + + // Store the updated response in cache + if ($modified && $this->canCache->canCacheResponse($response)) { + $this->storage->cache($request, $response); + } + + return true; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php new file mode 100644 index 000000000..88b86f3ca --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php @@ -0,0 +1,19 @@ +=5.3.2", + "guzzle/http": "self.version", + "guzzle/cache": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cache": "" } + }, + "target-dir": "Guzzle/Plugin/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php new file mode 100644 index 000000000..5218e5f0e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php @@ -0,0 +1,538 @@ + '', + 'value' => '', + 'domain' => '', + 'path' => '/', + 'expires' => null, + 'max_age' => 0, + 'comment' => null, + 'comment_url' => null, + 'port' => array(), + 'version' => null, + 'secure' => false, + 'discard' => false, + 'http_only' => false + ); + + $this->data = array_merge($defaults, $data); + // Extract the expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the expires date + $this->setExpires(time() + (int) $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires(strtotime($this->getExpires())); + } + } + + /** + * Get the cookie as an array + * + * @return array + */ + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + * + * @return Cookie + */ + public function setName($name) + { + return $this->setData('name', $name); + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + * + * @return Cookie + */ + public function setValue($value) + { + return $this->setData('value', $value); + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + * + * @return Cookie + */ + public function setDomain($domain) + { + return $this->setData('domain', $domain); + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + * + * @return Cookie + */ + public function setPath($path) + { + return $this->setData('path', $path); + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['max_age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + * + * @return Cookie + */ + public function setMaxAge($maxAge) + { + return $this->setData('max_age', $maxAge); + } + + /** + * The UNIX timestamp when the cookie expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + * + * @return Cookie + */ + public function setExpires($timestamp) + { + return $this->setData('expires', $timestamp); + } + + /** + * Version of the cookie specification. RFC 2965 is 1 + * + * @return mixed + */ + public function getVersion() + { + return $this->data['version']; + } + + /** + * Set the cookie version + * + * @param string|int $version Version to set + * + * @return Cookie + */ + public function setVersion($version) + { + return $this->setData('version', $version); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + * + * @return Cookie + */ + public function setSecure($secure) + { + return $this->setData('secure', (bool) $secure); + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + * + * @return Cookie + */ + public function setDiscard($discard) + { + return $this->setData('discard', $discard); + } + + /** + * Get the comment + * + * @return string|null + */ + public function getComment() + { + return $this->data['comment']; + } + + /** + * Set the comment of the cookie + * + * @param string $comment Cookie comment + * + * @return Cookie + */ + public function setComment($comment) + { + return $this->setData('comment', $comment); + } + + /** + * Get the comment URL of the cookie + * + * @return string|null + */ + public function getCommentUrl() + { + return $this->data['comment_url']; + } + + /** + * Set the comment URL of the cookie + * + * @param string $commentUrl Cookie comment URL for more information + * + * @return Cookie + */ + public function setCommentUrl($commentUrl) + { + return $this->setData('comment_url', $commentUrl); + } + + /** + * Get an array of acceptable ports this cookie can be used with + * + * @return array + */ + public function getPorts() + { + return $this->data['port']; + } + + /** + * Set a list of acceptable ports this cookie can be used with + * + * @param array $ports Array of acceptable ports + * + * @return Cookie + */ + public function setPorts(array $ports) + { + return $this->setData('port', $ports); + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['http_only']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + * + * @return Cookie + */ + public function setHttpOnly($httpOnly) + { + return $this->setData('http_only', $httpOnly); + } + + /** + * Get an array of extra cookie data + * + * @return array + */ + public function getAttributes() + { + return $this->data['data']; + } + + /** + * Get a specific data point from the extra cookie data + * + * @param string $name Name of the data point to retrieve + * + * @return null|string + */ + public function getAttribute($name) + { + return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null; + } + + /** + * Set a cookie data attribute + * + * @param string $name Name of the attribute to set + * @param string $value Value to set + * + * @return Cookie + */ + public function setAttribute($name, $value) + { + $this->data['data'][$name] = $value; + + return $this; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4 + // A request-path path-matches a given cookie-path if at least one of + // the following conditions holds: + + // o The cookie-path and the request-path are identical. + if ($path == $this->getPath()) { + return true; + } + + $pos = stripos($path, $this->getPath()); + if ($pos === 0) { + // o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + if (substr($this->getPath(), -1, 1) === "/") { + return true; + } + + // o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- + // path is a %x2F ("/") character. + if (substr($path, strlen($this->getPath()), 1) === "/") { + return true; + } + } + + return false; + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain); + } + + /** + * Check if the cookie is compatible with a specific port + * + * @param int $port Port to check + * + * @return bool + */ + public function matchesPort($port) + { + return count($this->getPorts()) == 0 || in_array($port, $this->getPorts()); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (strpbrk($name, self::getInvalidCharacters()) !== false) { + return 'The cookie name must not contain invalid characters: ' . $name; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name in a private network + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } + + /** + * Set a value and return the cookie object + * + * @param string $key Key to set + * @param string $value Value to set + * + * @return Cookie + */ + private function setData($key, $value) + { + $this->data[$key] = $value; + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php new file mode 100644 index 000000000..6b675039f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php @@ -0,0 +1,237 @@ +strictMode = $strictMode; + } + + /** + * Enable or disable strict mode on the cookie jar + * + * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them. + * + * @return self + */ + public function setStrictMode($strictMode) + { + $this->strictMode = $strictMode; + } + + public function remove($domain = null, $path = null, $name = null) + { + $cookies = $this->all($domain, $path, $name, false, false); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) { + return !in_array($cookie, $cookies, true); + }); + + return $this; + } + + public function removeTemporary() + { + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + }); + + return $this; + } + + public function removeExpired() + { + $currentTime = time(); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) { + return !$cookie->getExpires() || $currentTime < $cookie->getExpires(); + }); + + return $this; + } + + public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true) + { + return array_values(array_filter($this->cookies, function (Cookie $cookie) use ( + $domain, + $path, + $name, + $skipDiscardable, + $skipExpired + ) { + return false === (($name && $cookie->getName() != $name) || + ($skipExpired && $cookie->isExpired()) || + ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) || + ($path && !$cookie->matchesPath($path)) || + ($domain && !$cookie->matchesDomain($domain))); + })); + } + + public function add(Cookie $cookie) + { + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new InvalidCookieException($result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, domain, port and name are identical + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getPorts() != $cookie->getPorts() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + /** + * Serializes the cookie cookieJar + * + * @return string + */ + public function serialize() + { + // Only serialize long term cookies and unexpired cookies + return json_encode(array_map(function (Cookie $cookie) { + return $cookie->toArray(); + }, $this->all(null, null, null, true, true))); + } + + /** + * Unserializes the cookie cookieJar + */ + public function unserialize($data) + { + $data = json_decode($data, true); + if (empty($data)) { + $this->cookies = array(); + } else { + $this->cookies = array_map(function (array $cookie) { + return new Cookie($cookie); + }, $data); + } + } + + /** + * Returns the total number of stored cookies + * + * @return int + */ + public function count() + { + return count($this->cookies); + } + + /** + * Returns an iterator + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->cookies); + } + + public function addCookiesFromResponse(Response $response, RequestInterface $request = null) + { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { + $parser = ParserRegistry::getInstance()->getParser('cookie'); + foreach ($cookieHeader as $cookie) { + if ($parsed = $request + ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath()) + : $parser->parseCookie($cookie) + ) { + // Break up cookie v2 into multiple cookies + foreach ($parsed['cookies'] as $key => $value) { + $row = $parsed; + $row['name'] = $key; + $row['value'] = $value; + unset($row['cookies']); + $this->add(new Cookie($row)); + } + } + } + } + } + + public function getMatchingCookies(RequestInterface $request) + { + // Find cookies that match this request + $cookies = $this->all($request->getHost(), $request->getPath()); + // Remove ineligible cookies + foreach ($cookies as $index => $cookie) { + if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) { + unset($cookies[$index]); + } + }; + + return $cookies; + } + + /** + * If a cookie already exists and the server asks to set it again with a null value, the + * cookie must be deleted. + * + * @param \Guzzle\Plugin\Cookie\Cookie $cookie + */ + private function removeCookieIfEmpty(Cookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName()); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php new file mode 100644 index 000000000..7faa7d21f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php @@ -0,0 +1,85 @@ +filename = $cookieFile; + $this->load(); + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->persist(); + } + + /** + * Save the contents of the data array to the file + * + * @throws RuntimeException if the file cannot be found or created + */ + protected function persist() + { + if (false === file_put_contents($this->filename, $this->serialize())) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load the contents of the json formatted file into the data array and discard any unsaved state + */ + protected function load() + { + $json = file_get_contents($this->filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + + $this->unserialize($json); + $this->cookies = $this->cookies ?: array(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php new file mode 100644 index 000000000..df3210ee1 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php @@ -0,0 +1,70 @@ +cookieJar = $cookieJar ?: new ArrayCookieJar(); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', 125), + 'request.sent' => array('onRequestSent', 125) + ); + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Add cookies before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + if (!$request->getParams()->get('cookies.disable')) { + $request->removeHeader('Cookie'); + // Find cookies that match this request + foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) { + $request->addCookie($cookie->getName(), $cookie->getValue()); + } + } + } + + /** + * Extract cookies from a sent request + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php new file mode 100644 index 000000000..b1fa6fd89 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cookie": "" } + }, + "target-dir": "Guzzle/Plugin/Cookie", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php new file mode 100644 index 000000000..610e60cad --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php @@ -0,0 +1,46 @@ +getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest'); + */ +class CurlAuthPlugin implements EventSubscriberInterface +{ + private $username; + private $password; + private $scheme; + + /** + * @param string $username HTTP basic auth username + * @param string $password Password + * @param int $scheme Curl auth scheme + */ + public function __construct($username, $password, $scheme=CURLAUTH_BASIC) + { + Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');"); + $this->username = $username; + $this->password = $password; + $this->scheme = $scheme; + } + + public static function getSubscribedEvents() + { + return array('client.create_request' => array('onRequestCreate', 255)); + } + + /** + * Add basic auth + * + * @param Event $event + */ + public function onRequestCreate(Event $event) + { + $event['request']->setAuth($this->username, $this->password, $this->scheme); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json new file mode 100644 index 000000000..edc8b24e5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-curlauth", + "description": "Guzzle cURL authorization plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "curl", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" } + }, + "target-dir": "Guzzle/Plugin/CurlAuth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php new file mode 100644 index 000000000..5dce8bd6c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php @@ -0,0 +1,22 @@ + array('onCommandBeforeSend', -1)); + } + + /** + * Adds a listener to requests before they sent from a command + * + * @param Event $event Event emitted + */ + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + if ($operation = $command->getOperation()) { + if ($operation->getErrorResponses()) { + $request = $command->getRequest(); + $request->getEventDispatcher() + ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation)); + } + } + } + + /** + * @param RequestInterface $request Request that received an error + * @param CommandInterface $command Command that created the request + * @param Operation $operation Operation that defines the request and errors + * + * @return \Closure Returns a closure + * @throws ErrorResponseException + */ + protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation) + { + return function (Event $event) use ($request, $command, $operation) { + $response = $event['response']; + foreach ($operation->getErrorResponses() as $error) { + if (!isset($error['class'])) { + continue; + } + if (isset($error['code']) && $response->getStatusCode() != $error['code']) { + continue; + } + if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) { + continue; + } + $className = $error['class']; + $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface'; + if (!class_exists($className)) { + throw new ErrorResponseException("{$className} does not exist"); + } elseif (!(in_array($errorClassInterface, class_implements($className)))) { + throw new ErrorResponseException("{$className} must implement {$errorClassInterface}"); + } + throw $className::fromCommand($command, $response); + } + }; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php new file mode 100644 index 000000000..1d89e40e7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/service": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" } + }, + "target-dir": "Guzzle/Plugin/ErrorResponse", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php new file mode 100644 index 000000000..7375e892b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php @@ -0,0 +1,163 @@ + array('onRequestSent', 9999)); + } + + /** + * Convert to a string that contains all request and response headers + * + * @return string + */ + public function __toString() + { + $lines = array(); + foreach ($this->transactions as $entry) { + $response = isset($entry['response']) ? $entry['response'] : ''; + $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n"; + } + + return implode("\n", $lines); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * @param Response $response Response of the request + * + * @return HistoryPlugin + */ + public function add(RequestInterface $request, Response $response = null) + { + if (!$response && $request->getResponse()) { + $response = $request->getResponse(); + } + + $this->transactions[] = array('request' => $request, 'response' => $response); + if (count($this->transactions) > $this->getlimit()) { + array_shift($this->transactions); + } + + return $this; + } + + /** + * Set the max number of requests to store + * + * @param int $limit Limit + * + * @return HistoryPlugin + */ + public function setLimit($limit) + { + $this->limit = (int) $limit; + + return $this; + } + + /** + * Get the request limit + * + * @return int + */ + public function getLimit() + { + return $this->limit; + } + + /** + * Get all of the raw transactions in the form of an array of associative arrays containing + * 'request' and 'response' keys. + * + * @return array + */ + public function getAll() + { + return $this->transactions; + } + + /** + * Get the requests in the history + * + * @return \ArrayIterator + */ + public function getIterator() + { + // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll()) + return new \ArrayIterator(array_map(function ($entry) { + $entry['request']->getParams()->set('actual_response', $entry['response']); + return $entry['request']; + }, $this->transactions)); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->transactions); + } + + /** + * Get the last request sent + * + * @return RequestInterface + */ + public function getLastRequest() + { + $last = end($this->transactions); + + return $last['request']; + } + + /** + * Get the last response in the history + * + * @return Response|null + */ + public function getLastResponse() + { + $last = end($this->transactions); + + return isset($last['response']) ? $last['response'] : null; + } + + /** + * Clears the history + * + * @return HistoryPlugin + */ + public function clear() + { + $this->transactions = array(); + + return $this; + } + + public function onRequestSent(Event $event) + { + $this->add($event['request'], $event['response']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json new file mode 100644 index 000000000..ba0bf2c4d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-history", + "description": "Guzzle history plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\History": "" } + }, + "target-dir": "Guzzle/Plugin/History", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php new file mode 100644 index 000000000..cabdea854 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php @@ -0,0 +1,161 @@ +logAdapter = $logAdapter; + $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter); + $this->wireBodies = $wireBodies; + } + + /** + * Get a log plugin that outputs full request, response, and curl error information to stderr + * + * @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable + * @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available + * + * @return self + */ + public static function getDebugPlugin($wireBodies = true, $stream = null) + { + if ($stream === null) { + if (defined('STDERR')) { + $stream = STDERR; + } else { + $stream = fopen('php://output', 'w'); + } + } + + return new self(new ClosureLogAdapter(function ($m) use ($stream) { + fwrite($stream, $m . PHP_EOL); + }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies); + } + + public static function getSubscribedEvents() + { + return array( + 'curl.callback.write' => array('onCurlWrite', 255), + 'curl.callback.read' => array('onCurlRead', 255), + 'request.before_send' => array('onRequestBeforeSend', 255), + 'request.sent' => array('onRequestSent', 255) + ); + } + + /** + * Event triggered when curl data is read from a request + * + * @param Event $event + */ + public function onCurlRead(Event $event) + { + // Stream the request body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('request_wire')) { + $wire->write($event['read']); + } + } + + /** + * Event triggered when curl data is written to a response + * + * @param Event $event + */ + public function onCurlWrite(Event $event) + { + // Stream the response body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('response_wire')) { + $wire->write($event['write']); + } + } + + /** + * Called before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + if ($this->wireBodies) { + $request = $event['request']; + // Ensure that curl IO events are emitted + $request->getCurlOptions()->set('emit_io', true); + // We need to make special handling for content wiring and non-repeatable streams. + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable()) + ) { + // The body of the request cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('request_wire', EntityBody::factory()); + } + if (!$request->getResponseBody()->isRepeatable()) { + // The body of the response cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('response_wire', EntityBody::factory()); + } + } + } + + /** + * Triggers the actual log write when a request completes + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $handle = $event['handle']; + + if ($wire = $request->getParams()->get('request_wire')) { + $request = clone $request; + $request->setBody($wire); + } + + if ($wire = $request->getParams()->get('response_wire')) { + $response = clone $response; + $response->setBody($wire); + } + + // Send the log message to the adapter, adding a category and host + $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG; + $message = $this->formatter->format($request, $response, $handle); + $this->logAdapter->log($message, $priority, array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json new file mode 100644 index 000000000..130e6da0a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-log", + "description": "Guzzle log plugin for over the wire logging", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "log", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Log": "" } + }, + "target-dir": "Guzzle/Plugin/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php new file mode 100644 index 000000000..851242433 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php @@ -0,0 +1,57 @@ +contentMd5Param = $contentMd5Param; + $this->validateMd5Param = $validateMd5Param; + } + + public static function getSubscribedEvents() + { + return array('command.before_send' => array('onCommandBeforeSend', -255)); + } + + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + $request = $command->getRequest(); + + // Only add an MD5 is there is a MD5 option on the operation and it has a payload + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && $command->getOperation()->hasParam($this->contentMd5Param)) { + // Check if an MD5 checksum value should be passed along to the request + if ($command[$this->contentMd5Param] === true) { + if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) { + $request->setHeader('Content-MD5', $md5); + } + } + } + + // Check if MD5 validation should be used with the response + if ($command[$this->validateMd5Param] === true) { + $request->addSubscriber(new Md5ValidatorPlugin(true, false)); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php new file mode 100644 index 000000000..5d7a3785e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php @@ -0,0 +1,88 @@ +contentLengthCutoff = $contentLengthCutoff; + $this->contentEncoded = $contentEncoded; + } + + public static function getSubscribedEvents() + { + return array('request.complete' => array('onRequestComplete', 255)); + } + + /** + * {@inheritdoc} + * @throws UnexpectedValueException + */ + public function onRequestComplete(Event $event) + { + $response = $event['response']; + + if (!$contentMd5 = $response->getContentMd5()) { + return; + } + + $contentEncoding = $response->getContentEncoding(); + if ($contentEncoding && !$this->contentEncoded) { + return false; + } + + // Make sure that the size of the request is under the cutoff size + if ($this->contentLengthCutoff) { + $size = $response->getContentLength() ?: $response->getBody()->getSize(); + if (!$size || $size > $this->contentLengthCutoff) { + return; + } + } + + if (!$contentEncoding) { + $hash = $response->getBody()->getContentMd5(); + } elseif ($contentEncoding == 'gzip') { + $response->getBody()->compress('zlib.deflate'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } elseif ($contentEncoding == 'compress') { + $response->getBody()->compress('bzip2.compress'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } else { + return; + } + + if ($contentMd5 !== $hash) { + throw new UnexpectedValueException( + "The response entity body may have been modified over the wire. The Content-MD5 " + . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})." + ); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json new file mode 100644 index 000000000..0602d0609 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-md5", + "description": "Guzzle MD5 plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Md5": "" } + }, + "target-dir": "Guzzle/Plugin/Md5", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php new file mode 100644 index 000000000..2440578cf --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php @@ -0,0 +1,245 @@ +readBodies = $readBodies; + $this->temporary = $temporary; + if ($items) { + foreach ($items as $item) { + if ($item instanceof \Exception) { + $this->addException($item); + } else { + $this->addResponse($item); + } + } + } + } + + public static function getSubscribedEvents() + { + // Use a number lower than the CachePlugin + return array('request.before_send' => array('onRequestBeforeSend', -999)); + } + + public static function getAllEvents() + { + return array('mock.request'); + } + + /** + * Get a mock response from a file + * + * @param string $path File to retrieve a mock response from + * + * @return Response + * @throws InvalidArgumentException if the file is not found + */ + public static function getMockFile($path) + { + if (!file_exists($path)) { + throw new InvalidArgumentException('Unable to open mock file: ' . $path); + } + + return Response::fromMessage(file_get_contents($path)); + } + + /** + * Set whether or not to consume the entity body of a request when a mock + * response is used + * + * @param bool $readBodies Set to true to read and consume entity bodies + * + * @return self + */ + public function readBodies($readBodies) + { + $this->readBodies = $readBodies; + + return $this; + } + + /** + * Returns the number of remaining mock responses + * + * @return int + */ + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|Response $response Response object or path to response file + * + * @return MockPlugin + * @throws InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (!($response instanceof Response)) { + if (!is_string($response)) { + throw new InvalidArgumentException('Invalid response'); + } + $response = self::getMockFile($response); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Add an exception to the end of the queue + * + * @param CurlException $e Exception to throw when the request is executed + * + * @return MockPlugin + */ + public function addException(CurlException $e) + { + $this->queue[] = $e; + + return $this; + } + + /** + * Clear the queue + * + * @return MockPlugin + */ + public function clearQueue() + { + $this->queue = array(); + + return $this; + } + + /** + * Returns an array of mock responses remaining in the queue + * + * @return array + */ + public function getQueue() + { + return $this->queue; + } + + /** + * Check if this is a temporary plugin + * + * @return bool + */ + public function isTemporary() + { + return $this->temporary; + } + + /** + * Get a response from the front of the list and add it to a request + * + * @param RequestInterface $request Request to mock + * + * @return self + * @throws CurlException When request.send is called and an exception is queued + */ + public function dequeue(RequestInterface $request) + { + $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request)); + + $item = array_shift($this->queue); + if ($item instanceof Response) { + if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) { + $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) { + while ($data = $event['request']->getBody()->read(8096)); + // Remove the listener after one-time use + $event['request']->getEventDispatcher()->removeListener('request.sent', $f); + }); + } + $request->setResponse($item); + } elseif ($item instanceof CurlException) { + // Emulates exceptions encountered while transferring requests + $item->setRequest($request); + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item)); + // Only throw if the exception wasn't handled + if ($state == RequestInterface::STATE_ERROR) { + throw $item; + } + } + + return $this; + } + + /** + * Clear the array of received requests + */ + public function flush() + { + $this->received = array(); + } + + /** + * Get an array of requests that were mocked by this plugin + * + * @return array + */ + public function getReceivedRequests() + { + return $this->received; + } + + /** + * Called when a request is about to be sent + * + * @param Event $event + * @throws \OutOfBoundsException When queue is empty + */ + public function onRequestBeforeSend(Event $event) + { + if (!$this->queue) { + throw new \OutOfBoundsException('Mock queue is empty'); + } + + $request = $event['request']; + $this->received[] = $request; + // Detach the filter from the client so it's a one-time use + if ($this->temporary && count($this->queue) == 1 && $request->getClient()) { + $request->getClient()->getEventDispatcher()->removeSubscriber($this); + } + $this->dequeue($request); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json new file mode 100644 index 000000000..f8201e31f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-mock", + "description": "Guzzle Mock plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["mock", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Mock": "" } + }, + "target-dir": "Guzzle/Plugin/Mock", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php new file mode 100644 index 000000000..95e0c3e4a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php @@ -0,0 +1,306 @@ +config = Collection::fromConfig($config, array( + 'version' => '1.0', + 'request_method' => self::REQUEST_METHOD_HEADER, + 'consumer_key' => 'anonymous', + 'consumer_secret' => 'anonymous', + 'signature_method' => 'HMAC-SHA1', + 'signature_callback' => function($stringToSign, $key) { + return hash_hmac('sha1', $stringToSign, $key, true); + } + ), array( + 'signature_method', 'signature_callback', 'version', + 'consumer_key', 'consumer_secret' + )); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -1000) + ); + } + + /** + * Request before-send event handler + * + * @param Event $event Event received + * @return array + * @throws \InvalidArgumentException + */ + public function onRequestBeforeSend(Event $event) + { + $timestamp = $this->getTimestamp($event); + $request = $event['request']; + $nonce = $this->generateNonce($request); + $authorizationParams = $this->getOauthParams($timestamp, $nonce); + $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce); + + switch ($this->config['request_method']) { + case self::REQUEST_METHOD_HEADER: + $request->setHeader( + 'Authorization', + $this->buildAuthorizationHeader($authorizationParams) + ); + break; + case self::REQUEST_METHOD_QUERY: + foreach ($authorizationParams as $key => $value) { + $request->getQuery()->set($key, $value); + } + break; + default: + throw new \InvalidArgumentException(sprintf( + 'Invalid consumer method "%s"', + $this->config['request_method'] + )); + } + + return $authorizationParams; + } + + /** + * Builds the Authorization header for a request + * + * @param array $authorizationParams Associative array of authorization parameters + * + * @return string + */ + private function buildAuthorizationHeader($authorizationParams) + { + $authorizationString = 'OAuth '; + foreach ($authorizationParams as $key => $val) { + if ($val) { + $authorizationString .= $key . '="' . urlencode($val) . '", '; + } + } + + return substr($authorizationString, 0, -2); + } + + /** + * Calculate signature for request + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getSignature(RequestInterface $request, $timestamp, $nonce) + { + $string = $this->getStringToSign($request, $timestamp, $nonce); + $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']); + + return base64_encode(call_user_func($this->config['signature_callback'], $string, $key)); + } + + /** + * Calculate string to sign + * + * @param RequestInterface $request Request to generate a signature for + * @param int $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getStringToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getParamsToSign($request, $timestamp, $nonce); + + // Convert booleans to strings. + $params = $this->prepareParameters($params); + + // Build signing string from combined params + $parameterString = clone $request->getQuery(); + $parameterString->replace($params); + + $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null); + + return strtoupper($request->getMethod()) . '&' + . rawurlencode($url) . '&' + . rawurlencode((string) $parameterString); + } + + /** + * Get the oauth parameters as named by the oauth spec + * + * @param $timestamp + * @param $nonce + * @return Collection + */ + protected function getOauthParams($timestamp, $nonce) + { + $params = new Collection(array( + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_nonce' => $nonce, + 'oauth_signature_method' => $this->config['signature_method'], + 'oauth_timestamp' => $timestamp, + )); + + // Optional parameters should not be set if they have not been set in the config as + // the parameter may be considered invalid by the Oauth service. + $optionalParams = array( + 'callback' => 'oauth_callback', + 'token' => 'oauth_token', + 'verifier' => 'oauth_verifier', + 'version' => 'oauth_version' + ); + + foreach ($optionalParams as $optionName => $oauthName) { + if (isset($this->config[$optionName]) == true) { + $params[$oauthName] = $this->config[$optionName]; + } + } + + return $params; + } + + /** + * Get all of the parameters required to sign a request including: + * * The oauth params + * * The request GET params + * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded) + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return array + */ + public function getParamsToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getOauthParams($timestamp, $nonce); + + // Add query string parameters + $params->merge($request->getQuery()); + + // Add POST fields to signing string if required + if ($this->shouldPostFieldsBeSigned($request)) + { + $params->merge($request->getPostFields()); + } + + // Sort params + $params = $params->toArray(); + uksort($params, 'strcmp'); + + return $params; + } + + /** + * Decide whether the post fields should be added to the base string that Oauth signs. + * This implementation is correct. Non-conformant APIs may require that this method be + * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type + * is 'application/x-www-form-urlencoded' + * + * @param $request + * @return bool Whether the post fields should be signed or not + */ + public function shouldPostFieldsBeSigned($request) + { + if (!$this->config->get('disable_post_params') && + $request instanceof EntityEnclosingRequestInterface && + false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) + { + return true; + } + + return false; + } + + /** + * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same + * exact timestamp to use separate nonce's. + * + * @param RequestInterface $request Request to generate a nonce for + * + * @return string + */ + public function generateNonce(RequestInterface $request) + { + return sha1(uniqid('', true) . $request->getUrl()); + } + + /** + * Gets timestamp from event or create new timestamp + * + * @param Event $event Event containing contextual information + * + * @return int + */ + public function getTimestamp(Event $event) + { + return $event['timestamp'] ?: time(); + } + + /** + * Convert booleans to strings, removed unset parameters, and sorts the array + * + * @param array $data Data array + * + * @return array + */ + protected function prepareParameters($data) + { + ksort($data); + foreach ($data as $key => &$value) { + switch (gettype($value)) { + case 'NULL': + unset($data[$key]); + break; + case 'array': + $data[$key] = self::prepareParameters($value); + break; + case 'boolean': + $data[$key] = $value ? 'true' : 'false'; + break; + } + } + + return $data; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json new file mode 100644 index 000000000..c9766ba16 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-oauth", + "description": "Guzzle OAuth plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["oauth", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Oauth": "" } + }, + "target-dir": "Guzzle/Plugin/Oauth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json new file mode 100644 index 000000000..2bbe64cc5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json @@ -0,0 +1,44 @@ +{ + "name": "guzzle/plugin", + "description": "Guzzle plugin component containing all Guzzle HTTP plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["http", "client", "plugin", "extension", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "suggest": { + "guzzle/cache": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin": "" } + }, + "target-dir": "Guzzle/Plugin", + "replace": { + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version" + }, + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php new file mode 100644 index 000000000..cd06f5722 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php @@ -0,0 +1,177 @@ + 'JSON_ERROR_NONE - No errors', + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' + ); + + public function load($config, array $options = array()) + { + // Reset the array of loaded files because this is a new config + $this->loadedFiles = array(); + + if (is_string($config)) { + $config = $this->loadFile($config); + } elseif (!is_array($config)) { + throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config)); + } else { + $this->mergeIncludes($config); + } + + return $this->build($config, $options); + } + + /** + * Add an include alias to the loader + * + * @param string $filename Filename to alias (e.g. _foo) + * @param string $alias Actual file to use (e.g. /path/to/foo.json) + * + * @return self + */ + public function addAlias($filename, $alias) + { + $this->aliases[$filename] = $alias; + + return $this; + } + + /** + * Remove an alias from the loader + * + * @param string $alias Alias to remove + * + * @return self + */ + public function removeAlias($alias) + { + unset($this->aliases[$alias]); + + return $this; + } + + /** + * Perform the parsing of a config file and create the end result + * + * @param array $config Configuration data + * @param array $options Options to use when building + * + * @return mixed + */ + protected abstract function build($config, array $options); + + /** + * Load a configuration file (can load JSON or PHP files that return an array when included) + * + * @param string $filename File to load + * + * @return array + * @throws InvalidArgumentException + * @throws RuntimeException when the JSON cannot be parsed + */ + protected function loadFile($filename) + { + if (isset($this->aliases[$filename])) { + $filename = $this->aliases[$filename]; + } + + switch (pathinfo($filename, PATHINFO_EXTENSION)) { + case 'js': + case 'json': + $level = error_reporting(0); + $json = file_get_contents($filename); + error_reporting($level); + + if ($json === false) { + $err = error_get_last(); + throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']); + } + + $config = json_decode($json, true); + // Throw an exception if there was an error loading the file + if ($error = json_last_error()) { + $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error'; + throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}"); + } + break; + case 'php': + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + $config = require $filename; + if (!is_array($config)) { + throw new InvalidArgumentException('PHP files must return an array of configuration data'); + } + break; + default: + throw new InvalidArgumentException('Unknown file extension: ' . $filename); + } + + // Keep track of this file being loaded to prevent infinite recursion + $this->loadedFiles[$filename] = true; + + // Merge include files into the configuration array + $this->mergeIncludes($config, dirname($filename)); + + return $config; + } + + /** + * Merges in all include files + * + * @param array $config Config data that contains includes + * @param string $basePath Base path to use when a relative path is encountered + * + * @return array Returns the merged and included data + */ + protected function mergeIncludes(&$config, $basePath = null) + { + if (!empty($config['includes'])) { + foreach ($config['includes'] as &$path) { + // Account for relative paths + if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) { + $path = "{$basePath}/{$path}"; + } + // Don't load the same files more than once + if (!isset($this->loadedFiles[$path])) { + $this->loadedFiles[$path] = true; + $config = $this->mergeData($this->loadFile($path), $config); + } + } + } + } + + /** + * Default implementation for merging two arrays of data (uses array_merge_recursive) + * + * @param array $a Original data + * @param array $b Data to merge into the original and overwrite existing values + * + * @return array + */ + protected function mergeData(array $a, array $b) + { + return array_merge_recursive($a, $b); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php new file mode 100644 index 000000000..38150db4b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php @@ -0,0 +1,189 @@ +load($config, $globalParameters); + } + + /** + * @param array $serviceBuilderConfig Service configuration settings: + * - name: Name of the service + * - class: Client class to instantiate using a factory method + * - params: array of key value pair configuration settings for the builder + */ + public function __construct(array $serviceBuilderConfig = array()) + { + $this->builderConfig = $serviceBuilderConfig; + } + + public static function getAllEvents() + { + return array('service_builder.create_client'); + } + + public function unserialize($serialized) + { + $this->builderConfig = json_decode($serialized, true); + } + + public function serialize() + { + return json_encode($this->builderConfig); + } + + /** + * Attach a plugin to every client created by the builder + * + * @param EventSubscriberInterface $plugin Plugin to attach to each client + * + * @return self + */ + public function addGlobalPlugin(EventSubscriberInterface $plugin) + { + $this->plugins[] = $plugin; + + return $this; + } + + /** + * Get data from the service builder without triggering the building of a service + * + * @param string $name Name of the service to retrieve + * + * @return array|null + */ + public function getData($name) + { + return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null; + } + + public function get($name, $throwAway = false) + { + if (!isset($this->builderConfig[$name])) { + + // Check to see if arbitrary data is being referenced + if (isset($this->clients[$name])) { + return $this->clients[$name]; + } + + // Check aliases and return a match if found + foreach ($this->builderConfig as $actualName => $config) { + if (isset($config['alias']) && $config['alias'] == $name) { + return $this->get($actualName, $throwAway); + } + } + throw new ServiceNotFoundException('No service is registered as ' . $name); + } + + if (!$throwAway && isset($this->clients[$name])) { + return $this->clients[$name]; + } + + $builder =& $this->builderConfig[$name]; + + // Convert references to the actual client + foreach ($builder['params'] as &$v) { + if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') { + $v = $this->get(trim($v, '{} ')); + } + } + + // Get the configured parameters and merge in any parameters provided for throw-away clients + $config = $builder['params']; + if (is_array($throwAway)) { + $config = $throwAway + $config; + } + + $client = $builder['class']::factory($config); + + if (!$throwAway) { + $this->clients[$name] = $client; + } + + if ($client instanceof ClientInterface) { + foreach ($this->plugins as $plugin) { + $client->addSubscriber($plugin); + } + // Dispatch an event letting listeners know a client was created + $this->dispatch('service_builder.create_client', array('client' => $client)); + } + + return $client; + } + + public function set($key, $service) + { + if (is_array($service) && isset($service['class']) && isset($service['params'])) { + $this->builderConfig[$key] = $service; + } else { + $this->clients[$key] = $service; + } + + return $this; + } + + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + public function offsetUnset($offset) + { + unset($this->builderConfig[$offset]); + unset($this->clients[$offset]); + } + + public function offsetExists($offset) + { + return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]); + } + + public function offsetGet($offset) + { + return $this->get($offset); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php new file mode 100644 index 000000000..4fc310a47 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php @@ -0,0 +1,40 @@ + &$service) { + + $service['params'] = isset($service['params']) ? $service['params'] : array(); + + // Check if this client builder extends another client + if (!empty($service['extends'])) { + + // Make sure that the service it's extending has been defined + if (!isset($services[$service['extends']])) { + throw new ServiceNotFoundException( + "{$name} is trying to extend a non-existent service: {$service['extends']}" + ); + } + + $extended = &$services[$service['extends']]; + + // Use the correct class attribute + if (empty($service['class'])) { + $service['class'] = isset($extended['class']) ? $extended['class'] : ''; + } + if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) { + $service['params'] = $service['params'] + $extendsParams; + } + } + + // Overwrite default values with global parameter values + if (!empty($options)) { + $service['params'] = $options + $service['params']; + } + + $service['class'] = isset($service['class']) ? $service['class'] : ''; + } + + return new $class($services); + } + + protected function mergeData(array $a, array $b) + { + $result = $b + $a; + + // Merge services using a recursive union of arrays + if (isset($a['services']) && $b['services']) { + + // Get a union of the services of the two arrays + $result['services'] = $b['services'] + $a['services']; + + // Merge each service in using a union of the two arrays + foreach ($result['services'] as $name => &$service) { + + // By default, services completely override a previously defined service unless it extends itself + if (isset($a['services'][$name]['extends']) + && isset($b['services'][$name]['extends']) + && $b['services'][$name]['extends'] == $name + ) { + $service += $a['services'][$name]; + // Use the `extends` attribute of the parent + $service['extends'] = $a['services'][$name]['extends']; + // Merge parameters using a union if both have parameters + if (isset($a['services'][$name]['params'])) { + $service['params'] += $a['services'][$name]['params']; + } + } + } + } + + return $result; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php new file mode 100644 index 000000000..26f8360cc --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php @@ -0,0 +1,46 @@ +loader = $loader; + $this->cache = $cache; + } + + public function load($config, array $options = array()) + { + if (!is_string($config)) { + $key = false; + } else { + $key = 'loader_' . crc32($config); + if ($result = $this->cache->fetch($key)) { + return $result; + } + } + + $result = $this->loader->load($config, $options); + if ($key) { + $this->cache->save($key, $result); + } + + return $result; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php new file mode 100644 index 000000000..3e5f8e53d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php @@ -0,0 +1,297 @@ +getCommand($method, isset($args[0]) ? $args[0] : array())->getResult(); + } + + public function getCommand($name, array $args = array()) + { + // Add global client options to the command + if ($options = $this->getConfig(self::COMMAND_PARAMS)) { + $args += $options; + } + + if (!($command = $this->getCommandFactory()->factory($name, $args))) { + throw new InvalidArgumentException("Command was not found matching {$name}"); + } + + $command->setClient($this); + $this->dispatch('client.command.create', array('client' => $this, 'command' => $command)); + + return $command; + } + + /** + * Set the command factory used to create commands by name + * + * @param CommandFactoryInterface $factory Command factory + * + * @return self + */ + public function setCommandFactory(CommandFactoryInterface $factory) + { + $this->commandFactory = $factory; + + return $this; + } + + /** + * Set the resource iterator factory associated with the client + * + * @param ResourceIteratorFactoryInterface $factory Resource iterator factory + * + * @return self + */ + public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory) + { + $this->resourceIteratorFactory = $factory; + + return $this; + } + + public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array()) + { + if (!($command instanceof CommandInterface)) { + $command = $this->getCommand($command, $commandOptions ?: array()); + } + + return $this->getResourceIteratorFactory()->build($command, $iteratorOptions); + } + + public function execute($command) + { + if ($command instanceof CommandInterface) { + $this->send($this->prepareCommand($command)); + $this->dispatch('command.after_send', array('command' => $command)); + return $command->getResult(); + } elseif (is_array($command) || $command instanceof \Traversable) { + return $this->executeMultiple($command); + } else { + throw new InvalidArgumentException('Command must be a command or array of commands'); + } + } + + public function setDescription(ServiceDescriptionInterface $service) + { + $this->serviceDescription = $service; + + if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) { + $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service)); + } + + // If a baseUrl was set on the description, then update the client + if ($baseUrl = $service->getBaseUrl()) { + $this->setBaseUrl($baseUrl); + } + + return $this; + } + + public function getDescription() + { + return $this->serviceDescription; + } + + /** + * Set the inflector used with the client + * + * @param InflectorInterface $inflector Inflection object + * + * @return self + */ + public function setInflector(InflectorInterface $inflector) + { + $this->inflector = $inflector; + + return $this; + } + + /** + * Get the inflector used with the client + * + * @return self + */ + public function getInflector() + { + if (!$this->inflector) { + $this->inflector = Inflector::getDefault(); + } + + return $this->inflector; + } + + /** + * Prepare a command for sending and get the RequestInterface object created by the command + * + * @param CommandInterface $command Command to prepare + * + * @return RequestInterface + */ + protected function prepareCommand(CommandInterface $command) + { + // Set the client and prepare the command + $request = $command->setClient($this)->prepare(); + // Set the state to new if the command was previously executed + $request->setState(RequestInterface::STATE_NEW); + $this->dispatch('command.before_send', array('command' => $command)); + + return $request; + } + + /** + * Execute multiple commands in parallel + * + * @param array|Traversable $commands Array of CommandInterface objects to execute + * + * @return array Returns an array of the executed commands + * @throws Exception\CommandTransferException + */ + protected function executeMultiple($commands) + { + $requests = array(); + $commandRequests = new \SplObjectStorage(); + + foreach ($commands as $command) { + $request = $this->prepareCommand($command); + $commandRequests[$request] = $command; + $requests[] = $request; + } + + try { + $this->send($requests); + foreach ($commands as $command) { + $this->dispatch('command.after_send', array('command' => $command)); + } + return $commands; + } catch (MultiTransferException $failureException) { + // Throw a CommandTransferException using the successful and failed commands + $e = CommandTransferException::fromMultiTransferException($failureException); + + // Remove failed requests from the successful requests array and add to the failures array + foreach ($failureException->getFailedRequests() as $request) { + if (isset($commandRequests[$request])) { + $e->addFailedCommand($commandRequests[$request]); + unset($commandRequests[$request]); + } + } + + // Always emit the command after_send events for successful commands + foreach ($commandRequests as $success) { + $e->addSuccessfulCommand($commandRequests[$success]); + $this->dispatch('command.after_send', array('command' => $commandRequests[$success])); + } + + throw $e; + } + } + + protected function getResourceIteratorFactory() + { + if (!$this->resourceIteratorFactory) { + // Build the default resource iterator factory if one is not set + $clientClass = get_class($this); + $prefix = substr($clientClass, 0, strrpos($clientClass, '\\')); + $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array( + "{$prefix}\\Iterator", + "{$prefix}\\Model" + )); + } + + return $this->resourceIteratorFactory; + } + + /** + * Get the command factory associated with the client + * + * @return CommandFactoryInterface + */ + protected function getCommandFactory() + { + if (!$this->commandFactory) { + $this->commandFactory = CompositeFactory::getDefaultChain($this); + } + + return $this->commandFactory; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function enableMagicMethods($isEnabled) + { + Version::warn(__METHOD__ . ' is deprecated'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php new file mode 100644 index 000000000..814154f00 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php @@ -0,0 +1,68 @@ +operation = $operation ?: $this->createOperation(); + foreach ($this->operation->getParams() as $name => $arg) { + $currentValue = $this[$name]; + $configValue = $arg->getValue($currentValue); + // If default or static values are set, then this should always be updated on the config object + if ($currentValue !== $configValue) { + $this[$name] = $configValue; + } + } + + $headers = $this[self::HEADERS_OPTION]; + if (!$headers instanceof Collection) { + $this[self::HEADERS_OPTION] = new Collection((array) $headers); + } + + // You can set a command.on_complete option in your parameters to set an onComplete callback + if ($onComplete = $this['command.on_complete']) { + unset($this['command.on_complete']); + $this->setOnComplete($onComplete); + } + + // Set the hidden additional parameters + if (!$this[self::HIDDEN_PARAMS]) { + $this[self::HIDDEN_PARAMS] = array( + self::HEADERS_OPTION, + self::RESPONSE_PROCESSING, + self::HIDDEN_PARAMS, + self::REQUEST_OPTIONS + ); + } + + $this->init(); + } + + /** + * Custom clone behavior + */ + public function __clone() + { + $this->request = null; + $this->result = null; + } + + /** + * Execute the command in the same manner as calling a function + * + * @return mixed Returns the result of {@see AbstractCommand::execute} + */ + public function __invoke() + { + return $this->execute(); + } + + public function getName() + { + return $this->operation->getName(); + } + + /** + * Get the API command information about the command + * + * @return OperationInterface + */ + public function getOperation() + { + return $this->operation; + } + + public function setOnComplete($callable) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException('The onComplete function must be callable'); + } + + $this->onComplete = $callable; + + return $this; + } + + public function execute() + { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be executed.'); + } + + return $this->client->execute($this); + } + + public function getClient() + { + return $this->client; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getRequest() + { + if (!$this->request) { + throw new CommandException('The command must be prepared before retrieving the request'); + } + + return $this->request; + } + + public function getResponse() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + return $this->request->getResponse(); + } + + public function getResult() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + if (null === $this->result) { + $this->process(); + // Call the onComplete method if one is set + if ($this->onComplete) { + call_user_func($this->onComplete, $this); + } + } + + return $this->result; + } + + public function setResult($result) + { + $this->result = $result; + + return $this; + } + + public function isPrepared() + { + return $this->request !== null; + } + + public function isExecuted() + { + return $this->request !== null && $this->request->getState() == 'complete'; + } + + public function prepare() + { + if (!$this->isPrepared()) { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be prepared.'); + } + + // If no response processing value was specified, then attempt to use the highest level of processing + if (!isset($this[self::RESPONSE_PROCESSING])) { + $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL; + } + + // Notify subscribers of the client that the command is being prepared + $this->client->dispatch('command.before_prepare', array('command' => $this)); + + // Fail on missing required arguments, and change parameters via filters + $this->validate(); + // Delegate to the subclass that implements the build method + $this->build(); + + // Add custom request headers set on the command + if ($headers = $this[self::HEADERS_OPTION]) { + foreach ($headers as $key => $value) { + $this->request->setHeader($key, $value); + } + } + + // Add any curl options to the request + if ($options = $this[Client::CURL_OPTIONS]) { + $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options)); + } + + // Set a custom response body + if ($responseBody = $this[self::RESPONSE_BODY]) { + $this->request->setResponseBody($responseBody); + } + + $this->client->dispatch('command.after_prepare', array('command' => $this)); + } + + return $this->request; + } + + /** + * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is + * set, then the command will validate using the default {@see SchemaValidator}. + * + * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema + * + * @return self + */ + public function setValidator(ValidatorInterface $validator) + { + $this->validator = $validator; + + return $this; + } + + public function getRequestHeaders() + { + return $this[self::HEADERS_OPTION]; + } + + /** + * Initialize the command (hook that can be implemented in subclasses) + */ + protected function init() {} + + /** + * Create the request object that will carry out the command + */ + abstract protected function build(); + + /** + * Hook used to create an operation for concrete commands that are not associated with a service description + * + * @return OperationInterface + */ + protected function createOperation() + { + return new Operation(array('name' => get_class($this))); + } + + /** + * Create the result of the command after the request has been completed. + * Override this method in subclasses to customize this behavior + */ + protected function process() + { + $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW + ? DefaultResponseParser::getInstance()->parse($this) + : $this->request->getResponse(); + } + + /** + * Validate and prepare the command based on the schema and rules defined by the command's Operation object + * + * @throws ValidationException when validation errors occur + */ + protected function validate() + { + // Do not perform request validation/transformation if it is disable + if ($this[self::DISABLE_VALIDATION]) { + return; + } + + $errors = array(); + $validator = $this->getValidator(); + foreach ($this->operation->getParams() as $name => $schema) { + $value = $this[$name]; + if (!$validator->validate($schema, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + // Update the config value if it changed and no validation errors were encountered + $this->data[$name] = $value; + } + } + + // Validate additional parameters + $hidden = $this[self::HIDDEN_PARAMS]; + + if ($properties = $this->operation->getAdditionalParameters()) { + foreach ($this->toArray() as $name => $value) { + // It's only additional if it isn't defined in the schema + if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) { + // Always set the name so that error messages are useful + $properties->setName($name); + if (!$validator->validate($properties, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + $this->data[$name] = $value; + } + } + } + } + + if (!empty($errors)) { + $e = new ValidationException('Validation errors: ' . implode("\n", $errors)); + $e->setErrors($errors); + throw $e; + } + } + + /** + * Get the validator used to prepare and validate properties. If no validator has been set on the command, then + * the default {@see SchemaValidator} will be used. + * + * @return ValidatorInterface + */ + protected function getValidator() + { + if (!$this->validator) { + $this->validator = SchemaValidator::getInstance(); + } + + return $this->validator; + } + + /** + * Get array of any validation errors + * If no validator has been set then return false + */ + public function getValidationErrors() + { + if (!$this->validator) { + return false; + } + + return $this->validator->getErrors(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php new file mode 100644 index 000000000..cb6ac40ce --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php @@ -0,0 +1,41 @@ +request = $closure($this, $this->operation); + + if (!$this->request || !$this->request instanceof RequestInterface) { + throw new UnexpectedValueException('Closure command did not return a RequestInterface object'); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php new file mode 100644 index 000000000..fbb61d2ff --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php @@ -0,0 +1,128 @@ +stopPropagation(); + } + + /** + * Get the created object + * + * @return mixed + */ + public function getResult() + { + return $this['result']; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php new file mode 100644 index 000000000..2dc4acd37 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php @@ -0,0 +1,169 @@ +factory = $factory; + } + + /** + * Add a location visitor to the serializer + * + * @param string $location Location to associate with the visitor + * @param RequestVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, RequestVisitorInterface $visitor) + { + $this->factory->addRequestVisitor($location, $visitor); + + return $this; + } + + public function prepare(CommandInterface $command) + { + $request = $this->createRequest($command); + // Keep an array of visitors found in the operation + $foundVisitors = array(); + $operation = $command->getOperation(); + + // Add arguments to the request using the location attribute + foreach ($operation->getParams() as $name => $arg) { + /** @var $arg \Guzzle\Service\Description\Parameter */ + $location = $arg->getLocation(); + // Skip 'uri' locations because they've already been processed + if ($location && $location != 'uri') { + // Instantiate visitors as they are detected in the properties + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getRequestVisitor($location); + } + // Ensure that a value has been set for this parameter + $value = $command[$name]; + if ($value !== null) { + // Apply the parameter value with the location visitor + $foundVisitors[$location]->visit($command, $request, $arg, $value); + } + } + } + + // Serialize additional parameters + if ($additional = $operation->getAdditionalParameters()) { + if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) { + $foundVisitors[$additional->getLocation()] = $visitor; + } + } + + // Call the after method on each visitor found in the operation + foreach ($foundVisitors as $visitor) { + $visitor->after($command, $request); + } + + return $request; + } + + /** + * Serialize additional parameters + * + * @param OperationInterface $operation Operation that owns the command + * @param CommandInterface $command Command to prepare + * @param RequestInterface $request Request to serialize + * @param Parameter $additional Additional parameters + * + * @return null|RequestVisitorInterface + */ + protected function prepareAdditionalParameters( + OperationInterface $operation, + CommandInterface $command, + RequestInterface $request, + Parameter $additional + ) { + if (!($location = $additional->getLocation())) { + return; + } + + $visitor = $this->factory->getRequestVisitor($location); + $hidden = $command[$command::HIDDEN_PARAMS]; + + foreach ($command->toArray() as $key => $value) { + // Ignore values that are null or built-in command options + if ($value !== null + && !in_array($key, $hidden) + && !$operation->hasParam($key) + ) { + $additional->setName($key); + $visitor->visit($command, $request, $additional, $value); + } + } + + return $visitor; + } + + /** + * Create a request for the command and operation + * + * @param CommandInterface $command Command to create a request for + * + * @return RequestInterface + */ + protected function createRequest(CommandInterface $command) + { + $operation = $command->getOperation(); + $client = $command->getClient(); + $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array(); + + // If the command does not specify a template, then assume the base URL of the client + if (!($uri = $operation->getUri())) { + return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options); + } + + // Get the path values and use the client config settings + $variables = array(); + foreach ($operation->getParams() as $name => $arg) { + if ($arg->getLocation() == 'uri') { + if (isset($command[$name])) { + $variables[$name] = $arg->filter($command[$name]); + if (!is_array($variables[$name])) { + $variables[$name] = (string) $variables[$name]; + } + } + } + } + + return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php new file mode 100644 index 000000000..4fe380376 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php @@ -0,0 +1,55 @@ +getRequest()->getResponse(); + + // Account for hard coded content-type values specified in service descriptions + if ($contentType = $command['command.expects']) { + $response->setHeader('Content-Type', $contentType); + } else { + $contentType = (string) $response->getHeader('Content-Type'); + } + + return $this->handleParsing($command, $response, $contentType); + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $result = $response; + if ($result->getBody()) { + if (stripos($contentType, 'json') !== false) { + $result = $result->json(); + } elseif (stripos($contentType, 'xml') !== false) { + $result = $result->xml(); + } + } + + return $result; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php new file mode 100644 index 000000000..1c5ce0741 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php @@ -0,0 +1,39 @@ +client = $client; + $this->aliases = $aliases; + } + + public function factory($name, array $args = array()) + { + if (isset($this->aliases[$name])) { + try { + return $this->client->getCommand($this->aliases[$name], $args); + } catch (InvalidArgumentException $e) { + return null; + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php new file mode 100644 index 000000000..8c46983d6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php @@ -0,0 +1,154 @@ +getDescription()) { + $factories[] = new ServiceDescriptionFactory($description); + } + $factories[] = new ConcreteClassFactory($client); + + return new self($factories); + } + + /** + * @param array $factories Array of command factories + */ + public function __construct(array $factories = array()) + { + $this->factories = $factories; + } + + /** + * Add a command factory to the chain + * + * @param FactoryInterface $factory Factory to add + * @param string|FactoryInterface $before Insert the new command factory before a command factory class or object + * matching a class name. + * @return CompositeFactory + */ + public function add(FactoryInterface $factory, $before = null) + { + $pos = null; + + if ($before) { + foreach ($this->factories as $i => $f) { + if ($before instanceof FactoryInterface) { + if ($f === $before) { + $pos = $i; + break; + } + } elseif (is_string($before)) { + if ($f instanceof $before) { + $pos = $i; + break; + } + } + } + } + + if ($pos === null) { + $this->factories[] = $factory; + } else { + array_splice($this->factories, $i, 0, array($factory)); + } + + return $this; + } + + /** + * Check if the chain contains a specific command factory + * + * @param FactoryInterface|string $factory Factory to check + * + * @return bool + */ + public function has($factory) + { + return (bool) $this->find($factory); + } + + /** + * Remove a specific command factory from the chain + * + * @param string|FactoryInterface $factory Factory to remove by name or instance + * + * @return CompositeFactory + */ + public function remove($factory = null) + { + if (!($factory instanceof FactoryInterface)) { + $factory = $this->find($factory); + } + + $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) { + return $f !== $factory; + })); + + return $this; + } + + /** + * Get a command factory by class name + * + * @param string|FactoryInterface $factory Command factory class or instance + * + * @return null|FactoryInterface + */ + public function find($factory) + { + foreach ($this->factories as $f) { + if ($factory === $f || (is_string($factory) && $f instanceof $factory)) { + return $f; + } + } + } + + /** + * Create a command using the associated command factories + * + * @param string $name Name of the command + * @param array $args Command arguments + * + * @return CommandInterface + */ + public function factory($name, array $args = array()) + { + foreach ($this->factories as $factory) { + $command = $factory->factory($name, $args); + if ($command) { + return $command; + } + } + } + + public function count() + { + return count($this->factories); + } + + public function getIterator() + { + return new \ArrayIterator($this->factories); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php new file mode 100644 index 000000000..0e93deaa0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php @@ -0,0 +1,47 @@ +client = $client; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + public function factory($name, array $args = array()) + { + // Determine the class to instantiate based on the namespace of the current client and the default directory + $prefix = $this->client->getConfig('command.prefix'); + if (!$prefix) { + // The prefix can be specified in a factory method and is cached + $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\'; + $this->client->getConfig()->set('command.prefix', $prefix); + } + + $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name)))); + + // Create the concrete command if it exists + if (class_exists($class)) { + return new $class($args); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php new file mode 100644 index 000000000..35c299d9d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php @@ -0,0 +1,21 @@ +map = $map; + } + + public function factory($name, array $args = array()) + { + if (isset($this->map[$name])) { + $class = $this->map[$name]; + + return new $class($args); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php new file mode 100644 index 000000000..b943a5b50 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php @@ -0,0 +1,71 @@ +setServiceDescription($description); + $this->inflector = $inflector; + } + + /** + * Change the service description used with the factory + * + * @param ServiceDescriptionInterface $description Service description to use + * + * @return FactoryInterface + */ + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the service description + * + * @return ServiceDescriptionInterface + */ + public function getServiceDescription() + { + return $this->description; + } + + public function factory($name, array $args = array()) + { + $command = $this->description->getOperation($name); + + // If a command wasn't found, then try to uppercase the first letter and try again + if (!$command) { + $command = $this->description->getOperation(ucfirst($name)); + // If an inflector was passed, then attempt to get the command using snake_case inflection + if (!$command && $this->inflector) { + $command = $this->description->getOperation($this->inflector->snake($name)); + } + } + + if ($command) { + $class = $command->getClass(); + return new $class($args, $command, $this->description); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php new file mode 100644 index 000000000..adcfca1ba --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php @@ -0,0 +1,69 @@ +resolveRecursively($value, $param) + : $param->filter($value); + } + + /** + * Map nested parameters into the location_key based parameters + * + * @param array $value Value to map + * @param Parameter $param Parameter that holds information about the current key + * + * @return array Returns the mapped array + */ + protected function resolveRecursively(array $value, Parameter $param) + { + foreach ($value as $name => &$v) { + switch ($param->getType()) { + case 'object': + if ($subParam = $param->getProperty($name)) { + $key = $subParam->getWireName(); + $value[$key] = $this->prepareValue($v, $subParam); + if ($name != $key) { + unset($value[$name]); + } + } elseif ($param->getAdditionalProperties() instanceof Parameter) { + $v = $this->prepareValue($v, $param->getAdditionalProperties()); + } + break; + case 'array': + if ($items = $param->getItems()) { + $v = $this->prepareValue($v, $items); + } + break; + } + } + + return $param->filter($value); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php new file mode 100644 index 000000000..168d7806f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php @@ -0,0 +1,58 @@ +filter($value); + $entityBody = EntityBody::factory($value); + $request->setBody($entityBody); + $this->addExpectHeader($request, $entityBody, $param->getData('expect_header')); + // Add the Content-Encoding header if one is set on the EntityBody + if ($encoding = $entityBody->getContentEncoding()) { + $request->setHeader('Content-Encoding', $encoding); + } + } + + /** + * Add the appropriate expect header to a request + * + * @param EntityEnclosingRequestInterface $request Request to update + * @param EntityBodyInterface $body Entity body of the request + * @param string|int $expect Expect header setting + */ + protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect) + { + // Allow the `expect` data parameter to be set to remove the Expect header from the request + if ($expect === false) { + $request->removeHeader('Expect'); + } elseif ($expect !== true) { + // Default to using a MB as the point in which to start using the expect header + $expect = $expect ?: 1048576; + // If the expect_header value is numeric then only add if the size is greater than the cutoff + if (is_numeric($expect) && $body->getSize()) { + if ($body->getSize() < $expect) { + $request->removeHeader('Expect'); + } else { + $request->setHeader('Expect', '100-Continue'); + } + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php new file mode 100644 index 000000000..2a537542c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php @@ -0,0 +1,44 @@ +filter($value); + if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->addPrefixedHeaders($request, $param, $value); + } else { + $request->setHeader($param->getWireName(), $value); + } + } + + /** + * Add a prefixed array of headers to the request + * + * @param RequestInterface $request Request to update + * @param Parameter $param Parameter object + * @param array $value Header array to add + * + * @throws InvalidArgumentException + */ + protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value) + { + if (!is_array($value)) { + throw new InvalidArgumentException('An array of mapped headers expected, but received a single value'); + } + $prefix = $param->getSentAs(); + foreach ($value as $headerName => $headerValue) { + $request->setHeader($prefix . $headerName, $headerValue); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php new file mode 100644 index 000000000..757e1c520 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php @@ -0,0 +1,63 @@ +data = new \SplObjectStorage(); + } + + /** + * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a + * Content-Type header unless you specify one here. + * + * @param string $header Header to set when JSON is added (e.g. application/json) + * + * @return self + */ + public function setContentTypeHeader($header = 'application/json') + { + $this->jsonContentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + if (isset($this->data[$command])) { + $json = $this->data[$command]; + } else { + $json = array(); + } + $json[$param->getWireName()] = $this->prepareValue($value, $param); + $this->data[$command] = $json; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + if (isset($this->data[$command])) { + // Don't overwrite the Content-Type if one is set + if ($this->jsonContentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->jsonContentType); + } + + $request->setBody(json_encode($this->data[$command])); + unset($this->data[$command]); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php new file mode 100644 index 000000000..975850b74 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php @@ -0,0 +1,18 @@ +setPostField($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php new file mode 100644 index 000000000..0853ebe62 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php @@ -0,0 +1,24 @@ +filter($value); + if ($value instanceof PostFileInterface) { + $request->addPostFile($value); + } else { + $request->addPostFile($param->getWireName(), $value); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php new file mode 100644 index 000000000..315877aa0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php @@ -0,0 +1,18 @@ +getQuery()->set($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php new file mode 100644 index 000000000..14e0b2d2b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php @@ -0,0 +1,31 @@ +setResponseBody($value); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php new file mode 100644 index 000000000..5b7148787 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php @@ -0,0 +1,252 @@ +data = new \SplObjectStorage(); + } + + /** + * Change the content-type header that is added when XML is found + * + * @param string $header Header to set when XML is found + * + * @return self + */ + public function setContentTypeHeader($header) + { + $this->contentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $xml = isset($this->data[$command]) + ? $this->data[$command] + : $this->createRootElement($param->getParent()); + $this->addXml($xml, $param, $value); + + $this->data[$command] = $xml; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + $xml = null; + + // If data was found that needs to be serialized, then do so + if (isset($this->data[$command])) { + $xml = $this->finishDocument($this->data[$command]); + unset($this->data[$command]); + } else { + // Check if XML should always be sent for the command + $operation = $command->getOperation(); + if ($operation->getData('xmlAllowEmpty')) { + $xmlWriter = $this->createRootElement($operation); + $xml = $this->finishDocument($xmlWriter); + } + } + + if ($xml) { + // Don't overwrite the Content-Type if one is set + if ($this->contentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->contentType); + } + $request->setBody($xml); + } + } + + /** + * Create the root XML element to use with a request + * + * @param Operation $operation Operation object + * + * @return \XMLWriter + */ + protected function createRootElement(Operation $operation) + { + static $defaultRoot = array('name' => 'Request'); + // If no root element was specified, then just wrap the XML in 'Request' + $root = $operation->getData('xmlRoot') ?: $defaultRoot; + // Allow the XML declaration to be customized with xmlEncoding + $encoding = $operation->getData('xmlEncoding'); + + $xmlWriter = $this->startDocument($encoding); + + $xmlWriter->startElement($root['name']); + // Create the wrapping element with no namespaces if no namespaces were present + if (!empty($root['namespaces'])) { + // Create the wrapping element with an array of one or more namespaces + foreach ((array) $root['namespaces'] as $prefix => $uri) { + $nsLabel = 'xmlns'; + if (!is_numeric($prefix)) { + $nsLabel .= ':'.$prefix; + } + $xmlWriter->writeAttribute($nsLabel, $uri); + } + } + return $xmlWriter; + } + + /** + * Recursively build the XML body + * + * @param \XMLWriter $xmlWriter XML to modify + * @param Parameter $param API Parameter + * @param mixed $value Value to add + */ + protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value) + { + if ($value === null) { + return; + } + + $value = $param->filter($value); + $type = $param->getType(); + $name = $param->getWireName(); + $prefix = null; + $namespace = $param->getData('xmlNamespace'); + if (false !== strpos($name, ':')) { + list($prefix, $name) = explode(':', $name, 2); + } + + if ($type == 'object' || $type == 'array') { + if (!$param->getData('xmlFlattened')) { + $xmlWriter->startElementNS(null, $name, $namespace); + } + if ($param->getType() == 'array') { + $this->addXmlArray($xmlWriter, $param, $value); + } elseif ($param->getType() == 'object') { + $this->addXmlObject($xmlWriter, $param, $value); + } + if (!$param->getData('xmlFlattened')) { + $xmlWriter->endElement(); + } + return; + } + if ($param->getData('xmlAttribute')) { + $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value); + } else { + $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value); + } + } + + /** + * Write an attribute with namespace if used + * + * @param \XMLWriter $xmlWriter XMLWriter instance + * @param string $prefix Namespace prefix if any + * @param string $name Attribute name + * @param string $namespace The uri of the namespace + * @param string $value The attribute content + */ + protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value) + { + if (empty($namespace)) { + $xmlWriter->writeAttribute($name, $value); + } else { + $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value); + } + } + + /** + * Write an element with namespace if used + * + * @param \XMLWriter $xmlWriter XML writer resource + * @param string $prefix Namespace prefix if any + * @param string $name Element name + * @param string $namespace The uri of the namespace + * @param string $value The element content + */ + protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value) + { + $xmlWriter->startElementNS($prefix, $name, $namespace); + if (strpbrk($value, '<>&')) { + $xmlWriter->writeCData($value); + } else { + $xmlWriter->writeRaw($value); + } + $xmlWriter->endElement(); + } + + /** + * Create a new xml writer and start a document + * + * @param string $encoding document encoding + * + * @return \XMLWriter the writer resource + */ + protected function startDocument($encoding) + { + $xmlWriter = new \XMLWriter(); + $xmlWriter->openMemory(); + $xmlWriter->startDocument('1.0', $encoding); + + return $xmlWriter; + } + + /** + * End the document and return the output + * + * @param \XMLWriter $xmlWriter + * + * @return \string the writer resource + */ + protected function finishDocument($xmlWriter) + { + $xmlWriter->endDocument(); + + return $xmlWriter->outputMemory(); + } + + /** + * Add an array to the XML + */ + protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + if ($items = $param->getItems()) { + foreach ($value as $v) { + $this->addXml($xmlWriter, $items, $v); + } + } + } + + /** + * Add an object to the XML + */ + protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + $noAttributes = array(); + // add values which have attributes + foreach ($value as $name => $v) { + if ($property = $param->getProperty($name)) { + if ($property->getData('xmlAttribute')) { + $this->addXml($xmlWriter, $property, $v); + } else { + $noAttributes[] = array('value' => $v, 'property' => $property); + } + } + } + // now add values with no attributes + foreach ($noAttributes as $element) { + $this->addXml($xmlWriter, $element['property'], $element['value']); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php new file mode 100644 index 000000000..d87eeb945 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php @@ -0,0 +1,26 @@ +getName()] = $param->filter($response->getBody()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php new file mode 100644 index 000000000..0f8737cbd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php @@ -0,0 +1,50 @@ +getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->processPrefixedHeaders($response, $param, $value); + } else { + $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName())); + } + } + + /** + * Process a prefixed header array + * + * @param Response $response Response that contains the headers + * @param Parameter $param Parameter object + * @param array $value Value response array to modify + */ + protected function processPrefixedHeaders(Response $response, Parameter $param, &$value) + { + // Grab prefixed headers that should be placed into an array with the prefix stripped + if ($prefix = $param->getSentAs()) { + $container = $param->getName(); + $len = strlen($prefix); + // Find all matching headers and place them into the containing element + foreach ($response->getHeaders()->toArray() as $key => $header) { + if (stripos($key, $prefix) === 0) { + // Account for multi-value headers + $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header; + } + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php new file mode 100644 index 000000000..a609ebd8c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php @@ -0,0 +1,93 @@ +getResponse()->json(); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $name = $param->getName(); + $key = $param->getWireName(); + if (isset($value[$key])) { + $this->recursiveProcess($param, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + if ($value === null) { + return; + } + + if (is_array($value)) { + $type = $param->getType(); + if ($type == 'array') { + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } elseif ($type == 'object' && !isset($value[0])) { + // On the above line, we ensure that the array is associative and not numerically indexed + $knownProperties = array(); + if ($properties = $param->getProperties()) { + foreach ($properties as $property) { + $name = $property->getName(); + $key = $property->getWireName(); + $knownProperties[$name] = 1; + if (isset($value[$key])) { + $this->recursiveProcess($property, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } elseif (($additional = $param->getAdditionalProperties()) !== true) { + // Validate and filter additional properties + foreach ($value as &$v) { + $this->recursiveProcess($additional, $v); + } + } + } + } + + $value = $param->filter($value); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php new file mode 100644 index 000000000..1b10ebce7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php @@ -0,0 +1,23 @@ +getName()] = $response->getReasonPhrase(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php new file mode 100644 index 000000000..033f40c3f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php @@ -0,0 +1,46 @@ +getName()] = $response->getStatusCode(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php new file mode 100644 index 000000000..bb7124be7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php @@ -0,0 +1,151 @@ +getResponse()->xml()), true); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $sentAs = $param->getWireName(); + $name = $param->getName(); + if (isset($value[$sentAs])) { + $this->recursiveProcess($param, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being processed + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + $type = $param->getType(); + + if (!is_array($value)) { + if ($type == 'array') { + // Cast to an array if the value was a string, but should be an array + $this->recursiveProcess($param->getItems(), $value); + $value = array($value); + } + } elseif ($type == 'object') { + $this->processObject($param, $value); + } elseif ($type == 'array') { + $this->processArray($param, $value); + } elseif ($type == 'string' && gettype($value) == 'array') { + $value = ''; + } + + if ($value !== null) { + $value = $param->filter($value); + } + } + + /** + * Process an array + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processArray(Parameter $param, &$value) + { + // Convert the node if it was meant to be an array + if (!isset($value[0])) { + // Collections fo nodes are sometimes wrapped in an additional array. For example: + // 12 should become: + // array('Items' => array(array('a' => 1), array('a' => 2)) + // Some nodes are not wrapped. For example: 12 + // should become array('Foo' => array(array('a' => 1), array('a' => 2)) + if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) { + // Account for the case of a collection wrapping wrapped nodes: Items => Item[] + $value = $value[$param->getItems()->getWireName()]; + // If the wrapped node only had one value, then make it an array of nodes + if (!isset($value[0]) || !is_array($value)) { + $value = array($value); + } + } elseif (!empty($value)) { + // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the + // value is set and not empty + $value = array($value); + } + } + + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } + + /** + * Process an object + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processObject(Parameter $param, &$value) + { + // Ensure that the array is associative and not numerically indexed + if (!isset($value[0]) && ($properties = $param->getProperties())) { + $knownProperties = array(); + foreach ($properties as $property) { + $name = $property->getName(); + $sentAs = $property->getWireName(); + $knownProperties[$name] = 1; + if ($property->getData('xmlAttribute')) { + $this->processXmlAttribute($property, $value); + } elseif (isset($value[$sentAs])) { + $this->recursiveProcess($property, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } + } + } + + /** + * Process an XML attribute property + * + * @param Parameter $property Property to process + * @param array $value Value to process and update + */ + protected function processXmlAttribute(Parameter $property, array &$value) + { + $sentAs = $property->getWireName(); + if (isset($value['@attributes'][$sentAs])) { + $value[$property->getName()] = $value['@attributes'][$sentAs]; + unset($value['@attributes'][$sentAs]); + if (empty($value['@attributes'])) { + unset($value['@attributes']); + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php new file mode 100644 index 000000000..74cb62813 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php @@ -0,0 +1,138 @@ + 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor', + 'request.header' => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor', + 'request.json' => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', + 'request.postField' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor', + 'request.postFile' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor', + 'request.query' => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor', + 'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.responseBody' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.xml' => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor', + 'response.body' => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor', + 'response.header' => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor', + 'response.json' => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', + 'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor', + 'response.statusCode' => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor', + 'response.xml' => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor' + ); + + /** @var array Array of mappings of location names to classes */ + protected $mappings; + + /** @var array Cache of instantiated visitors */ + protected $cache = array(); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to + * use the default values. + */ + public function __construct(array $mappings = null) + { + $this->mappings = $mappings === null ? self::$defaultMappings : $mappings; + } + + /** + * Get an instance of a request visitor by location name + * + * @param string $visitor Visitor name + * + * @return RequestVisitorInterface + */ + public function getRequestVisitor($visitor) + { + return $this->getKey('request.' . $visitor); + } + + /** + * Get an instance of a response visitor by location name + * + * @param string $visitor Visitor name + * + * @return ResponseVisitorInterface + */ + public function getResponseVisitor($visitor) + { + return $this->getKey('response.' . $visitor); + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param RequestVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addRequestVisitor($name, RequestVisitorInterface $visitor) + { + $this->cache['request.' . $name] = $visitor; + + return $this; + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param ResponseVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addResponseVisitor($name, ResponseVisitorInterface $visitor) + { + $this->cache['response.' . $name] = $visitor; + + return $this; + } + + /** + * Get a visitor by key value name + * + * @param string $key Key name to retrieve + * + * @return mixed + * @throws InvalidArgumentException + */ + private function getKey($key) + { + if (!isset($this->cache[$key])) { + if (!isset($this->mappings[$key])) { + list($type, $name) = explode('.', $key); + throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}"); + } + $this->cache[$key] = new $this->mappings[$key]; + } + + return $this->cache[$key]; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php new file mode 100644 index 000000000..0748b5af0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php @@ -0,0 +1,89 @@ +responseParser = $parser; + + return $this; + } + + /** + * Set the request serializer used with the command + * + * @param RequestSerializerInterface $serializer Request serializer + * + * @return self + */ + public function setRequestSerializer(RequestSerializerInterface $serializer) + { + $this->requestSerializer = $serializer; + + return $this; + } + + /** + * Get the request serializer used with the command + * + * @return RequestSerializerInterface + */ + public function getRequestSerializer() + { + if (!$this->requestSerializer) { + // Use the default request serializer if none was found + $this->requestSerializer = DefaultRequestSerializer::getInstance(); + } + + return $this->requestSerializer; + } + + /** + * Get the response parser used for the operation + * + * @return ResponseParserInterface + */ + public function getResponseParser() + { + if (!$this->responseParser) { + // Use the default response parser if none was found + $this->responseParser = OperationResponseParser::getInstance(); + } + + return $this->responseParser; + } + + protected function build() + { + // Prepare and serialize the request + $this->request = $this->getRequestSerializer()->prepare($this); + } + + protected function process() + { + // Do not process the response if 'command.response_processing' is set to 'raw' + $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW + ? $this->request->getResponse() + : $this->getResponseParser()->parse($this); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php new file mode 100644 index 000000000..ca00bc062 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php @@ -0,0 +1,195 @@ +factory = $factory; + $this->schemaInModels = $schemaInModels; + } + + /** + * Add a location visitor to the command + * + * @param string $location Location to associate with the visitor + * @param ResponseVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, ResponseVisitorInterface $visitor) + { + $this->factory->addResponseVisitor($location, $visitor); + + return $this; + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $operation = $command->getOperation(); + $type = $operation->getResponseType(); + $model = null; + + if ($type == OperationInterface::TYPE_MODEL) { + $model = $operation->getServiceDescription()->getModel($operation->getResponseClass()); + } elseif ($type == OperationInterface::TYPE_CLASS) { + return $this->parseClass($command); + } + + if (!$model) { + // Return basic processing if the responseType is not model or the model cannot be found + return parent::handleParsing($command, $response, $contentType); + } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) { + // Returns a model with no visiting if the command response processing is not model + return new Model(parent::handleParsing($command, $response, $contentType)); + } else { + // Only inject the schema into the model if "schemaInModel" is true + return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null); + } + } + + /** + * Parse a class object + * + * @param CommandInterface $command Command to parse into an object + * + * @return mixed + * @throws ResponseClassException + */ + protected function parseClass(CommandInterface $command) + { + // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result + $event = new CreateResponseClassEvent(array('command' => $command)); + $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event); + if ($result = $event->getResult()) { + return $result; + } + + $className = $command->getOperation()->getResponseClass(); + if (!method_exists($className, 'fromCommand')) { + throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method"); + } + + return $className::fromCommand($command); + } + + /** + * Perform transformations on the result array + * + * @param Parameter $model Model that defines the structure + * @param CommandInterface $command Command that performed the operation + * @param Response $response Response received + * + * @return array Returns the array of result data + */ + protected function visitResult(Parameter $model, CommandInterface $command, Response $response) + { + $foundVisitors = $result = $knownProps = array(); + $props = $model->getProperties(); + + foreach ($props as $schema) { + if ($location = $schema->getLocation()) { + // Trigger the before method on the first found visitor of this type + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + } + } + + // Visit additional properties when it is an actual schema + if (($additional = $model->getAdditionalProperties()) instanceof Parameter) { + $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors); + } + + // Apply the parameter value with the location visitor + foreach ($props as $schema) { + $knownProps[$schema->getName()] = 1; + if ($location = $schema->getLocation()) { + $foundVisitors[$location]->visit($command, $response, $schema, $result); + } + } + + // Remove any unknown and potentially unsafe top-level properties + if ($additional === false) { + $result = array_intersect_key($result, $knownProps); + } + + // Call the after() method of each found visitor + foreach ($foundVisitors as $visitor) { + $visitor->after($command); + } + + return $result; + } + + protected function visitAdditionalProperties( + Parameter $model, + CommandInterface $command, + Response $response, + Parameter $additional, + &$result, + array &$foundVisitors + ) { + // Only visit when a location is specified + if ($location = $additional->getLocation()) { + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + // Only traverse if an array was parsed from the before() visitors + if (is_array($result)) { + // Find each additional property + foreach (array_keys($result) as $key) { + // Check if the model actually knows this property. If so, then it is not additional + if (!$model->getProperty($key)) { + // Set the name to the key so that we can parse it with each visitor + $additional->setName($key); + $foundVisitors[$location]->visit($command, $response, $additional, $result); + } + } + // Reset the additionalProperties name to null + $additional->setName(null); + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php new file mode 100644 index 000000000..60b9334d4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php @@ -0,0 +1,21 @@ + true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true, + 'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true, + 'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true, + 'errorResponses' => true + ); + + /** @var array Parameters */ + protected $parameters = array(); + + /** @var Parameter Additional parameters schema */ + protected $additionalParameters; + + /** @var string Name of the command */ + protected $name; + + /** @var string HTTP method */ + protected $httpMethod; + + /** @var string This is a short summary of what the operation does */ + protected $summary; + + /** @var string A longer text field to explain the behavior of the operation. */ + protected $notes; + + /** @var string Reference URL providing more information about the operation */ + protected $documentationUrl; + + /** @var string HTTP URI of the command */ + protected $uri; + + /** @var string Class of the command object */ + protected $class; + + /** @var string This is what is returned from the method */ + protected $responseClass; + + /** @var string Type information about the response */ + protected $responseType; + + /** @var string Information about the response returned by the operation */ + protected $responseNotes; + + /** @var bool Whether or not the command is deprecated */ + protected $deprecated; + + /** @var array Array of errors that could occur when running the command */ + protected $errorResponses; + + /** @var ServiceDescriptionInterface */ + protected $description; + + /** @var array Extra operation information */ + protected $data; + + /** + * Builds an Operation object using an array of configuration data: + * - name: (string) Name of the command + * - httpMethod: (string) HTTP method of the operation + * - uri: (string) URI template that can create a relative or absolute URL + * - class: (string) Concrete class that implements this command + * - parameters: (array) Associative array of parameters for the command. {@see Parameter} for information. + * - summary: (string) This is a short summary of what the operation does + * - notes: (string) A longer text field to explain the behavior of the operation. + * - documentationUrl: (string) Reference URL providing more information about the operation + * - responseClass: (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant + * class name, or model. + * - responseNotes: (string) Information about the response returned by the operation + * - responseType: (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this + * value will be automatically inferred based on whether or not there is a model matching the + * name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default. + * - deprecated: (bool) Set to true if this is a deprecated command + * - errorResponses: (array) Errors that could occur when executing the command. Array of hashes, each with a + * 'code' (the HTTP response code), 'reason' (response reason phrase or description of the + * error), and 'class' (a custom exception class that would be thrown if the error is + * encountered). + * - data: (array) Any extra data that might be used to help build or serialize the operation + * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is + * not in the schema + * + * @param array $config Array of configuration data + * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found + */ + public function __construct(array $config = array(), ServiceDescriptionInterface $description = null) + { + $this->description = $description; + + // Get the intersection of the available properties and properties set on the operation + foreach (array_intersect_key($config, self::$properties) as $key => $value) { + $this->{$key} = $value; + } + + $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS; + $this->deprecated = (bool) $this->deprecated; + $this->errorResponses = $this->errorResponses ?: array(); + $this->data = $this->data ?: array(); + + if (!$this->responseClass) { + $this->responseClass = 'array'; + $this->responseType = 'primitive'; + } elseif ($this->responseType) { + // Set the response type to perform validation + $this->setResponseType($this->responseType); + } else { + // A response class was set and no response type was set, so guess what the type is + $this->inferResponseType(); + } + + // Parameters need special handling when adding + if ($this->parameters) { + foreach ($this->parameters as $name => $param) { + if ($param instanceof Parameter) { + $param->setName($name)->setParent($this); + } elseif (is_array($param)) { + $param['name'] = $name; + $this->addParam(new Parameter($param, $this->description)); + } + } + } + + if ($this->additionalParameters) { + if ($this->additionalParameters instanceof Parameter) { + $this->additionalParameters->setParent($this); + } elseif (is_array($this->additionalParameters)) { + $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description)); + } + } + } + + public function toArray() + { + $result = array(); + // Grab valid properties and filter out values that weren't set + foreach (array_keys(self::$properties) as $check) { + if ($value = $this->{$check}) { + $result[$check] = $value; + } + } + // Remove the name property + unset($result['name']); + // Parameters need to be converted to arrays + $result['parameters'] = array(); + foreach ($this->parameters as $key => $param) { + $result['parameters'][$key] = $param->toArray(); + } + // Additional parameters need to be cast to an array + if ($this->additionalParameters instanceof Parameter) { + $result['additionalParameters'] = $this->additionalParameters->toArray(); + } + + return $result; + } + + public function getServiceDescription() + { + return $this->description; + } + + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + public function getParams() + { + return $this->parameters; + } + + public function getParamNames() + { + return array_keys($this->parameters); + } + + public function hasParam($name) + { + return isset($this->parameters[$name]); + } + + public function getParam($param) + { + return isset($this->parameters[$param]) ? $this->parameters[$param] : null; + } + + /** + * Add a parameter to the command + * + * @param Parameter $param Parameter to add + * + * @return self + */ + public function addParam(Parameter $param) + { + $this->parameters[$param->getName()] = $param; + $param->setParent($this); + + return $this; + } + + /** + * Remove a parameter from the command + * + * @param string $name Name of the parameter to remove + * + * @return self + */ + public function removeParam($name) + { + unset($this->parameters[$name]); + + return $this; + } + + public function getHttpMethod() + { + return $this->httpMethod; + } + + /** + * Set the HTTP method of the command + * + * @param string $httpMethod Method to set + * + * @return self + */ + public function setHttpMethod($httpMethod) + { + $this->httpMethod = $httpMethod; + + return $this; + } + + public function getClass() + { + return $this->class; + } + + /** + * Set the concrete class of the command + * + * @param string $className Concrete class name + * + * @return self + */ + public function setClass($className) + { + $this->class = $className; + + return $this; + } + + public function getName() + { + return $this->name; + } + + /** + * Set the name of the command + * + * @param string $name Name of the command + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getSummary() + { + return $this->summary; + } + + /** + * Set a short summary of what the operation does + * + * @param string $summary Short summary of the operation + * + * @return self + */ + public function setSummary($summary) + { + $this->summary = $summary; + + return $this; + } + + public function getNotes() + { + return $this->notes; + } + + /** + * Set a longer text field to explain the behavior of the operation. + * + * @param string $notes Notes on the operation + * + * @return self + */ + public function setNotes($notes) + { + $this->notes = $notes; + + return $this; + } + + public function getDocumentationUrl() + { + return $this->documentationUrl; + } + + /** + * Set the URL pointing to additional documentation on the command + * + * @param string $docUrl Documentation URL + * + * @return self + */ + public function setDocumentationUrl($docUrl) + { + $this->documentationUrl = $docUrl; + + return $this; + } + + public function getResponseClass() + { + return $this->responseClass; + } + + /** + * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array', + * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID). + * + * @param string $responseClass Type of response + * + * @return self + */ + public function setResponseClass($responseClass) + { + $this->responseClass = $responseClass; + $this->inferResponseType(); + + return $this; + } + + public function getResponseType() + { + return $this->responseType; + } + + /** + * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation' + * + * @param string $responseType Response type information + * + * @return self + * @throws InvalidArgumentException + */ + public function setResponseType($responseType) + { + static $types = array( + self::TYPE_PRIMITIVE => true, + self::TYPE_CLASS => true, + self::TYPE_MODEL => true, + self::TYPE_DOCUMENTATION => true + ); + if (!isset($types[$responseType])) { + throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types))); + } + + $this->responseType = $responseType; + + return $this; + } + + public function getResponseNotes() + { + return $this->responseNotes; + } + + /** + * Set notes about the response of the operation + * + * @param string $notes Response notes + * + * @return self + */ + public function setResponseNotes($notes) + { + $this->responseNotes = $notes; + + return $this; + } + + public function getDeprecated() + { + return $this->deprecated; + } + + /** + * Set whether or not the command is deprecated + * + * @param bool $isDeprecated Set to true to mark as deprecated + * + * @return self + */ + public function setDeprecated($isDeprecated) + { + $this->deprecated = $isDeprecated; + + return $this; + } + + public function getUri() + { + return $this->uri; + } + + /** + * Set the URI template of the command + * + * @param string $uri URI template to set + * + * @return self + */ + public function setUri($uri) + { + $this->uri = $uri; + + return $this; + } + + public function getErrorResponses() + { + return $this->errorResponses; + } + + /** + * Add an error to the command + * + * @param string $code HTTP response code + * @param string $reason HTTP response reason phrase or information about the error + * @param string $class Exception class associated with the error + * + * @return self + */ + public function addErrorResponse($code, $reason, $class) + { + $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class); + + return $this; + } + + /** + * Set all of the error responses of the operation + * + * @param array $errorResponses Hash of error name to a hash containing a code, reason, class + * + * @return self + */ + public function setErrorResponses(array $errorResponses) + { + $this->errorResponses = $errorResponses; + + return $this; + } + + public function getData($name) + { + return isset($this->data[$name]) ? $this->data[$name] : null; + } + + /** + * Set a particular data point on the operation + * + * @param string $name Name of the data value + * @param mixed $value Value to set + * + * @return self + */ + public function setData($name, $value) + { + $this->data[$name] = $value; + + return $this; + } + + /** + * Get the additionalParameters of the operation + * + * @return Parameter|null + */ + public function getAdditionalParameters() + { + return $this->additionalParameters; + } + + /** + * Set the additionalParameters of the operation + * + * @param Parameter|null $parameter Parameter to set + * + * @return self + */ + public function setAdditionalParameters($parameter) + { + if ($this->additionalParameters = $parameter) { + $this->additionalParameters->setParent($this); + } + + return $this; + } + + /** + * Infer the response type from the responseClass value + */ + protected function inferResponseType() + { + static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1); + if (isset($primitives[$this->responseClass])) { + $this->responseType = self::TYPE_PRIMITIVE; + } elseif ($this->description && $this->description->hasModel($this->responseClass)) { + $this->responseType = self::TYPE_MODEL; + } else { + $this->responseType = self::TYPE_CLASS; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php new file mode 100644 index 000000000..4de41bd67 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php @@ -0,0 +1,159 @@ +getModel($data['$ref'])) { + $data = $model->toArray() + $data; + } + } elseif (isset($data['extends'])) { + // If this parameter extends from another parameter then start with the actual data + // union in the parent's data (e.g. actual supersedes parent) + if ($extends = $description->getModel($data['extends'])) { + $data += $extends->toArray(); + } + } + } + + // Pull configuration data into the parameter + foreach ($data as $key => $value) { + $this->{$key} = $value; + } + + $this->serviceDescription = $description; + $this->required = (bool) $this->required; + $this->data = (array) $this->data; + + if ($this->filters) { + $this->setFilters((array) $this->filters); + } + + if ($this->type == 'object' && $this->additionalProperties === null) { + $this->additionalProperties = true; + } + } + + /** + * Convert the object to an array + * + * @return array + */ + public function toArray() + { + static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs', + 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum', + 'filters'); + + $result = array(); + + // Anything that is in the `Items` attribute of an array *must* include it's name if available + if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) { + $result['name'] = $this->name; + } + + foreach ($checks as $c) { + if ($value = $this->{$c}) { + $result[$c] = $value; + } + } + + if ($this->default !== null) { + $result['default'] = $this->default; + } + + if ($this->items !== null) { + $result['items'] = $this->getItems()->toArray(); + } + + if ($this->additionalProperties !== null) { + $result['additionalProperties'] = $this->getAdditionalProperties(); + if ($result['additionalProperties'] instanceof self) { + $result['additionalProperties'] = $result['additionalProperties']->toArray(); + } + } + + if ($this->type == 'object' && $this->properties) { + $result['properties'] = array(); + foreach ($this->getProperties() as $name => $property) { + $result['properties'][$name] = $property->toArray(); + } + } + + return $result; + } + + /** + * Get the default or static value of the command based on a value + * + * @param string $value Value that is currently set + * + * @return mixed Returns the value, a static value if one is present, or a default value + */ + public function getValue($value) + { + if ($this->static || ($this->default !== null && $value === null)) { + return $this->default; + } + + return $value; + } + + /** + * Run a value through the filters OR format attribute associated with the parameter + * + * @param mixed $value Value to filter + * + * @return mixed Returns the filtered value + */ + public function filter($value) + { + // Formats are applied exclusively and supersed filters + if ($this->format) { + return SchemaFormatter::format($this->format, $value); + } + + // Convert Boolean values + if ($this->type == 'boolean' && !is_bool($value)) { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); + } + + // Apply filters to the value + if ($this->filters) { + foreach ($this->filters as $filter) { + if (is_array($filter)) { + // Convert complex filters that hold value place holders + foreach ($filter['args'] as &$data) { + if ($data == '@value') { + $data = $value; + } elseif ($data == '@api') { + $data = $this; + } + } + $value = call_user_func_array($filter['method'], $filter['args']); + } else { + $value = call_user_func($filter, $value); + } + } + } + + return $value; + } + + /** + * Get the name of the parameter + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the key of the parameter, where sentAs will supersede name if it is set + * + * @return string + */ + public function getWireName() + { + return $this->sentAs ?: $this->name; + } + + /** + * Set the name of the parameter + * + * @param string $name Name to set + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get the type(s) of the parameter + * + * @return string|array + */ + public function getType() + { + return $this->type; + } + + /** + * Set the type(s) of the parameter + * + * @param string|array $type Type of parameter or array of simple types used in a union + * + * @return self + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Get if the parameter is required + * + * @return bool + */ + public function getRequired() + { + return $this->required; + } + + /** + * Set if the parameter is required + * + * @param bool $isRequired Whether or not the parameter is required + * + * @return self + */ + public function setRequired($isRequired) + { + $this->required = (bool) $isRequired; + + return $this; + } + + /** + * Get the default value of the parameter + * + * @return string|null + */ + public function getDefault() + { + return $this->default; + } + + /** + * Set the default value of the parameter + * + * @param string|null $default Default value to set + * + * @return self + */ + public function setDefault($default) + { + $this->default = $default; + + return $this; + } + + /** + * Get the description of the parameter + * + * @return string|null + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set the description of the parameter + * + * @param string $description Description + * + * @return self + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Get the minimum acceptable value for an integer + * + * @return int|null + */ + public function getMinimum() + { + return $this->minimum; + } + + /** + * Set the minimum acceptable value for an integer + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinimum($min) + { + $this->minimum = $min; + + return $this; + } + + /** + * Get the maximum acceptable value for an integer + * + * @return int|null + */ + public function getMaximum() + { + return $this->maximum; + } + + /** + * Set the maximum acceptable value for an integer + * + * @param int $max Maximum + * + * @return self + */ + public function setMaximum($max) + { + $this->maximum = $max; + + return $this; + } + + /** + * Get the minimum allowed length of a string value + * + * @return int + */ + public function getMinLength() + { + return $this->minLength; + } + + /** + * Set the minimum allowed length of a string value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinLength($min) + { + $this->minLength = $min; + + return $this; + } + + /** + * Get the maximum allowed length of a string value + * + * @return int|null + */ + public function getMaxLength() + { + return $this->maxLength; + } + + /** + * Set the maximum allowed length of a string value + * + * @param int $max Maximum length + * + * @return self + */ + public function setMaxLength($max) + { + $this->maxLength = $max; + + return $this; + } + + /** + * Get the maximum allowed number of items in an array value + * + * @return int|null + */ + public function getMaxItems() + { + return $this->maxItems; + } + + /** + * Set the maximum allowed number of items in an array value + * + * @param int $max Maximum + * + * @return self + */ + public function setMaxItems($max) + { + $this->maxItems = $max; + + return $this; + } + + /** + * Get the minimum allowed number of items in an array value + * + * @return int + */ + public function getMinItems() + { + return $this->minItems; + } + + /** + * Set the minimum allowed number of items in an array value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinItems($min) + { + $this->minItems = $min; + + return $this; + } + + /** + * Get the location of the parameter + * + * @return string|null + */ + public function getLocation() + { + return $this->location; + } + + /** + * Set the location of the parameter + * + * @param string|null $location Location of the parameter + * + * @return self + */ + public function setLocation($location) + { + $this->location = $location; + + return $this; + } + + /** + * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being + * applied to a location. + * + * @return string|null + */ + public function getSentAs() + { + return $this->sentAs; + } + + /** + * Set the sentAs attribute + * + * @param string|null $name Name of the value as it is sent over the wire + * + * @return self + */ + public function setSentAs($name) + { + $this->sentAs = $name; + + return $this; + } + + /** + * Retrieve a known property from the parameter by name or a data property by name. When not specific name value + * is specified, all data properties will be returned. + * + * @param string|null $name Specify a particular property name to retrieve + * + * @return array|mixed|null + */ + public function getData($name = null) + { + if (!$name) { + return $this->data; + } + + if (isset($this->data[$name])) { + return $this->data[$name]; + } elseif (isset($this->{$name})) { + return $this->{$name}; + } + + return null; + } + + /** + * Set the extra data properties of the parameter or set a specific extra property + * + * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set + * @param mixed|null $data When setting a specific extra property, specify the data to set for it + * + * @return self + */ + public function setData($nameOrData, $data = null) + { + if (is_array($nameOrData)) { + $this->data = $nameOrData; + } else { + $this->data[$nameOrData] = $data; + } + + return $this; + } + + /** + * Get whether or not the default value can be changed + * + * @return mixed|null + */ + public function getStatic() + { + return $this->static; + } + + /** + * Set to true if the default value cannot be changed + * + * @param bool $static True or false + * + * @return self + */ + public function setStatic($static) + { + $this->static = (bool) $static; + + return $this; + } + + /** + * Get an array of filters used by the parameter + * + * @return array + */ + public function getFilters() + { + return $this->filters ?: array(); + } + + /** + * Set the array of filters used by the parameter + * + * @param array $filters Array of functions to use as filters + * + * @return self + */ + public function setFilters(array $filters) + { + $this->filters = array(); + foreach ($filters as $filter) { + $this->addFilter($filter); + } + + return $this; + } + + /** + * Add a filter to the parameter + * + * @param string|array $filter Method to filter the value through + * + * @return self + * @throws InvalidArgumentException + */ + public function addFilter($filter) + { + if (is_array($filter)) { + if (!isset($filter['method'])) { + throw new InvalidArgumentException('A [method] value must be specified for each complex filter'); + } + } + + if (!$this->filters) { + $this->filters = array($filter); + } else { + $this->filters[] = $filter; + } + + return $this; + } + + /** + * Get the parent object (an {@see OperationInterface} or {@see Parameter} + * + * @return OperationInterface|Parameter|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * Set the parent object of the parameter + * + * @param OperationInterface|Parameter|null $parent Parent container of the parameter + * + * @return self + */ + public function setParent($parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Get the properties of the parameter + * + * @return array + */ + public function getProperties() + { + if (!$this->propertiesCache) { + $this->propertiesCache = array(); + foreach (array_keys($this->properties) as $name) { + $this->propertiesCache[$name] = $this->getProperty($name); + } + } + + return $this->propertiesCache; + } + + /** + * Get a specific property from the parameter + * + * @param string $name Name of the property to retrieve + * + * @return null|Parameter + */ + public function getProperty($name) + { + if (!isset($this->properties[$name])) { + return null; + } + + if (!($this->properties[$name] instanceof self)) { + $this->properties[$name]['name'] = $name; + $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription); + $this->properties[$name]->setParent($this); + } + + return $this->properties[$name]; + } + + /** + * Remove a property from the parameter + * + * @param string $name Name of the property to remove + * + * @return self + */ + public function removeProperty($name) + { + unset($this->properties[$name]); + $this->propertiesCache = null; + + return $this; + } + + /** + * Add a property to the parameter + * + * @param Parameter $property Properties to set + * + * @return self + */ + public function addProperty(Parameter $property) + { + $this->properties[$property->getName()] = $property; + $property->setParent($this); + $this->propertiesCache = null; + + return $this; + } + + /** + * Get the additionalProperties value of the parameter + * + * @return bool|Parameter|null + */ + public function getAdditionalProperties() + { + if (is_array($this->additionalProperties)) { + $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription); + $this->additionalProperties->setParent($this); + } + + return $this->additionalProperties; + } + + /** + * Set the additionalProperties value of the parameter + * + * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow + * + * @return self + */ + public function setAdditionalProperties($additional) + { + $this->additionalProperties = $additional; + + return $this; + } + + /** + * Set the items data of the parameter + * + * @param Parameter|null $items Items to set + * + * @return self + */ + public function setItems(Parameter $items = null) + { + if ($this->items = $items) { + $this->items->setParent($this); + } + + return $this; + } + + /** + * Get the item data of the parameter + * + * @return Parameter|null + */ + public function getItems() + { + if (is_array($this->items)) { + $this->items = new static($this->items, $this->serviceDescription); + $this->items->setParent($this); + } + + return $this->items; + } + + /** + * Get the class that the parameter must implement + * + * @return null|string + */ + public function getInstanceOf() + { + return $this->instanceOf; + } + + /** + * Set the class that the parameter must be an instance of + * + * @param string|null $instanceOf Class or interface name + * + * @return self + */ + public function setInstanceOf($instanceOf) + { + $this->instanceOf = $instanceOf; + + return $this; + } + + /** + * Get the enum of strings that are valid for the parameter + * + * @return array|null + */ + public function getEnum() + { + return $this->enum; + } + + /** + * Set the enum of strings that are valid for the parameter + * + * @param array|null $enum Array of strings or null + * + * @return self + */ + public function setEnum(array $enum = null) + { + $this->enum = $enum; + + return $this; + } + + /** + * Get the regex pattern that must match a value when the value is a string + * + * @return string + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Set the regex pattern that must match a value when the value is a string + * + * @param string $pattern Regex pattern + * + * @return self + */ + public function setPattern($pattern) + { + $this->pattern = $pattern; + + return $this; + } + + /** + * Get the format attribute of the schema + * + * @return string + */ + public function getFormat() + { + return $this->format; + } + + /** + * Set the format attribute of the schema + * + * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http) + * + * @return self + */ + public function setFormat($format) + { + $this->format = $format; + + return $this; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php new file mode 100644 index 000000000..7f47fc9d7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php @@ -0,0 +1,156 @@ +setTimezone(self::getUtcTimeZone())->format($format); + } + + throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php new file mode 100644 index 000000000..b045422d4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php @@ -0,0 +1,291 @@ +castIntegerToStringType = $castIntegerToStringType; + } + + public function validate(Parameter $param, &$value) + { + $this->errors = array(); + $this->recursiveProcess($param, $value); + + if (empty($this->errors)) { + return true; + } else { + sort($this->errors); + return false; + } + } + + /** + * Get the errors encountered while validating + * + * @return array + */ + public function getErrors() + { + return $this->errors ?: array(); + } + + /** + * Recursively validate a parameter + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and validate. The value may change during this validate. + * @param string $path Current validation path (used for error reporting) + * @param int $depth Current depth in the validation validate + * + * @return bool Returns true if valid, or false if invalid + */ + protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0) + { + // Update the value by adding default or static values + $value = $param->getValue($value); + + $required = $param->getRequired(); + // if the value is null and the parameter is not required or is static, then skip any further recursion + if ((null === $value && !$required) || $param->getStatic()) { + return true; + } + + $type = $param->getType(); + // Attempt to limit the number of times is_array is called by tracking if the value is an array + $valueIsArray = is_array($value); + // If a name is set then update the path so that validation messages are more helpful + if ($name = $param->getName()) { + $path .= "[{$name}]"; + } + + if ($type == 'object') { + + // Objects are either associative arrays, ToArrayInterface, or some other object + if ($param->getInstanceOf()) { + $instance = $param->getInstanceOf(); + if (!($value instanceof $instance)) { + $this->errors[] = "{$path} must be an instance of {$instance}"; + return false; + } + } + + // Determine whether or not this "value" has properties and should be traversed + $traverse = $temporaryValue = false; + + // Convert the value to an array + if (!$valueIsArray && $value instanceof ToArrayInterface) { + $value = $value->toArray(); + } + + if ($valueIsArray) { + // Ensure that the array is associative and not numerically indexed + if (isset($value[0])) { + $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array."; + return false; + } + $traverse = true; + } elseif ($value === null) { + // Attempt to let the contents be built up by default values if possible + $value = array(); + $temporaryValue = $valueIsArray = $traverse = true; + } + + if ($traverse) { + + if ($properties = $param->getProperties()) { + // if properties were found, the validate each property of the value + foreach ($properties as $property) { + $name = $property->getName(); + if (isset($value[$name])) { + $this->recursiveProcess($property, $value[$name], $path, $depth + 1); + } else { + $current = null; + $this->recursiveProcess($property, $current, $path, $depth + 1); + // Only set the value if it was populated with something + if (null !== $current) { + $value[$name] = $current; + } + } + } + } + + $additional = $param->getAdditionalProperties(); + if ($additional !== true) { + // If additional properties were found, then validate each against the additionalProperties attr. + $keys = array_keys($value); + // Determine the keys that were specified that were not listed in the properties of the schema + $diff = array_diff($keys, array_keys($properties)); + if (!empty($diff)) { + // Determine which keys are not in the properties + if ($additional instanceOf Parameter) { + foreach ($diff as $key) { + $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth); + } + } else { + // if additionalProperties is set to false and there are additionalProperties in the values, then fail + foreach ($diff as $prop) { + $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop); + } + } + } + } + + // A temporary value will be used to traverse elements that have no corresponding input value. + // This allows nested required parameters with default values to bubble up into the input. + // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value. + if ($temporaryValue && empty($value)) { + $value = null; + $valueIsArray = false; + } + } + + } elseif ($type == 'array' && $valueIsArray && $param->getItems()) { + foreach ($value as $i => &$item) { + // Validate each item in an array against the items attribute of the schema + $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1); + } + } + + // If the value is required and the type is not null, then there is an error if the value is not set + if ($required && $value === null && $type != 'null') { + $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required'); + if ($param->getDescription()) { + $message .= ': ' . $param->getDescription(); + } + $this->errors[] = $message; + return false; + } + + // Validate that the type is correct. If the type is string but an integer was passed, the class can be + // instructed to cast the integer to a string to pass validation. This is the default behavior. + if ($type && (!$type = $this->determineType($type, $value))) { + if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) { + $value = (string) $value; + } else { + $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType()); + } + } + + // Perform type specific validation for strings, arrays, and integers + if ($type == 'string') { + + // Strings can have enums which are a list of predefined values + if (($enum = $param->getEnum()) && !in_array($value, $enum)) { + $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) { + return '"' . addslashes($s) . '"'; + }, $enum)); + } + // Strings can have a regex pattern that the value must match + if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) { + $this->errors[] = "{$path} must match the following regular expression: {$pattern}"; + } + + $strLen = null; + if ($min = $param->getMinLength()) { + $strLen = strlen($value); + if ($strLen < $min) { + $this->errors[] = "{$path} length must be greater than or equal to {$min}"; + } + } + if ($max = $param->getMaxLength()) { + if (($strLen ?: strlen($value)) > $max) { + $this->errors[] = "{$path} length must be less than or equal to {$max}"; + } + } + + } elseif ($type == 'array') { + + $size = null; + if ($min = $param->getMinItems()) { + $size = count($value); + if ($size < $min) { + $this->errors[] = "{$path} must contain {$min} or more elements"; + } + } + if ($max = $param->getMaxItems()) { + if (($size ?: count($value)) > $max) { + $this->errors[] = "{$path} must contain {$max} or fewer elements"; + } + } + + } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') { + if (($min = $param->getMinimum()) && $value < $min) { + $this->errors[] = "{$path} must be greater than or equal to {$min}"; + } + if (($max = $param->getMaximum()) && $value > $max) { + $this->errors[] = "{$path} must be less than or equal to {$max}"; + } + } + + return empty($this->errors); + } + + /** + * From the allowable types, determine the type that the variable matches + * + * @param string $type Parameter type + * @param mixed $value Value to determine the type + * + * @return string|bool Returns the matching type on + */ + protected function determineType($type, $value) + { + foreach ((array) $type as $t) { + if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) { + return 'string'; + } elseif ($t == 'object' && (is_array($value) || is_object($value))) { + return 'object'; + } elseif ($t == 'array' && is_array($value)) { + return 'array'; + } elseif ($t == 'integer' && is_integer($value)) { + return 'integer'; + } elseif ($t == 'boolean' && is_bool($value)) { + return 'boolean'; + } elseif ($t == 'number' && is_numeric($value)) { + return 'number'; + } elseif ($t == 'numeric' && is_numeric($value)) { + return 'numeric'; + } elseif ($t == 'null' && !$value) { + return 'null'; + } elseif ($t == 'any') { + return 'any'; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php new file mode 100644 index 000000000..286e65eec --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php @@ -0,0 +1,271 @@ +load($config, $options); + } + + /** + * @param array $config Array of configuration data + */ + public function __construct(array $config = array()) + { + $this->fromArray($config); + } + + public function serialize() + { + return json_encode($this->toArray()); + } + + public function unserialize($json) + { + $this->operations = array(); + $this->fromArray(json_decode($json, true)); + } + + public function toArray() + { + $result = array( + 'name' => $this->name, + 'apiVersion' => $this->apiVersion, + 'baseUrl' => $this->baseUrl, + 'description' => $this->description + ) + $this->extraData; + $result['operations'] = array(); + foreach ($this->getOperations() as $name => $operation) { + $result['operations'][$operation->getName() ?: $name] = $operation->toArray(); + } + if (!empty($this->models)) { + $result['models'] = array(); + foreach ($this->models as $id => $model) { + $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model; + } + } + + return array_filter($result); + } + + public function getBaseUrl() + { + return $this->baseUrl; + } + + /** + * Set the baseUrl of the description + * + * @param string $baseUrl Base URL of each operation + * + * @return self + */ + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + + return $this; + } + + public function getOperations() + { + foreach (array_keys($this->operations) as $name) { + $this->getOperation($name); + } + + return $this->operations; + } + + public function hasOperation($name) + { + return isset($this->operations[$name]); + } + + public function getOperation($name) + { + // Lazily retrieve and build operations + if (!isset($this->operations[$name])) { + return null; + } + + if (!($this->operations[$name] instanceof Operation)) { + $this->operations[$name] = new Operation($this->operations[$name], $this); + } + + return $this->operations[$name]; + } + + /** + * Add a operation to the service description + * + * @param OperationInterface $operation Operation to add + * + * @return self + */ + public function addOperation(OperationInterface $operation) + { + $this->operations[$operation->getName()] = $operation->setServiceDescription($this); + + return $this; + } + + public function getModel($id) + { + if (!isset($this->models[$id])) { + return null; + } + + if (!($this->models[$id] instanceof Parameter)) { + $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this); + } + + return $this->models[$id]; + } + + public function getModels() + { + // Ensure all models are converted into parameter objects + foreach (array_keys($this->models) as $id) { + $this->getModel($id); + } + + return $this->models; + } + + public function hasModel($id) + { + return isset($this->models[$id]); + } + + /** + * Add a model to the service description + * + * @param Parameter $model Model to add + * + * @return self + */ + public function addModel(Parameter $model) + { + $this->models[$model->getName()] = $model; + + return $this; + } + + public function getApiVersion() + { + return $this->apiVersion; + } + + public function getName() + { + return $this->name; + } + + public function getDescription() + { + return $this->description; + } + + public function getData($key) + { + return isset($this->extraData[$key]) ? $this->extraData[$key] : null; + } + + public function setData($key, $value) + { + $this->extraData[$key] = $value; + + return $this; + } + + /** + * Initialize the state from an array + * + * @param array $config Configuration data + * @throws InvalidArgumentException + */ + protected function fromArray(array $config) + { + // Keep a list of default keys used in service descriptions that is later used to determine extra data keys + static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description'); + // Pull in the default configuration values + foreach ($defaultKeys as $key) { + if (isset($config[$key])) { + $this->{$key} = $config[$key]; + } + } + + // Account for the Swagger name for Guzzle's baseUrl + if (isset($config['basePath'])) { + $this->baseUrl = $config['basePath']; + } + + // Ensure that the models and operations properties are always arrays + $this->models = (array) $this->models; + $this->operations = (array) $this->operations; + + // We want to add operations differently than adding the other properties + $defaultKeys[] = 'operations'; + + // Create operations for each operation + if (isset($config['operations'])) { + foreach ($config['operations'] as $name => $operation) { + if (!($operation instanceof Operation) && !is_array($operation)) { + throw new InvalidArgumentException('Invalid operation in service description: ' + . gettype($operation)); + } + $this->operations[$name] = $operation; + } + } + + // Get all of the additional properties of the service description and store them in a data array + foreach (array_diff(array_keys($config), $defaultKeys) as $key) { + $this->extraData[$key] = $config[$key]; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php new file mode 100644 index 000000000..5983e586b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php @@ -0,0 +1,106 @@ + $op) { + $name = $op['name'] = isset($op['name']) ? $op['name'] : $name; + // Extend other operations + if (!empty($op['extends'])) { + $this->resolveExtension($name, $op, $operations); + } + $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array(); + $operations[$name] = $op; + } + } + + return new ServiceDescription(array( + 'apiVersion' => isset($config['apiVersion']) ? $config['apiVersion'] : null, + 'baseUrl' => isset($config['baseUrl']) ? $config['baseUrl'] : null, + 'description' => isset($config['description']) ? $config['description'] : null, + 'operations' => $operations, + 'models' => isset($config['models']) ? $config['models'] : null + ) + $config); + } + + /** + * @param string $name Name of the operation + * @param array $op Operation value array + * @param array $operations Currently loaded operations + * @throws DescriptionBuilderException when extending a non-existent operation + */ + protected function resolveExtension($name, array &$op, array &$operations) + { + $resolved = array(); + $original = empty($op['parameters']) ? false: $op['parameters']; + $hasClass = !empty($op['class']); + foreach ((array) $op['extends'] as $extendedCommand) { + if (empty($operations[$extendedCommand])) { + throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}"); + } + $toArray = $operations[$extendedCommand]; + $resolved = empty($resolved) + ? $toArray['parameters'] + : array_merge($resolved, $toArray['parameters']); + + $op = $op + $toArray; + if (!$hasClass && isset($toArray['class'])) { + $op['class'] = $toArray['class']; + } + } + $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php new file mode 100644 index 000000000..94ca77da4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php @@ -0,0 +1,28 @@ +getMessage(), $e->getCode(), $e->getPrevious()); + $ce->setSuccessfulRequests($e->getSuccessfulRequests()); + + $alreadyAddedExceptions = array(); + foreach ($e->getFailedRequests() as $request) { + if ($re = $e->getExceptionForFailedRequest($request)) { + $alreadyAddedExceptions[] = $re; + $ce->addFailedRequestWithException($request, $re); + } else { + $ce->addFailedRequest($request); + } + } + + // Add any exceptions that did not map to a request + if (count($alreadyAddedExceptions) < count($e)) { + foreach ($e as $ex) { + if (!in_array($ex, $alreadyAddedExceptions)) { + $ce->add($ex); + } + } + } + + return $ce; + } + + /** + * Get all of the commands in the transfer + * + * @return array + */ + public function getAllCommands() + { + return array_merge($this->successfulCommands, $this->failedCommands); + } + + /** + * Add to the array of successful commands + * + * @param CommandInterface $command Successful command + * + * @return self + */ + public function addSuccessfulCommand(CommandInterface $command) + { + $this->successfulCommands[] = $command; + + return $this; + } + + /** + * Add to the array of failed commands + * + * @param CommandInterface $command Failed command + * + * @return self + */ + public function addFailedCommand(CommandInterface $command) + { + $this->failedCommands[] = $command; + + return $this; + } + + /** + * Get an array of successful commands + * + * @return array + */ + public function getSuccessfulCommands() + { + return $this->successfulCommands; + } + + /** + * Get an array of failed commands + * + * @return array + */ + public function getFailedCommands() + { + return $this->failedCommands; + } + + /** + * Get the Exception that caused the given $command to fail + * + * @param CommandInterface $command Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedCommand(CommandInterface $command) + { + return $this->getExceptionForFailedRequest($command->getRequest()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php new file mode 100644 index 000000000..1407e5687 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php @@ -0,0 +1,7 @@ +invalidCommands = $commands; + parent::__construct( + 'Encountered commands in a batch transfer that use inconsistent clients. The batching ' . + 'strategy you use with a command transfer must divide command batches by client.' + ); + } + + /** + * Get the invalid commands + * + * @return array + */ + public function getCommands() + { + return $this->invalidCommands; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php new file mode 100644 index 000000000..d59ff2185 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php @@ -0,0 +1,9 @@ +errors = $errors; + } + + /** + * Get any validation errors + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php new file mode 100644 index 000000000..21140e772 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php @@ -0,0 +1,37 @@ +canBuild($command)) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + $className = $this->getClassName($command); + + return new $className($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return (bool) $this->getClassName($command); + } + + /** + * Get the name of the class to instantiate for the command + * + * @param CommandInterface $command Command that is associated with the iterator + * + * @return string + */ + abstract protected function getClassName(CommandInterface $command); +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php new file mode 100644 index 000000000..2efc133c6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php @@ -0,0 +1,67 @@ +factories = $factories; + } + + public function build(CommandInterface $command, array $options = array()) + { + if (!($factory = $this->getFactory($command))) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + return $factory->build($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return $this->getFactory($command) !== false; + } + + /** + * Add a factory to the composite factory + * + * @param ResourceIteratorFactoryInterface $factory Factory to add + * + * @return self + */ + public function addFactory(ResourceIteratorFactoryInterface $factory) + { + $this->factories[] = $factory; + + return $this; + } + + /** + * Get the factory that matches the command object + * + * @param CommandInterface $command Command retrieving the iterator for + * + * @return ResourceIteratorFactoryInterface|bool + */ + protected function getFactory(CommandInterface $command) + { + foreach ($this->factories as $factory) { + if ($factory->canBuild($command)) { + return $factory; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php new file mode 100644 index 000000000..c71ca9d85 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php @@ -0,0 +1,34 @@ +map = $map; + } + + public function getClassName(CommandInterface $command) + { + $className = $command->getName(); + + if (isset($this->map[$className])) { + return $this->map[$className]; + } elseif (isset($this->map['*'])) { + // If a wildcard was added, then always use that + return $this->map['*']; + } + + return null; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php new file mode 100644 index 000000000..2322434a5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php @@ -0,0 +1,64 @@ +data = $data; + $this->structure = $structure; + } + + /** + * Get the structure of the model + * + * @return Parameter + */ + public function getStructure() + { + return $this->structure ?: new Parameter(); + } + + /** + * Provides debug information about the model object + * + * @return string + */ + public function __toString() + { + $output = 'Debug output of '; + if ($this->structure) { + $output .= $this->structure->getName() . ' '; + } + $output .= 'model'; + $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n"; + $output .= "Model data\n-----------\n\n"; + $output .= "This data can be retrieved from the model object using the get() method of the model " + . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n"; + $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1); + $output .= implode("\n", $lines); + + if ($this->structure) { + $output .= "\n\nModel structure\n---------------\n\n"; + $output .= "The following JSON document defines how the model was parsed from an HTTP response into the " + . "associative array structure you see above.\n\n"; + $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n"; + } + + return $output . "\n"; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php new file mode 100644 index 000000000..e14152432 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php @@ -0,0 +1,254 @@ +originalCommand = $command; + + // Parse options from the array of options + $this->data = $data; + $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0; + $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false; + } + + /** + * Get all of the resources as an array (Warning: this could issue a large number of requests) + * + * @return array + */ + public function toArray() + { + return iterator_to_array($this, false); + } + + public function setLimit($limit) + { + $this->limit = $limit; + $this->resetState(); + + return $this; + } + + public function setPageSize($pageSize) + { + $this->pageSize = $pageSize; + $this->resetState(); + + return $this; + } + + /** + * Get an option from the iterator + * + * @param string $key Key of the option to retrieve + * + * @return mixed|null Returns NULL if not set or the value if set + */ + public function get($key) + { + return array_key_exists($key, $this->data) ? $this->data[$key] : null; + } + + /** + * Set an option on the iterator + * + * @param string $key Key of the option to set + * @param mixed $value Value to set for the option + * + * @return ResourceIterator + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + public function current() + { + return $this->resources ? current($this->resources) : false; + } + + public function key() + { + return max(0, $this->iteratedCount - 1); + } + + public function count() + { + return $this->retrievedCount; + } + + /** + * Get the total number of requests sent + * + * @return int + */ + public function getRequestCount() + { + return $this->requestCount; + } + + /** + * Rewind the Iterator to the first element and send the original command + */ + public function rewind() + { + // Use the original command + $this->command = clone $this->originalCommand; + $this->resetState(); + $this->next(); + } + + public function valid() + { + return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken) + && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + public function next() + { + $this->iteratedCount++; + + // Check if a new set of resources needs to be retrieved + $sendRequest = false; + if (!$this->resources) { + $sendRequest = true; + } else { + // iterate over the internal array + $current = next($this->resources); + $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + if ($sendRequest) { + + $this->dispatch('resource_iterator.before_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + + // Get a new command object from the original command + $this->command = clone $this->originalCommand; + // Send a request and retrieve the newly loaded resources + $this->resources = $this->sendRequest(); + $this->requestCount++; + + // If no resources were found, then the last request was not needed + // and iteration must stop + if (empty($this->resources)) { + $this->invalid = true; + } else { + // Add to the number of retrieved resources + $this->retrievedCount += count($this->resources); + // Ensure that we rewind to the beginning of the array + reset($this->resources); + } + + $this->dispatch('resource_iterator.after_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + } + } + + /** + * Retrieve the NextToken that can be used in other iterators. + * + * @return string Returns a NextToken + */ + public function getNextToken() + { + return $this->nextToken; + } + + /** + * Returns the value that should be specified for the page size for a request that will maintain any hard limits, + * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit + * + * @return int Returns the page size of the next request. + */ + protected function calculatePageSize() + { + if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) { + return 1 + ($this->limit - $this->iteratedCount); + } + + return (int) $this->pageSize; + } + + /** + * Reset the internal state of the iterator without triggering a rewind() + */ + protected function resetState() + { + $this->iteratedCount = 0; + $this->retrievedCount = 0; + $this->nextToken = false; + $this->resources = null; + $this->invalid = false; + } + + /** + * Send a request to retrieve the next page of results. Hook for subclasses to implement. + * + * @return array Returns the newly loaded resources + */ + abstract protected function sendRequest(); +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php new file mode 100644 index 000000000..6aa36153f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php @@ -0,0 +1,111 @@ +iterator = $iterator; + $this->callback = $callback; + Version::warn(__CLASS__ . ' is deprecated'); + } + + /** + * Apply the callback to the contents of the resource iterator + * + * @param int $perBatch The number of records to group per batch transfer + * + * @return int Returns the number of iterated resources + */ + public function apply($perBatch = 50) + { + $this->iterated = $this->batches = $batches = 0; + $that = $this; + $it = $this->iterator; + $callback = $this->callback; + + $batch = BatchBuilder::factory() + ->createBatchesWith(new BatchSizeDivisor($perBatch)) + ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) { + $batches++; + $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch)); + call_user_func_array($callback, array($it, $batch)); + $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch)); + })) + ->autoFlushAt($perBatch) + ->build(); + + $this->dispatch('iterator_batch.created_batch', array('batch' => $batch)); + + foreach ($this->iterator as $resource) { + $this->iterated++; + $batch->add($resource); + } + + $batch->flush(); + $this->batches = $batches; + + return $this->iterated; + } + + /** + * Get the total number of batches sent + * + * @return int + */ + public function getBatchCount() + { + return $this->batches; + } + + /** + * Get the total number of iterated resources + * + * @return int + */ + public function getIteratedCount() + { + return $this->iterated; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php new file mode 100644 index 000000000..2fd998071 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php @@ -0,0 +1,60 @@ + AbcFoo). + */ +class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory +{ + /** @var array List of namespaces used to look for classes */ + protected $namespaces; + + /** @var InflectorInterface Inflector used to determine class names */ + protected $inflector; + + /** + * @param string|array $namespaces List of namespaces for iterator objects + * @param InflectorInterface $inflector Inflector used to resolve class names + */ + public function __construct($namespaces = array(), InflectorInterface $inflector = null) + { + $this->namespaces = (array) $namespaces; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + /** + * Registers a namespace to check for Iterators + * + * @param string $namespace Namespace which contains Iterator classes + * + * @return self + */ + public function registerNamespace($namespace) + { + array_unshift($this->namespaces, $namespace); + + return $this; + } + + protected function getClassName(CommandInterface $command) + { + $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator'; + + // Determine the name of the class to load + foreach ($this->namespaces as $namespace) { + $potentialClassName = $namespace . '\\' . $iteratorName; + if (class_exists($potentialClassName)) { + return $potentialClassName; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php new file mode 100644 index 000000000..8b4e8dbe0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php @@ -0,0 +1,30 @@ +=5.3.2", + "guzzle/cache": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Service": "" } + }, + "target-dir": "Guzzle/Service", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php new file mode 100644 index 000000000..d115fd890 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php @@ -0,0 +1,284 @@ +contextOptions = stream_context_get_options($context); + $this->context = $context; + } elseif (is_array($context) || !$context) { + $this->contextOptions = $context; + $this->createContext($params); + } elseif ($context) { + throw new InvalidArgumentException('$context must be an array or resource'); + } + + // Dispatch the before send event + $request->dispatch('request.before_send', array( + 'request' => $request, + 'context' => $this->context, + 'context_options' => $this->contextOptions + )); + + $this->setUrl($request); + $this->addDefaultContextOptions($request); + $this->addSslOptions($request); + $this->addBodyOptions($request); + $this->addProxyOptions($request); + + // Create the file handle but silence errors + return $this->createStream($params) + ->setCustomData('request', $request) + ->setCustomData('response_headers', $this->getLastResponseHeaders()); + } + + /** + * Set an option on the context and the internal options array + * + * @param string $wrapper Stream wrapper name of http + * @param string $name Context name + * @param mixed $value Context value + * @param bool $overwrite Set to true to overwrite an existing value + */ + protected function setContextValue($wrapper, $name, $value, $overwrite = false) + { + if (!isset($this->contextOptions[$wrapper])) { + $this->contextOptions[$wrapper] = array($name => $value); + } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) { + return; + } + $this->contextOptions[$wrapper][$name] = $value; + stream_context_set_option($this->context, $wrapper, $name, $value); + } + + /** + * Create a stream context + * + * @param array $params Parameter array + */ + protected function createContext(array $params) + { + $options = $this->contextOptions; + $this->context = $this->createResource(function () use ($params, $options) { + return stream_context_create($options, $params); + }); + } + + /** + * Get the last response headers received by the HTTP request + * + * @return array + */ + public function getLastResponseHeaders() + { + return $this->lastResponseHeaders; + } + + /** + * Adds the default context options to the stream context options + * + * @param RequestInterface $request Request + */ + protected function addDefaultContextOptions(RequestInterface $request) + { + $this->setContextValue('http', 'method', $request->getMethod()); + $headers = $request->getHeaderLines(); + + // "Connection: close" is required to get streams to work in HTTP 1.1 + if (!$request->hasHeader('Connection')) { + $headers[] = 'Connection: close'; + } + + $this->setContextValue('http', 'header', $headers); + $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion()); + $this->setContextValue('http', 'ignore_errors', true); + } + + /** + * Set the URL to use with the factory + * + * @param RequestInterface $request Request that owns the URL + */ + protected function setUrl(RequestInterface $request) + { + $this->url = $request->getUrl(true); + + // Check for basic Auth username + if ($request->getUsername()) { + $this->url->setUsername($request->getUsername()); + } + + // Check for basic Auth password + if ($request->getPassword()) { + $this->url->setPassword($request->getPassword()); + } + } + + /** + * Add SSL options to the stream context + * + * @param RequestInterface $request Request + */ + protected function addSslOptions(RequestInterface $request) + { + if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) { + $this->setContextValue('ssl', 'verify_peer', true, true); + if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) { + $this->setContextValue('ssl', 'cafile', $cafile, true); + } + } else { + $this->setContextValue('ssl', 'verify_peer', false, true); + } + } + + /** + * Add body (content) specific options to the context options + * + * @param RequestInterface $request + */ + protected function addBodyOptions(RequestInterface $request) + { + // Add the content for the request if needed + if (!($request instanceof EntityEnclosingRequestInterface)) { + return; + } + + if (count($request->getPostFields())) { + $this->setContextValue('http', 'content', (string) $request->getPostFields(), true); + } elseif ($request->getBody()) { + $this->setContextValue('http', 'content', (string) $request->getBody(), true); + } + + // Always ensure a content-length header is sent + if (isset($this->contextOptions['http']['content'])) { + $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array(); + $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']); + $this->setContextValue('http', 'header', $headers, true); + } + } + + /** + * Add proxy parameters to the context if needed + * + * @param RequestInterface $request Request + */ + protected function addProxyOptions(RequestInterface $request) + { + if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) { + $this->setContextValue('http', 'proxy', $proxy); + } + } + + /** + * Create the stream for the request with the context options + * + * @param array $params Parameters of the stream + * + * @return StreamInterface + */ + protected function createStream(array $params) + { + $http_response_header = null; + $url = $this->url; + $context = $this->context; + $fp = $this->createResource(function () use ($context, $url, &$http_response_header) { + return fopen((string) $url, 'r', false, $context); + }); + + // Determine the class to instantiate + $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream'; + + /** @var $stream StreamInterface */ + $stream = new $className($fp); + + // Track the response headers of the request + if (isset($http_response_header)) { + $this->lastResponseHeaders = $http_response_header; + $this->processResponseHeaders($stream); + } + + return $stream; + } + + /** + * Process response headers + * + * @param StreamInterface $stream + */ + protected function processResponseHeaders(StreamInterface $stream) + { + // Set the size on the stream if it was returned in the response + foreach ($this->lastResponseHeaders as $header) { + if ((stripos($header, 'Content-Length:')) === 0) { + $stream->setSize(trim(substr($header, 15))); + } + } + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Closure to invoke that must return a valid resource + * + * @return resource + * @throws RuntimeException on error + */ + protected function createResource($callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = array( + 'message' => $msg, + 'file' => $file, + 'line' => $line + ); + return true; + }); + $resource = call_user_func($callback); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource. '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new RuntimeException(trim($message)); + } + + return $resource; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php new file mode 100644 index 000000000..12bed268d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php @@ -0,0 +1,289 @@ + array( + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true + ), + 'write' => array( + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ) + ); + + /** + * @param resource $stream Stream resource to wrap + * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream. + * + * @throws InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $size = null) + { + $this->setStream($stream, $size); + } + + /** + * Closes the stream when the helper is destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { + return ''; + } + + $originalPos = $this->ftell(); + $body = stream_get_contents($this->stream, -1, 0); + $this->seek($originalPos); + + return $body; + } + + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->cache[self::IS_READABLE] = false; + $this->cache[self::IS_WRITABLE] = false; + } + + /** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return bool|string Returns false on failure or a hash string on success + */ + public static function getHash(StreamInterface $stream, $algo, $rawOutput = false) + { + $pos = $stream->ftell(); + if (!$stream->seek(0)) { + return false; + } + + $ctx = hash_init($algo); + while (!$stream->feof()) { + hash_update($ctx, $stream->read(8192)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; + } + + public function getMetaData($key = null) + { + $meta = stream_get_meta_data($this->stream); + + return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null); + } + + public function getStream() + { + return $this->stream; + } + + public function setStream($stream, $size = null) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Stream must be a resource'); + } + + $this->size = $size; + $this->stream = $stream; + $this->rebuildCache(); + + return $this; + } + + public function detachStream() + { + $this->stream = null; + + return $this; + } + + public function getWrapper() + { + return $this->cache[self::WRAPPER_TYPE]; + } + + public function getWrapperData() + { + return $this->getMetaData('wrapper_data') ?: array(); + } + + public function getStreamType() + { + return $this->cache[self::STREAM_TYPE]; + } + + public function getUri() + { + return $this->cache['uri']; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + // If the stream is a file based stream and local, then use fstat + clearstatcache(true, $this->cache['uri']); + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) { + // Only get the size based on the content if the the stream is readable and seekable + $pos = $this->ftell(); + $this->size = strlen((string) $this); + $this->seek($pos); + return $this->size; + } + + return false; + } + + public function isReadable() + { + return $this->cache[self::IS_READABLE]; + } + + public function isRepeatable() + { + return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]; + } + + public function isWritable() + { + return $this->cache[self::IS_WRITABLE]; + } + + public function isConsumed() + { + return feof($this->stream); + } + + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->cache[self::IS_LOCAL]; + } + + public function isSeekable() + { + return $this->cache[self::SEEKABLE]; + } + + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false; + } + + public function read($length) + { + return fread($this->stream, $length); + } + + public function write($string) + { + // We can't know the size after writing anything + $this->size = null; + + return fwrite($this->stream, $string); + } + + public function ftell() + { + return ftell($this->stream); + } + + public function rewind() + { + return $this->seek(0); + } + + public function readLine($maxLength = null) + { + if (!$this->cache[self::IS_READABLE]) { + return false; + } else { + return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream()); + } + } + + public function setCustomData($key, $value) + { + $this->customData[$key] = $value; + + return $this; + } + + public function getCustomData($key) + { + return isset($this->customData[$key]) ? $this->customData[$key] : null; + } + + /** + * Reprocess stream metadata + */ + protected function rebuildCache() + { + $this->cache = stream_get_meta_data($this->stream); + $this->cache[self::IS_LOCAL] = stream_is_local($this->stream); + $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]); + $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php new file mode 100644 index 000000000..6d7dc3761 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php @@ -0,0 +1,218 @@ +=5.3.2", + "guzzle/common": "self.version" + }, + "suggest": { + "guzzle/http": "To convert Guzzle request objects to PHP streams" + }, + "autoload": { + "psr-0": { "Guzzle\\Stream": "" } + }, + "target-dir": "Guzzle/Stream", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php new file mode 100644 index 000000000..951738d2a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php @@ -0,0 +1,33 @@ +getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + + $decoratorA = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($batch)) + ->getMockForAbstractClass(); + + $decoratorB = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($decoratorA)) + ->getMockForAbstractClass(); + + $decoratorA->add('foo'); + $this->assertFalse($decoratorB->isEmpty()); + $this->assertFalse($batch->isEmpty()); + $this->assertEquals(array($decoratorB, $decoratorA), $decoratorB->getDecorators()); + $this->assertEquals(array(), $decoratorB->flush()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php new file mode 100644 index 000000000..4da09d30e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php @@ -0,0 +1,86 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + } + + private function getMockBatchBuilder() + { + return BatchBuilder::factory() + ->transferWith($this->getMockTransfer()) + ->createBatchesWith($this->getMockDivisor()); + } + + public function testFactoryCreatesInstance() + { + $builder = BatchBuilder::factory(); + $this->assertInstanceOf('Guzzle\Batch\BatchBuilder', $builder); + } + + public function testAddsAutoFlush() + { + $batch = $this->getMockBatchBuilder()->autoFlushAt(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\FlushingBatch', $batch); + } + + public function testAddsExceptionBuffering() + { + $batch = $this->getMockBatchBuilder()->bufferExceptions()->build(); + $this->assertInstanceOf('Guzzle\Batch\ExceptionBufferingBatch', $batch); + } + + public function testAddHistory() + { + $batch = $this->getMockBatchBuilder()->keepHistory()->build(); + $this->assertInstanceOf('Guzzle\Batch\HistoryBatch', $batch); + } + + public function testAddsNotify() + { + $batch = $this->getMockBatchBuilder()->notify(function() {})->build(); + $this->assertInstanceOf('Guzzle\Batch\NotifyingBatch', $batch); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testTransferStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->createBatchesWith($this->getMockDivisor())->build(); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testDivisorStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->transferWith($this->getMockTransfer())->build(); + } + + public function testTransfersRequests() + { + $batch = BatchBuilder::factory()->transferRequests(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\BatchRequestTransfer', $this->readAttribute($batch, 'transferStrategy')); + } + + public function testTransfersCommands() + { + $batch = BatchBuilder::factory()->transferCommands(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\BatchCommandTransfer', $this->readAttribute($batch, 'transferStrategy')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php new file mode 100644 index 000000000..753db7dab --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php @@ -0,0 +1,36 @@ +createBatches($queue); + $this->assertEquals(array(array('foo'), array('baz')), $batches); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php new file mode 100644 index 000000000..6ba7ae052 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php @@ -0,0 +1,52 @@ +itemsTransferred = null; + $itemsTransferred =& $this->itemsTransferred; + + $this->transferStrategy = new BatchClosureTransfer(function (array $batch) use (&$itemsTransferred) { + $itemsTransferred = $batch; + return; + }); + } + + public function testTransfersBatch() + { + $batchedItems = array('foo', 'bar', 'baz'); + $this->transferStrategy->transfer($batchedItems); + + $this->assertEquals($batchedItems, $this->itemsTransferred); + } + + public function testTransferBailsOnEmptyBatch() + { + $batchedItems = array(); + $this->transferStrategy->transfer($batchedItems); + + $this->assertNull($this->itemsTransferred); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsCallable() + { + $foo = new BatchClosureTransfer('uh oh!'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php new file mode 100644 index 000000000..a04efabbb --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php @@ -0,0 +1,83 @@ + $command) { + if ($i % 2) { + $command->setClient($client1); + } else { + $command->setClient($client2); + } + $queue[] = $command; + } + + $batch = new BatchCommandTransfer(2); + $this->assertEquals(array( + array($commands[0], $commands[2]), + array($commands[4]), + array($commands[1], $commands[3]) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreCommands() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchCommandTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = $this->getMockBuilder('Guzzle\Service\Client') + ->setMethods(array('send')) + ->getMock(); + $client->expects($this->once()) + ->method('send'); + $command = new Mc(); + $command->setClient($client); + $batch = new BatchCommandTransfer(2); + $batch->transfer(array($command)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchCommandTransfer(2); + $batch->transfer(array()); + } + + /** + * @expectedException Guzzle\Service\Exception\InconsistentClientTransferException + */ + public function testEnsuresAllCommandsUseTheSameClient() + { + $batch = new BatchCommandTransfer(2); + $client1 = new Client('http://www.example.com'); + $client2 = new Client('http://www.example.com'); + $command1 = new Mc(); + $command1->setClient($client1); + $command2 = new Mc(); + $command2->setClient($client2); + $batch->transfer(array($command1, $command2)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php new file mode 100644 index 000000000..dec7bd55e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php @@ -0,0 +1,80 @@ +setCurlMulti(new CurlMulti()); + + $client2 = new Client('http://www.example.com'); + $client2->setCurlMulti(new CurlMulti()); + + $request1 = $client1->get(); + $request2 = $client2->get(); + $request3 = $client1->get(); + $request4 = $client2->get(); + $request5 = $client1->get(); + + $queue = new \SplQueue(); + $queue[] = $request1; + $queue[] = $request2; + $queue[] = $request3; + $queue[] = $request4; + $queue[] = $request5; + + $batch = new BatchRequestTransfer(2); + $this->assertEquals(array( + array($request1, $request3), + array($request3), + array($request2, $request4) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreRequests() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchRequestTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = new Client('http://127.0.0.1:123'); + $request = $client->get(); + // For some reason... PHP unit clones the request, which emits a request.clone event. This causes the + // 'sorted' property of the event dispatcher to contain an array in the cloned request that is not present in + // the original. + $request->dispatch('request.clone'); + + $multi = $this->getMock('Guzzle\Http\Curl\CurlMultiInterface'); + $client->setCurlMulti($multi); + $multi->expects($this->once()) + ->method('add') + ->with($request); + $multi->expects($this->once()) + ->method('send'); + + $batch = new BatchRequestTransfer(2); + $batch->transfer(array($request)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchRequestTransfer(2); + $batch->transfer(array()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php new file mode 100644 index 000000000..5542228fd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php @@ -0,0 +1,24 @@ +assertEquals(3, $d->getSize()); + $d->setSize(2); + $batches = $d->createBatches($queue); + $this->assertEquals(array(array('foo', 'baz'), array('bar')), $batches); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php new file mode 100644 index 000000000..296f57aef --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php @@ -0,0 +1,91 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + } + + public function testAddsItemsToQueue() + { + $batch = new Batch($this->getMockTransfer(), $this->getMockDivisor()); + $this->assertSame($batch, $batch->add('foo')); + $this->assertEquals(1, count($batch)); + } + + public function testFlushReturnsItems() + { + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer'); + + $divisor = $this->getMockDivisor(); + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnValue(array(array('foo', 'baz'), array('bar')))); + + $batch = new Batch($transfer, $divisor); + + $batch->add('foo')->add('baz')->add('bar'); + $items = $batch->flush(); + + $this->assertEquals(array('foo', 'baz', 'bar'), $items); + } + + public function testThrowsExceptionContainingTheFailedBatch() + { + $called = 0; + $originalException = new \Exception('Foo!'); + + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer') + ->will($this->returnCallback(function () use (&$called, $originalException) { + if (++$called == 2) { + throw $originalException; + } + })); + + $divisor = $this->getMockDivisor(); + $batch = new Batch($transfer, $divisor); + + // PHPunit clones objects before passing them to a callback. + // Horrible hack to get around this! + $queue = $this->readAttribute($batch, 'queue'); + + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnCallback(function ($batch) use ($queue) { + foreach ($queue as $item) { + $items[] = $item; + } + return array_chunk($items, 2); + })); + + $batch->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertFalse($batch->isEmpty()); + + try { + $items = $batch->flush(); + $this->fail('Expected exception'); + } catch (BatchTransferException $e) { + $this->assertEquals($originalException, $e->getPrevious()); + $this->assertEquals(array('bar', 'bee'), array_values($e->getBatch())); + $this->assertEquals(1, count($batch)); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php new file mode 100644 index 000000000..fd810b11f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php @@ -0,0 +1,45 @@ +getMockBuilder('Guzzle\Batch\BatchTransferInterface') + ->setMethods(array('transfer')) + ->getMock(); + + $d = new BatchSizeDivisor(1); + $batch = new Batch($t, $d); + + $called = 0; + $t->expects($this->exactly(3)) + ->method('transfer') + ->will($this->returnCallback(function ($batch) use (&$called) { + if (++$called === 2) { + throw new \Exception('Foo'); + } + })); + + $decorator = new ExceptionBufferingBatch($batch); + $decorator->add('foo')->add('baz')->add('bar'); + $result = $decorator->flush(); + + $e = $decorator->getExceptions(); + $this->assertEquals(1, count($e)); + $this->assertEquals(array('baz'), $e[0]->getBatch()); + + $decorator->clearExceptions(); + $this->assertEquals(0, count($decorator->getExceptions())); + + $this->assertEquals(array('foo', 'bar'), $result); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php new file mode 100644 index 000000000..9b37a485f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php @@ -0,0 +1,40 @@ +getMock('Guzzle\Batch\BatchTransferInterface', array('transfer')); + $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface', array('createBatches')); + + $batch = new Batch($t, $d); + $queue = $this->readAttribute($batch, 'queue'); + + $d->expects($this->exactly(2)) + ->method('createBatches') + ->will($this->returnCallback(function () use ($queue) { + $items = array(); + foreach ($queue as $item) { + $items[] = $item; + } + return array($items); + })); + + $t->expects($this->exactly(2)) + ->method('transfer'); + + $flush = new FlushingBatch($batch, 3); + $this->assertEquals(3, $flush->getThreshold()); + $flush->setThreshold(2); + $flush->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertEquals(1, count($flush)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php new file mode 100644 index 000000000..60d6f951a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php @@ -0,0 +1,26 @@ +getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + + $history = new HistoryBatch($batch); + $history->add('foo')->add('baz'); + $this->assertEquals(array('foo', 'baz'), $history->getHistory()); + $history->clearHistory(); + $this->assertEquals(array(), $history->getHistory()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php new file mode 100644 index 000000000..69a89007a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Batch\Batch', array('flush'), array( + $this->getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + )); + + $batch->expects($this->once()) + ->method('flush') + ->will($this->returnValue(array('foo', 'baz'))); + + $data = array(); + $decorator = new NotifyingBatch($batch, function ($batch) use (&$data) { + $data[] = $batch; + }); + + $decorator->add('foo')->add('baz'); + $decorator->flush(); + $this->assertEquals(array(array('foo', 'baz')), $data); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsValid() + { + $batch = new Batch( + $this->getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + $decorator = new NotifyingBatch($batch, 'foo'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php new file mode 100644 index 000000000..c4140a91d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php @@ -0,0 +1,64 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresConfigIsObject() + { + CacheAdapterFactory::fromCache(array()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresKnownType() + { + CacheAdapterFactory::fromCache(new \stdClass()); + } + + public function cacheProvider() + { + return array( + array(new DoctrineCacheAdapter(new ArrayCache()), 'Guzzle\Cache\DoctrineCacheAdapter'), + array(new ArrayCache(), 'Guzzle\Cache\DoctrineCacheAdapter'), + array(StorageFactory::factory(array('adapter' => 'memory')), 'Guzzle\Cache\Zf2CacheAdapter'), + ); + } + + /** + * @dataProvider cacheProvider + */ + public function testCreatesNullCacheAdapterByDefault($cache, $type) + { + $adapter = CacheAdapterFactory::fromCache($cache); + $this->assertInstanceOf($type, $adapter); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php new file mode 100644 index 000000000..3e30dddc5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php @@ -0,0 +1,68 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + public function testGetCacheObject() + { + $this->assertEquals($this->cache, $this->adapter->getCacheObject()); + } + + public function testSave() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + } + + public function testFetch() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testContains() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->contains('test')); + } + + public function testDelete() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->delete('test')); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php new file mode 100644 index 000000000..12de65b5a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php @@ -0,0 +1,94 @@ +callables = array( + 'contains' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data); + }, + 'delete' => function($id, $options = array()) use ($that) { + unset($that->data[$id]); + return true; + }, + 'fetch' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data) ? $that->data[$id] : null; + }, + 'save' => function($id, $data, $lifeTime, $options = array()) use ($that) { + $that->data[$id] = $data; + return true; + } + ); + + $this->adapter = new ClosureCacheAdapter($this->callables); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->cache = null; + $this->callables = null; + parent::tearDown(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testEnsuresCallablesArePresent() + { + $callables = $this->callables; + unset($callables['delete']); + $cache = new ClosureCacheAdapter($callables); + } + + public function testAllCallablesMustBePresent() + { + $cache = new ClosureCacheAdapter($this->callables); + } + + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php new file mode 100644 index 000000000..e05df3f78 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php @@ -0,0 +1,20 @@ +assertEquals(false, $c->contains('foo')); + $this->assertEquals(true, $c->delete('foo')); + $this->assertEquals(false, $c->fetch('foo')); + $this->assertEquals(true, $c->save('foo', 'bar')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php new file mode 100644 index 000000000..9077c12d3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php @@ -0,0 +1,58 @@ +cache = StorageFactory::factory(array( + 'adapter' => 'memory' + )); + $this->adapter = new Zf2CacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php new file mode 100644 index 000000000..19d12e6f6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php @@ -0,0 +1,63 @@ +assertEquals(array(), AbstractHasDispatcher::getAllEvents()); + } + + public function testAllowsDispatcherToBeInjected() + { + $d = new EventDispatcher(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertSame($mock, $mock->setEventDispatcher($d)); + $this->assertSame($d, $mock->getEventDispatcher()); + } + + public function testCreatesDefaultEventDispatcherIfNeeded() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventDispatcher', $mock->getEventDispatcher()); + } + + public function testHelperDispatchesEvents() + { + $data = array(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $mock->getEventDispatcher()->addListener('test', function(Event $e) use (&$data) { + $data = $e->getIterator()->getArrayCopy(); + }); + $mock->dispatch('test', array( + 'param' => 'abc' + )); + $this->assertEquals(array( + 'param' => 'abc', + ), $data); + } + + public function testHelperAttachesSubscribers() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $subscriber = $this->getMockForAbstractClass('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') + ->setMethods(array('addSubscriber')) + ->getMock(); + + $dispatcher->expects($this->once()) + ->method('addSubscriber'); + + $mock->setEventDispatcher($dispatcher); + $mock->addSubscriber($subscriber); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php new file mode 100644 index 000000000..0648a02b8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php @@ -0,0 +1,529 @@ +coll = new Collection(); + } + + public function testConstructorCanBeCalledWithNoParams() + { + $this->coll = new Collection(); + $p = $this->coll->getAll(); + $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); + } + + public function testConstructorCanBeCalledWithParams() + { + $testData = array( + 'test' => 'value', + 'test_2' => 'value2' + ); + $this->coll = new Collection($testData); + $this->assertEquals($this->coll->getAll(), $testData, '-> getAll() must return the data passed in the constructor'); + $this->assertEquals($this->coll->getAll(), $this->coll->toArray()); + } + + public function testImplementsIteratorAggregate() + { + $this->coll->set('key', 'value'); + $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); + $this->assertEquals(1, count($this->coll)); + $total = 0; + foreach ($this->coll as $key => $value) { + $this->assertEquals('key', $key); + $this->assertEquals('value', $value); + $total++; + } + $this->assertEquals(1, $total); + } + + public function testCanAddValuesToExistingKeysByUsingArray() + { + $this->coll->add('test', 'value1'); + $this->assertEquals($this->coll->getAll(), array('test' => 'value1')); + $this->coll->add('test', 'value2'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2'))); + $this->coll->add('test', 'value3'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2', 'value3'))); + } + + public function testHandlesMergingInDisparateDataSources() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + $this->coll->merge($params); + $this->assertEquals($this->coll->getAll(), $params); + + // Pass the same object to itself + $this->assertEquals($this->coll->merge($this->coll), $this->coll); + } + + public function testCanClearAllDataOrSpecificKeys() + { + $this->coll->merge(array( + 'test' => 'value1', + 'test2' => 'value2' + )); + + // Clear a specific parameter by name + $this->coll->remove('test'); + + $this->assertEquals($this->coll->getAll(), array( + 'test2' => 'value2' + )); + + // Clear all parameters + $this->coll->clear(); + + $this->assertEquals($this->coll->getAll(), array()); + } + + public function testGetsValuesByKey() + { + $this->assertNull($this->coll->get('test')); + $this->coll->add('test', 'value'); + $this->assertEquals('value', $this->coll->get('test')); + $this->coll->set('test2', 'v2'); + $this->coll->set('test3', 'v3'); + $this->assertEquals(array( + 'test' => 'value', + 'test2' => 'v2' + ), $this->coll->getAll(array('test', 'test2'))); + } + + public function testProvidesKeys() + { + $this->assertEquals(array(), $this->coll->getKeys()); + $this->coll->merge(array( + 'test1' => 'value1', + 'test2' => 'value2' + )); + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + // Returns the cached array previously returned + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + $this->coll->remove('test1'); + $this->assertEquals(array('test2'), $this->coll->getKeys()); + $this->coll->add('test3', 'value3'); + $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); + } + + public function testChecksIfHasKey() + { + $this->assertFalse($this->coll->hasKey('test')); + $this->coll->add('test', 'value'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->coll->add('test2', 'value2'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->assertEquals(true, $this->coll->hasKey('test2')); + $this->assertFalse($this->coll->hasKey('testing')); + $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); + } + + public function testChecksIfHasValue() + { + $this->assertFalse($this->coll->hasValue('value')); + $this->coll->add('test', 'value'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->coll->add('test2', 'value2'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->assertEquals('test2', $this->coll->hasValue('value2')); + $this->assertFalse($this->coll->hasValue('val')); + } + + public function testCanGetAllValuesByArray() + { + $this->coll->add('foo', 'bar'); + $this->coll->add('tEsT', 'value'); + $this->coll->add('tesTing', 'v2'); + $this->coll->add('key', 'v3'); + $this->assertNull($this->coll->get('test')); + $this->assertEquals(array( + 'foo' => 'bar', + 'tEsT' => 'value', + 'tesTing' => 'v2' + ), $this->coll->getAll(array( + 'foo', 'tesTing', 'tEsT' + ))); + } + + public function testImplementsCount() + { + $data = new Collection(); + $this->assertEquals(0, $data->count()); + $data->add('key', 'value'); + $this->assertEquals(1, count($data)); + $data->add('key', 'value2'); + $this->assertEquals(1, count($data)); + $data->add('key_2', 'value3'); + $this->assertEquals(2, count($data)); + } + + public function testAddParamsByMerging() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + + // Add some parameters + $this->coll->merge($params); + + // Add more parameters by merging them in + $this->coll->merge(array( + 'test' => 'another', + 'different_key' => 'new value' + )); + + $this->assertEquals(array( + 'test' => array('value1', 'another'), + 'test2' => 'value2', + 'test3' => array('value3', 'value4'), + 'different_key' => 'new value' + ), $this->coll->getAll()); + } + + public function testAllowsFunctionalFilter() + { + $this->coll->merge(array( + 'fruit' => 'apple', + 'number' => 'ten', + 'prepositions' => array('about', 'above', 'across', 'after'), + 'same_number' => 'ten' + )); + + $filtered = $this->coll->filter(function($key, $value) { + return $value == 'ten'; + }); + + $this->assertNotEquals($filtered, $this->coll); + + $this->assertEquals(array( + 'number' => 'ten', + 'same_number' => 'ten' + ), $filtered->getAll()); + } + + public function testAllowsFunctionalMapping() + { + $this->coll->merge(array( + 'number_1' => 1, + 'number_2' => 2, + 'number_3' => 3 + )); + + $mapped = $this->coll->map(function($key, $value) { + return $value * $value; + }); + + $this->assertNotEquals($mapped, $this->coll); + + $this->assertEquals(array( + 'number_1' => 1, + 'number_2' => 4, + 'number_3' => 9 + ), $mapped->getAll()); + } + + public function testImplementsArrayAccess() + { + $this->coll->merge(array( + 'k1' => 'v1', + 'k2' => 'v2' + )); + + $this->assertTrue($this->coll->offsetExists('k1')); + $this->assertFalse($this->coll->offsetExists('Krull')); + + $this->coll->offsetSet('k3', 'v3'); + $this->assertEquals('v3', $this->coll->offsetGet('k3')); + $this->assertEquals('v3', $this->coll->get('k3')); + + $this->coll->offsetUnset('k1'); + $this->assertFalse($this->coll->offsetExists('k1')); + } + + public function testUsesStaticWhenCreatingNew() + { + $qs = new QueryString(array( + 'a' => 'b', + 'c' => 'd' + )); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->map(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->map(function($a, $b) {}, array(), false)); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->filter(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->filter(function($a, $b) {}, false)); + } + + public function testCanReplaceAllData() + { + $this->assertSame($this->coll, $this->coll->replace(array( + 'a' => '123' + ))); + + $this->assertEquals(array( + 'a' => '123' + ), $this->coll->getAll()); + } + + public function dataProvider() + { + return array( + array('this_is_a_test', '{a}_is_a_{b}', array( + 'a' => 'this', + 'b' => 'test' + )), + array('this_is_a_test', '{abc}_is_a_{0}', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', '{abc}_is_a_{0}', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', 'this_is_a_test', array( + 'abc' => 'this' + )), + array('{abc}_is_{not_found}a_{0}', '{abc}_is_{not_found}a_{0}', array()) + ); + } + + /** + * @dataProvider dataProvider + */ + public function testInjectsConfigData($output, $input, $config) + { + $collection = new Collection($config); + $this->assertEquals($output, $collection->inject($input)); + } + + public function testCanSearchByKey() + { + $collection = new Collection(array( + 'foo' => 'bar', + 'BaZ' => 'pho' + )); + + $this->assertEquals('foo', $collection->keySearch('FOO')); + $this->assertEquals('BaZ', $collection->keySearch('baz')); + $this->assertEquals(false, $collection->keySearch('Bar')); + } + + public function testPreparesFromConfig() + { + $c = Collection::fromConfig(array( + 'a' => '123', + 'base_url' => 'http://www.test.com/' + ), array( + 'a' => 'xyz', + 'b' => 'lol' + ), array('a')); + + $this->assertInstanceOf('Guzzle\Common\Collection', $c); + $this->assertEquals(array( + 'a' => '123', + 'b' => 'lol', + 'base_url' => 'http://www.test.com/' + ), $c->getAll()); + + try { + $c = Collection::fromConfig(array(), array(), array('a')); + $this->fail('Exception not throw when missing config'); + } catch (InvalidArgumentException $e) { + } + } + + function falseyDataProvider() + { + return array( + array(false, false), + array(null, null), + array('', ''), + array(array(), array()), + array(0, 0), + ); + } + + /** + * @dataProvider falseyDataProvider + */ + public function testReturnsCorrectData($a, $b) + { + $c = new Collection(array('value' => $a)); + $this->assertSame($b, $c->get('value')); + } + + public function testRetrievesNestedKeysUsingPath() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar' + ) + ) + ); + $collection = new Collection($data); + $this->assertEquals('bar', $collection->getPath('foo')); + $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); + $this->assertNull($collection->getPath('wewewf')); + $this->assertNull($collection->getPath('baz/mesa/jar/jar')); + } + + public function testFalseyKeysStillDescend() + { + $collection = new Collection(array( + '0' => array( + 'a' => 'jar' + ), + 1 => 'other' + )); + $this->assertEquals('jar', $collection->getPath('0/a')); + $this->assertEquals('other', $collection->getPath('1')); + } + + public function getPathProvider() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c') + ), + 'bar' => array( + 'baz' => 'bam', + 'array' => array('d', 'e', 'f') + ) + ), + 'bam' => array( + array('foo' => 1), + array('foo' => 2), + array('array' => array('h', 'i')) + ) + ); + $c = new Collection($data); + + return array( + // Simple path selectors + array($c, 'foo', 'bar'), + array($c, 'baz', $data['baz']), + array($c, 'bam', $data['bam']), + array($c, 'baz/mesa', $data['baz']['mesa']), + array($c, 'baz/mesa/jar', 'jar'), + // Merge everything two levels under baz + array($c, 'baz/*', array( + 'jar' => 'jar', + 'array' => array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array']), + 'baz' => 'bam' + )), + // Does not barf on missing keys + array($c, 'fefwfw', null), + // Does not barf when a wildcard does not resolve correctly + array($c, '*/*/*/*/*/wefwfe', array()), + // Allows custom separator + array($c, '*|mesa', $data['baz']['mesa'], '|'), + // Merge all 'array' keys two levels under baz (the trailing * does not hurt the results) + array($c, 'baz/*/array/*', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), + // Merge all 'array' keys two levels under baz + array($c, 'baz/*/array', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), + array($c, 'baz/mesa/array', $data['baz']['mesa']['array']), + // Having a trailing * does not hurt the results + array($c, 'baz/mesa/array/*', $data['baz']['mesa']['array']), + // Merge of anything one level deep + array($c, '*', array_merge(array('bar'), $data['baz'], $data['bam'])), + // Funky merge of anything two levels deep + array($c, '*/*', array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'), + 'baz' => 'bam', + 'foo' => array(1, 2) + )), + // Funky merge of all 'array' keys that are two levels deep + array($c, '*/*/array', array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i')) + ); + } + + /** + * @dataProvider getPathProvider + */ + public function testGetPath(Collection $c, $path, $expected, $separator = '/') + { + $this->assertEquals($expected, $c->getPath($path, $separator)); + } + + public function testOverridesSettings() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $c->overwriteWith(array('foo' => 10, 'bar' => 300)); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testOverwriteWithCollection() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testOverwriteWithTraversable() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b->getIterator()); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testCanSetNestedPathValueThatDoesNotExist() + { + $c = new Collection(array()); + $c->setPath('foo/bar/baz/123', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); + } + + public function testCanSetNestedPathValueThatExists() + { + $c = new Collection(array('foo' => array('bar' => 'test'))); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testVerifiesNestedPathIsValidAtExactLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testVerifiesThatNestedPathIsValidAtAnyLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar/baz', 'test'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php new file mode 100644 index 000000000..5484e1446 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php @@ -0,0 +1,62 @@ + '123', + 'other' => '456', + 'event' => 'test.notify' + )); + } + + public function testAllowsParameterInjection() + { + $event = new Event(array( + 'test' => '123' + )); + $this->assertEquals('123', $event['test']); + } + + public function testImplementsArrayAccess() + { + $event = $this->getEvent(); + $this->assertEquals('123', $event['test']); + $this->assertNull($event['foobar']); + + $this->assertTrue($event->offsetExists('test')); + $this->assertFalse($event->offsetExists('foobar')); + + unset($event['test']); + $this->assertFalse($event->offsetExists('test')); + + $event['test'] = 'new'; + $this->assertEquals('new', $event['test']); + } + + public function testImplementsIteratorAggregate() + { + $event = $this->getEvent(); + $this->assertInstanceOf('ArrayIterator', $event->getIterator()); + } + + public function testConvertsToArray() + { + $this->assertEquals(array( + 'test' => '123', + 'other' => '456', + 'event' => 'test.notify' + ), $this->getEvent()->toArray()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php new file mode 100644 index 000000000..c72a2a637 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php @@ -0,0 +1,21 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + $transferException = new BatchTransferException(array('foo'), array(1, 2), $e, $t, $d); + $this->assertEquals(array('foo'), $transferException->getBatch()); + $this->assertSame($t, $transferException->getTransferStrategy()); + $this->assertSame($d, $transferException->getDivisorStrategy()); + $this->assertSame($e, $transferException->getPrevious()); + $this->assertEquals(array(1, 2), $transferException->getTransferredItems()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php new file mode 100644 index 000000000..2aecf2a06 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php @@ -0,0 +1,66 @@ +getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $e->getMessage()); + $this->assertContains(" Test\n\n #0 ./", $e->getMessage()); + $this->assertSame($exceptions[0], $e->getFirst()); + } + + public function testCanSetExceptions() + { + $ex = new \Exception('foo'); + $e = new ExceptionCollection(); + $e->setExceptions(array($ex)); + $this->assertSame($ex, $e->getFirst()); + } + + public function testActsAsArray() + { + $e = new ExceptionCollection(); + $exceptions = $this->getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertEquals(2, count($e)); + $this->assertEquals($exceptions, $e->getIterator()->getArrayCopy()); + } + + public function testCanAddSelf() + { + $e1 = new ExceptionCollection(); + $e1->add(new \Exception("Test")); + $e2 = new ExceptionCollection('Meta description!'); + $e2->add(new \Exception("Test 2")); + $e3 = new ExceptionCollection(); + $e3->add(new \Exception('Baz')); + $e2->add($e3); + $e1->add($e2); + $message = $e1->getMessage(); + $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n Test\n\n #0 ", $message); + $this->assertContains("\n\n(Guzzle\\Common\\Exception\\ExceptionCollection) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n\n Meta description!\n\n", $message); + $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n Test 2\n\n #0 ", $message); + $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains(" Baz\n\n #0", $message); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php new file mode 100644 index 000000000..c3a81d1e4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php @@ -0,0 +1,27 @@ +isRunning()) { + self::$server->flush(); + } else { + self::$server->start(); + } + } + + return self::$server; + } + + /** + * Set the service builder to use for tests + * + * @param ServiceBuilderInterface $builder Service builder + */ + public static function setServiceBuilder(ServiceBuilderInterface $builder) + { + self::$serviceBuilder = $builder; + } + + /** + * Get a service builder object that can be used throughout the service tests + * + * @return ServiceBuilder + */ + public static function getServiceBuilder() + { + if (!self::$serviceBuilder) { + throw new RuntimeException('No service builder has been set via setServiceBuilder()'); + } + + return self::$serviceBuilder; + } + + /** + * Check if an event dispatcher has a subscriber + * + * @param HasDispatcherInterface $dispatcher + * @param EventSubscriberInterface $subscriber + * + * @return bool + */ + protected function hasSubscriber(HasDispatcherInterface $dispatcher, EventSubscriberInterface $subscriber) + { + $class = get_class($subscriber); + $all = array_keys(call_user_func(array($class, 'getSubscribedEvents'))); + + foreach ($all as $i => $event) { + foreach ($dispatcher->getEventDispatcher()->getListeners($event) as $e) { + if ($e[0] === $subscriber) { + unset($all[$i]); + break; + } + } + } + + return count($all) == 0; + } + + /** + * Get a wildcard observer for an event dispatcher + * + * @param HasDispatcherInterface $hasDispatcher + * + * @return MockObserver + */ + public function getWildcardObserver(HasDispatcherInterface $hasDispatcher) + { + $class = get_class($hasDispatcher); + $o = new MockObserver(); + $events = call_user_func(array($class, 'getAllEvents')); + foreach ($events as $event) { + $hasDispatcher->getEventDispatcher()->addListener($event, array($o, 'update')); + } + + return $o; + } + + /** + * Set the mock response base path + * + * @param string $path Path to mock response folder + * + * @return GuzzleTestCase + */ + public static function setMockBasePath($path) + { + self::$mockBasePath = $path; + } + + /** + * Mark a request as being mocked + * + * @param RequestInterface $request + * + * @return self + */ + public function addMockedRequest(RequestInterface $request) + { + $this->requests[] = $request; + + return $this; + } + + /** + * Get all of the mocked requests + * + * @return array + */ + public function getMockedRequests() + { + return $this->requests; + } + + /** + * Get a mock response for a client by mock file name + * + * @param string $path Relative path to the mock response file + * + * @return Response + */ + public function getMockResponse($path) + { + return $path instanceof Response + ? $path + : MockPlugin::getMockFile(self::$mockBasePath . DIRECTORY_SEPARATOR . $path); + } + + /** + * Set a mock response from a mock file on the next client request. + * + * This method assumes that mock response files are located under the + * Command/Mock/ directory of the Service being tested + * (e.g. Unfuddle/Command/Mock/). A mock response is added to the next + * request sent by the client. + * + * @param Client $client Client object to modify + * @param string $paths Path to files within the Mock folder of the service + * + * @return MockPlugin returns the created mock plugin + */ + public function setMockResponse(Client $client, $paths) + { + $this->requests = array(); + $that = $this; + $mock = new MockPlugin(null, true); + $client->getEventDispatcher()->removeSubscriber($mock); + $mock->getEventDispatcher()->addListener('mock.request', function(Event $event) use ($that) { + $that->addMockedRequest($event['request']); + }); + + if ($paths instanceof Response) { + // A single response instance has been specified, create an array with that instance + // as the only element for the following loop to work as expected + $paths = array($paths); + } + + foreach ((array) $paths as $path) { + $mock->addResponse($this->getMockResponse($path)); + } + + $client->getEventDispatcher()->addSubscriber($mock); + + return $mock; + } + + /** + * Compare HTTP headers and use special markup to filter values + * A header prefixed with '!' means it must not exist + * A header prefixed with '_' means it must be ignored + * A header value of '*' means anything after the * will be ignored + * + * @param array $filteredHeaders Array of special headers + * @param array $actualHeaders Array of headers to check against + * + * @return array|bool Returns an array of the differences or FALSE if none + */ + public function compareHeaders($filteredHeaders, $actualHeaders) + { + $comparison = new HeaderComparison(); + + return $comparison->compare($filteredHeaders, $actualHeaders); + } + + /** + * Case insensitive assertContains + * + * @param string $needle Search string + * @param string $haystack Search this + * @param string $message Optional failure message + */ + public function assertContainsIns($needle, $haystack, $message = null) + { + $this->assertContains(strtolower($needle), strtolower($haystack), $message); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php new file mode 100644 index 000000000..20feaa875 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php @@ -0,0 +1,34 @@ +getMockForAbstractClass('Guzzle\Http\AbstractEntityBodyDecorator', array($e)); + + $this->assertSame($e->getStream(), $mock->getStream()); + $this->assertSame($e->getContentLength(), $mock->getContentLength()); + $this->assertSame($e->getSize(), $mock->getSize()); + $this->assertSame($e->getContentMd5(), $mock->getContentMd5()); + $this->assertSame($e->getContentType(), $mock->getContentType()); + $this->assertSame($e->__toString(), $mock->__toString()); + $this->assertSame($e->getUri(), $mock->getUri()); + $this->assertSame($e->getStreamType(), $mock->getStreamType()); + $this->assertSame($e->getWrapper(), $mock->getWrapper()); + $this->assertSame($e->getWrapperData(), $mock->getWrapperData()); + $this->assertSame($e->isReadable(), $mock->isReadable()); + $this->assertSame($e->isWritable(), $mock->isWritable()); + $this->assertSame($e->isConsumed(), $mock->isConsumed()); + $this->assertSame($e->isLocal(), $mock->isLocal()); + $this->assertSame($e->isSeekable(), $mock->isSeekable()); + $this->assertSame($e->getContentEncoding(), $mock->getContentEncoding()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php new file mode 100644 index 000000000..e6e6cdbf5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php @@ -0,0 +1,249 @@ +decorated = EntityBody::factory('testing'); + $this->body = new CachingEntityBody($this->decorated); + } + + public function testUsesRemoteSizeIfPossible() + { + $body = EntityBody::factory('test'); + $caching = new CachingEntityBody($body); + $this->assertEquals(4, $caching->getSize()); + $this->assertEquals(4, $caching->getContentLength()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage does not support custom stream rewind + */ + public function testDoesNotAllowRewindFunction() + { + $this->body->setRewindFunction(true); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Cannot seek to byte 10 + */ + public function testCannotSeekPastWhatHasBeenRead() + { + $this->body->seek(10); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage supports only SEEK_SET and SEEK_CUR + */ + public function testCannotUseSeekEnd() + { + $this->body->seek(2, SEEK_END); + } + + public function testChangingUnderlyingStreamUpdatesSizeAndStream() + { + $size = filesize(__FILE__); + $s = fopen(__FILE__, 'r'); + $this->body->setStream($s, $size); + $this->assertEquals($size, $this->body->getSize()); + $this->assertEquals($size, $this->decorated->getSize()); + $this->assertSame($s, $this->body->getStream()); + $this->assertSame($s, $this->decorated->getStream()); + } + + public function testRewindUsesSeek() + { + $a = EntityBody::factory('foo'); + $d = $this->getMockBuilder('Guzzle\Http\CachingEntityBody') + ->setMethods(array('seek')) + ->setConstructorArgs(array($a)) + ->getMock(); + $d->expects($this->once()) + ->method('seek') + ->with(0) + ->will($this->returnValue(true)); + $d->rewind(); + } + + public function testCanSeekToReadBytes() + { + $this->assertEquals('te', $this->body->read(2)); + $this->body->seek(0); + $this->assertEquals('test', $this->body->read(4)); + $this->assertEquals(4, $this->body->ftell()); + $this->body->seek(2); + $this->assertEquals(2, $this->body->ftell()); + $this->body->seek(2, SEEK_CUR); + $this->assertEquals(4, $this->body->ftell()); + $this->assertEquals('ing', $this->body->read(3)); + } + + public function testWritesToBufferStream() + { + $this->body->read(2); + $this->body->write('hi'); + $this->body->rewind(); + $this->assertEquals('tehiing', (string) $this->body); + } + + public function testReadLinesFromBothStreams() + { + $this->body->seek($this->body->ftell()); + $this->body->write("test\n123\nhello\n1234567890\n"); + $this->body->rewind(); + $this->assertEquals("test\n", $this->body->readLine(7)); + $this->assertEquals("123\n", $this->body->readLine(7)); + $this->assertEquals("hello\n", $this->body->readLine(7)); + $this->assertEquals("123456", $this->body->readLine(7)); + $this->assertEquals("7890\n", $this->body->readLine(7)); + // We overwrote the decorated stream, so no more data + $this->assertEquals('', $this->body->readLine(7)); + } + + public function testSkipsOverwrittenBytes() + { + $decorated = EntityBody::factory( + implode("\n", array_map(function ($n) { + return str_pad($n, 4, '0', STR_PAD_LEFT); + }, range(0, 25))) + ); + + $body = new CachingEntityBody($decorated); + + $this->assertEquals("0000\n", $body->readLine()); + $this->assertEquals("0001\n", $body->readLine()); + // Write over part of the body yet to be read, so skip some bytes + $this->assertEquals(5, $body->write("TEST\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + // Read, which skips bytes, then reads + $this->assertEquals("0003\n", $body->readLine()); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("0004\n", $body->readLine()); + $this->assertEquals("0005\n", $body->readLine()); + + // Overwrite part of the cached body (so don't skip any bytes) + $body->seek(5); + $this->assertEquals(5, $body->write("ABCD\n")); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("TEST\n", $body->readLine()); + $this->assertEquals("0003\n", $body->readLine()); + $this->assertEquals("0004\n", $body->readLine()); + $this->assertEquals("0005\n", $body->readLine()); + $this->assertEquals("0006\n", $body->readLine()); + $this->assertEquals(5, $body->write("1234\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + + // Seek to 0 and ensure the overwritten bit is replaced + $body->rewind(); + $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); + + // Ensure that casting it to a string does not include the bit that was overwritten + $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); + } + + public function testWrapsContentType() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getContentType')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + $a->expects($this->once()) + ->method('getContentType') + ->will($this->returnValue('foo')); + $d = new CachingEntityBody($a); + $this->assertEquals('foo', $d->getContentType()); + } + + public function testWrapsContentEncoding() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getContentEncoding')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + $a->expects($this->once()) + ->method('getContentEncoding') + ->will($this->returnValue('foo')); + $d = new CachingEntityBody($a); + $this->assertEquals('foo', $d->getContentEncoding()); + } + + public function testWrapsMetadata() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getMetadata', 'getWrapper', 'getWrapperData', 'getStreamType', 'getUri')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + + $a->expects($this->once()) + ->method('getMetadata') + ->will($this->returnValue(array())); + // Called twice for getWrapper and getWrapperData + $a->expects($this->exactly(1)) + ->method('getWrapper') + ->will($this->returnValue('wrapper')); + $a->expects($this->once()) + ->method('getWrapperData') + ->will($this->returnValue(array())); + $a->expects($this->once()) + ->method('getStreamType') + ->will($this->returnValue('baz')); + $a->expects($this->once()) + ->method('getUri') + ->will($this->returnValue('path/to/foo')); + + $d = new CachingEntityBody($a); + $this->assertEquals(array(), $d->getMetaData()); + $this->assertEquals('wrapper', $d->getWrapper()); + $this->assertEquals(array(), $d->getWrapperData()); + $this->assertEquals('baz', $d->getStreamType()); + $this->assertEquals('path/to/foo', $d->getUri()); + } + + public function testWrapsCustomData() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getCustomData', 'setCustomData')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + + $a->expects($this->exactly(1)) + ->method('getCustomData') + ->with('foo') + ->will($this->returnValue('bar')); + + $a->expects($this->exactly(1)) + ->method('setCustomData') + ->with('foo', 'bar') + ->will($this->returnSelf()); + + $d = new CachingEntityBody($a); + $this->assertSame($d, $d->setCustomData('foo', 'bar')); + $this->assertEquals('bar', $d->getCustomData('foo')); + } + + public function testClosesBothStreams() + { + $s = fopen('php://temp', 'r'); + $a = EntityBody::factory($s); + $d = new CachingEntityBody($a); + $d->close(); + $this->assertFalse(is_resource($s)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php new file mode 100644 index 000000000..4a91a18f9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php @@ -0,0 +1,601 @@ +assertEquals('http://www.google.com/', $client->getBaseUrl()); + $this->assertSame($client, $client->setConfig(array( + 'test' => '123' + ))); + $this->assertEquals(array('test' => '123'), $client->getConfig()->getAll()); + $this->assertEquals('123', $client->getConfig('test')); + $this->assertSame($client, $client->setBaseUrl('http://www.test.com/{test}')); + $this->assertEquals('http://www.test.com/123', $client->getBaseUrl()); + $this->assertEquals('http://www.test.com/{test}', $client->getBaseUrl(false)); + + try { + $client->setConfig(false); + } catch (\InvalidArgumentException $e) { + } + } + + public function testDescribesEvents() + { + $this->assertEquals(array('client.create_request'), Client::getAllEvents()); + } + + public function testConstructorCanAcceptConfig() + { + $client = new Client('http://www.test.com/', array( + 'data' => '123' + )); + $this->assertEquals('123', $client->getConfig('data')); + } + + public function testCanUseCollectionAsConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(new Collection(array( + 'api' => 'v1', + 'key' => 'value', + 'base_url' => 'http://www.google.com/' + ))); + $this->assertEquals('v1', $client->getConfig('api')); + } + + public function testExpandsUriTemplatesUsingConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(array('api' => 'v1', 'key' => 'value', 'foo' => 'bar')); + $ref = new \ReflectionMethod($client, 'expandTemplate'); + $ref->setAccessible(true); + $this->assertEquals('Testing...api/v1/key/value', $ref->invoke($client, 'Testing...api/{api}/key/{key}')); + } + + public function testClientAttachersObserversToRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $logPlugin = $this->getLogPlugin(); + $client->getEventDispatcher()->addSubscriber($logPlugin); + + // Get a request from the client and ensure the the observer was + // attached to the new request + $request = $client->createRequest(); + $this->assertTrue($this->hasSubscriber($request, $logPlugin)); + } + + public function testClientReturnsValidBaseUrls() + { + $client = new Client('http://www.{foo}.{data}/', array( + 'data' => '123', + 'foo' => 'bar' + )); + $this->assertEquals('http://www.bar.123/', $client->getBaseUrl()); + $client->setBaseUrl('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $client->getBaseUrl()); + } + + public function testClientAddsCurlOptionsToRequests() + { + $client = new Client('http://www.test.com/', array( + 'api' => 'v1', + // Adds the option using the curl values + 'curl.options' => array( + 'CURLOPT_HTTPAUTH' => 'CURLAUTH_DIGEST', + 'abc' => 'foo', + 'blacklist' => 'abc', + 'debug' => true + ) + )); + + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertEquals(CURLAUTH_DIGEST, $options->get(CURLOPT_HTTPAUTH)); + $this->assertEquals('foo', $options->get('abc')); + $this->assertEquals('abc', $options->get('blacklist')); + } + + public function testClientAllowsFineGrainedSslControlButIsSecureByDefault() + { + $client = new Client('https://www.secure.com/'); + + // secure by default + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertTrue($options->get(CURLOPT_SSL_VERIFYPEER)); + + // set a capath if you prefer + $client = new Client('https://www.secure.com/'); + $client->setSslVerification(__DIR__); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertSame(__DIR__, $options->get(CURLOPT_CAPATH)); + } + + public function testConfigSettingsControlSslConfiguration() + { + // Use the default ca certs on the system + $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => 'system')); + $this->assertNull($client->getConfig('curl.options')); + // Can set the cacert value as well + $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => false)); + $options = $client->getConfig('curl.options'); + $this->assertArrayNotHasKey(CURLOPT_CAINFO, $options); + $this->assertSame(false, $options[CURLOPT_SSL_VERIFYPEER]); + $this->assertSame(0, $options[CURLOPT_SSL_VERIFYHOST]); + } + + public function testClientAllowsUnsafeOperationIfRequested() + { + // be really unsafe if you insist + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + + $client->setSslVerification(false); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertFalse($options->get(CURLOPT_SSL_VERIFYPEER)); + $this->assertNull($options->get(CURLOPT_CAINFO)); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testThrowsExceptionForInvalidCertificate() + { + $client = new Client('https://www.secure.com/'); + $client->setSslVerification('/path/to/missing/file'); + } + + public function testClientAllowsSettingSpecificSslCaInfo() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + + $client->setSslVerification(__FILE__); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertSame(__FILE__, $options->get(CURLOPT_CAINFO)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testClientPreventsInadvertentInsecureVerifyHostSetting() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + $client->setSslVerification(__FILE__, true, true); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testClientPreventsInvalidVerifyPeerSetting() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + $client->setSslVerification(__FILE__, 'yes'); + } + + public function testClientAddsParamsToRequests() + { + Version::$emitWarnings = false; + $client = new Client('http://www.example.com', array( + 'api' => 'v1', + 'request.params' => array( + 'foo' => 'bar', + 'baz' => 'jar' + ) + )); + $request = $client->createRequest(); + $this->assertEquals('bar', $request->getParams()->get('foo')); + $this->assertEquals('jar', $request->getParams()->get('baz')); + Version::$emitWarnings = true; + } + + public function urlProvider() + { + $u = $this->getServer()->getUrl() . 'base/'; + $u2 = $this->getServer()->getUrl() . 'base?z=1'; + return array( + array($u, '', $u), + array($u, 'relative/path/to/resource', $u . 'relative/path/to/resource'), + array($u, 'relative/path/to/resource?a=b&c=d', $u . 'relative/path/to/resource?a=b&c=d'), + array($u, '/absolute/path/to/resource', $this->getServer()->getUrl() . 'absolute/path/to/resource'), + array($u, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'), + array($u2, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d&z=1'), + array($u2, 'relative/path/to/resource', $this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1'), + array($u2, 'relative/path/to/resource?another=query', $this->getServer()->getUrl() . 'base/relative/path/to/resource?another=query&z=1') + ); + } + + /** + * @dataProvider urlProvider + */ + public function testBuildsRelativeUrls($baseUrl, $url, $result) + { + $client = new Client($baseUrl); + $this->assertEquals($result, $client->get($url)->getUrl()); + } + + public function testAllowsConfigsToBeChangedAndInjectedInBaseUrl() + { + $client = new Client('http://{a}/{b}'); + $this->assertEquals('http:///', $client->getBaseUrl()); + $this->assertEquals('http://{a}/{b}', $client->getBaseUrl(false)); + $client->setConfig(array( + 'a' => 'test.com', + 'b' => 'index.html' + )); + $this->assertEquals('http://test.com/index.html', $client->getBaseUrl()); + } + + public function testCreatesRequestsWithDefaultValues() + { + $client = new Client($this->getServer()->getUrl() . 'base'); + + // Create a GET request + $request = $client->createRequest(); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a DELETE request + $request = $client->createRequest('DELETE'); + $this->assertEquals('DELETE', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a HEAD request with custom headers + $request = $client->createRequest('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + $this->assertEquals('http://www.test.com/', $request->getUrl()); + + // Create a PUT request + $request = $client->createRequest('PUT'); + $this->assertEquals('PUT', $request->getMethod()); + + // Create a PUT request with injected config + $client->getConfig()->set('a', 1)->set('b', 2); + $request = $client->createRequest('PUT', '/path/{a}?q={b}'); + $this->assertEquals($request->getUrl(), $this->getServer()->getUrl() . 'path/1?q=2'); + } + + public function testClientHasHelperMethodsForCreatingRequests() + { + $url = $this->getServer()->getUrl(); + $client = new Client($url . 'base'); + $this->assertEquals('GET', $client->get()->getMethod()); + $this->assertEquals('PUT', $client->put()->getMethod()); + $this->assertEquals('POST', $client->post()->getMethod()); + $this->assertEquals('HEAD', $client->head()->getMethod()); + $this->assertEquals('DELETE', $client->delete()->getMethod()); + $this->assertEquals('OPTIONS', $client->options()->getMethod()); + $this->assertEquals('PATCH', $client->patch()->getMethod()); + $this->assertEquals($url . 'base/abc', $client->get('abc')->getUrl()); + $this->assertEquals($url . 'zxy', $client->put('/zxy')->getUrl()); + $this->assertEquals($url . 'zxy?a=b', $client->post('/zxy?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->head('?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->delete('/base?a=b')->getUrl()); + } + + public function testClientInjectsConfigsIntoUrls() + { + $client = new Client('http://www.test.com/api/v1', array( + 'test' => '123' + )); + $request = $client->get('relative/{test}'); + $this->assertEquals('http://www.test.com/api/v1/relative/123', $request->getUrl()); + } + + public function testAllowsEmptyBaseUrl() + { + $client = new Client(); + $request = $client->get('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $request->setResponse(new Response(200), true); + $request->send(); + } + + public function testAllowsCustomCurlMultiObjects() + { + $mock = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('add', 'send')); + $mock->expects($this->once()) + ->method('add') + ->will($this->returnSelf()); + $mock->expects($this->once()) + ->method('send') + ->will($this->returnSelf()); + + $client = new Client(); + $client->setCurlMulti($mock); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $client->send($request); + } + + public function testClientSendsMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + + $responses = array( + new Response(200), + new Response(201), + new Response(202) + ); + + $mock->addResponse($responses[0]); + $mock->addResponse($responses[1]); + $mock->addResponse($responses[2]); + + $client->getEventDispatcher()->addSubscriber($mock); + + $requests = array( + $client->get(), + $client->head(), + $client->put('/', null, 'test') + ); + + $this->assertEquals(array( + $responses[0], + $responses[1], + $responses[2] + ), $client->send($requests)); + } + + public function testClientSendsSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(200); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $this->assertEquals($response, $client->send($client->get())); + } + + /** + * @expectedException \Guzzle\Http\Exception\BadResponseException + */ + public function testClientThrowsExceptionForSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(404); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send($client->get()); + } + + /** + * @expectedException \Guzzle\Common\Exception\ExceptionCollection + */ + public function testClientThrowsExceptionForMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $mock->addResponse(new Response(200)); + $mock->addResponse(new Response(404)); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send(array($client->get(), $client->head())); + } + + public function testQueryStringsAreNotDoubleEncoded() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + 'data' => array( + 'test' => 'a&b' + ) + )); + + $request = $client->get('{/path*}{?query,data*}'); + $this->assertEquals('http://test.com/foo/bar?query=hi%20there&test=a%26b', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + $this->assertEquals('a&b', $request->getQuery()->get('test')); + } + + public function testQueryStringsAreNotDoubleEncodedUsingAbsolutePaths() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + )); + $request = $client->get('http://test.com{?query}'); + $this->assertEquals('http://test.com?query=hi%20there', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + } + + public function testAllowsUriTemplateInjection() + { + $client = new Client('http://test.com'); + $ref = new \ReflectionMethod($client, 'getUriTemplate'); + $ref->setAccessible(true); + $a = $ref->invoke($client); + $this->assertSame($a, $ref->invoke($client)); + $client->setUriTemplate(new UriTemplate()); + $this->assertNotSame($a, $ref->invoke($client)); + } + + public function testAllowsCustomVariablesWhenExpandingTemplates() + { + $client = new Client('http://test.com', array('test' => 'hi')); + $ref = new \ReflectionMethod($client, 'expandTemplate'); + $ref->setAccessible(true); + $uri = $ref->invoke($client, 'http://{test}{?query*}', array('query' => array('han' => 'solo'))); + $this->assertEquals('http://hi?han=solo', $uri); + } + + public function testUriArrayAllowsCustomTemplateVariables() + { + $client = new Client(); + $vars = array( + 'var' => 'hi' + ); + $this->assertEquals('/hi', (string) $client->createRequest('GET', array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->get(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->put(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->post(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->head(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->options(array('/{var}', $vars))->getUrl()); + } + + public function testAllowsDefaultHeaders() + { + Version::$emitWarnings = false; + $default = array('X-Test' => 'Hi!'); + $other = array('X-Other' => 'Foo'); + + $client = new Client(); + $client->setDefaultHeaders($default); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + $client->setDefaultHeaders(new Collection($default)); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + + $request = $client->createRequest('GET', null, $other); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET', null, new Collection($other)); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET'); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + Version::$emitWarnings = true; + } + + public function testDontReuseCurlMulti() + { + $client1 = new Client(); + $client2 = new Client(); + $this->assertNotSame($client1->getCurlMulti(), $client2->getCurlMulti()); + } + + public function testGetDefaultUserAgent() + { + $client = new Client(); + $agent = $this->readAttribute($client, 'userAgent'); + $version = curl_version(); + $testAgent = sprintf('Guzzle/%s curl/%s PHP/%s', Version::VERSION, $version['version'], PHP_VERSION); + $this->assertEquals($agent, $testAgent); + + $client->setUserAgent('foo'); + $this->assertEquals('foo', $this->readAttribute($client, 'userAgent')); + } + + public function testOverwritesUserAgent() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com', array('User-agent' => 'foo')); + $this->assertEquals('foo', (string) $request->getHeader('User-Agent')); + } + + public function testUsesDefaultUserAgent() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com'); + $this->assertContains('Guzzle/', (string) $request->getHeader('User-Agent')); + } + + public function testCanSetDefaultRequestOptions() + { + $client = new Client(); + $client->getConfig()->set('request.options', array( + 'query' => array('test' => '123', 'other' => 'abc'), + 'headers' => array('Foo' => 'Bar', 'Baz' => 'Bam') + )); + $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test')); + // Explicit options on a request should overrule default options + $this->assertEquals('Test', (string) $request->getHeader('Foo')); + $this->assertEquals('hello', $request->getQuery()->get('test')); + // Default options should still be set + $this->assertEquals('abc', $request->getQuery()->get('other')); + $this->assertEquals('Bam', (string) $request->getHeader('Baz')); + } + + public function testCanSetSetOptionsOnRequests() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test'), null, array( + 'cookies' => array('michael' => 'test') + )); + $this->assertEquals('test', $request->getCookie('michael')); + } + + public function testHasDefaultOptionsHelperMethods() + { + $client = new Client(); + // With path + $client->setDefaultOption('headers/foo', 'bar'); + $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); + // With simple key + $client->setDefaultOption('allow_redirects', false); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + + $this->assertEquals(array( + 'headers' => array('foo' => 'bar'), + 'allow_redirects' => false + ), $client->getConfig('request.options')); + + $request = $client->get('/'); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testHeadCanUseOptions() + { + $client = new Client(); + $head = $client->head('http://www.foo.com', array(), array('query' => array('foo' => 'bar'))); + $this->assertEquals('bar', $head->getQuery()->get('foo')); + } + + public function testCanSetRelativeUrlStartingWithHttp() + { + $client = new Client('http://www.foo.com'); + $this->assertEquals( + 'http://www.foo.com/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php new file mode 100644 index 000000000..5bf28de36 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php @@ -0,0 +1,947 @@ +getEventDispatcher()->addListener('request.sent', function (Event $e) use ($that) { + $that->requestHandle = $e['handle']; + }); + + return $request; + } + + public function setUp() + { + $this->requestHandle = null; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorExpectsCurlResource() + { + $h = new CurlHandle(false, array()); + } + + public function testConstructorExpectsProperOptions() + { + $h = curl_init($this->getServer()->getUrl()); + try { + $ha = new CurlHandle($h, false); + $this->fail('Expected InvalidArgumentException'); + } catch (\InvalidArgumentException $e) { + } + + $ha = new CurlHandle($h, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + + $ha = new CurlHandle($h, new Collection(array( + CURLOPT_URL => $this->getServer()->getUrl() + ))); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + } + + public function testConstructorInitializesObject() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertSame($handle, $h->getHandle()); + $this->assertInstanceOf('Guzzle\\Http\\Url', $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), (string) $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), $h->getOptions()->get(CURLOPT_URL)); + } + + public function testStoresStdErr() + { + $request = RequestFactory::getInstance()->create('GET', 'http://test.com'); + $request->getCurlOptions()->set('debug', true); + $h = CurlHandle::factory($request); + $this->assertEquals($h->getStderr(true), $h->getOptions()->get(CURLOPT_STDERR)); + $this->assertInternalType('resource', $h->getStderr(true)); + $this->assertInternalType('string', $h->getStderr(false)); + $r = $h->getStderr(true); + fwrite($r, 'test'); + $this->assertEquals('test', $h->getStderr(false)); + } + + public function testStoresCurlErrorNumber() + { + $h = new CurlHandle(curl_init('http://test.com'), array(CURLOPT_URL => 'http://test.com')); + $this->assertEquals(CURLE_OK, $h->getErrorNo()); + $h->setErrorNo(CURLE_OPERATION_TIMEOUTED); + $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $h->getErrorNo()); + } + + public function testAccountsForMissingStdErr() + { + $handle = curl_init('http://www.test.com/'); + $h = new CurlHandle($handle, array( + CURLOPT_URL => 'http://www.test.com/' + )); + $this->assertNull($h->getStderr(false)); + } + + public function testDeterminesIfResourceIsAvailable() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array()); + $this->assertTrue($h->isAvailable()); + + // Mess it up by closing the handle + curl_close($handle); + $this->assertFalse($h->isAvailable()); + + // Mess it up by unsetting the handle + $handle = null; + $this->assertFalse($h->isAvailable()); + } + + public function testWrapsErrorsAndInfo() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + $settings = array( + CURLOPT_PORT => 123, + CURLOPT_CONNECTTIMEOUT_MS => 1, + CURLOPT_TIMEOUT_MS => 1 + ); + + $handle = curl_init($this->getServer()->getUrl()); + curl_setopt_array($handle, $settings); + $h = new CurlHandle($handle, $settings); + @curl_exec($handle); + + $errors = array( + "couldn't connect to host", + 'timeout was reached', + 'connection time-out', + 'connect() timed out!', + 'failed connect to 127.0.0.1:123; connection refused', + 'failed to connect to 127.0.0.1 port 123: connection refused' + ); + $this->assertTrue(in_array(strtolower($h->getError()), $errors), $h->getError() . ' was not the error'); + + $this->assertTrue($h->getErrorNo() > 0); + + $this->assertEquals($this->getServer()->getUrl(), $h->getInfo(CURLINFO_EFFECTIVE_URL)); + $this->assertInternalType('array', $h->getInfo()); + + curl_close($handle); + $this->assertEquals(null, $h->getInfo('url')); + } + + public function testGetInfoWithoutDebugMode() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get($this->getServer()->getUrl()); + $response = $request->send(); + + $info = $response->getInfo(); + $this->assertFalse(empty($info)); + $this->assertEquals($this->getServer()->getUrl(), $info['url']); + } + + public function testWrapsCurlOptions() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_AUTOREFERER => true, + CURLOPT_BUFFERSIZE => 1024 + )); + + $this->assertEquals(true, $h->getOptions()->get(CURLOPT_AUTOREFERER)); + $this->assertEquals(1024, $h->getOptions()->get(CURLOPT_BUFFERSIZE)); + } + + /** + * Data provider for factory tests + * + * @return array + */ + public function dataProvider() + { + $testFile = __DIR__ . '/../../../../../phpunit.xml.dist'; + + $postBody = new QueryString(array('file' => '@' . $testFile)); + $qs = new QueryString(array( + 'x' => 'y', + 'z' => 'a' + )); + + $client = new Client(); + $userAgent = $client->getDefaultUserAgent(); + $auth = base64_encode('michael:123'); + $testFileSize = filesize($testFile); + + $tests = array( + // Send a regular GET + array('GET', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + )), + // Test that custom request methods can be used + array('TRACE', 'http://www.google.com/', null, null, array( + CURLOPT_CUSTOMREQUEST => 'TRACE' + )), + // Send a GET using a port + array('GET', 'http://127.0.0.1:8080', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_PORT => 8080, + CURLOPT_HTTPHEADER => array('Accept:', 'Host: 127.0.0.1:8080', 'User-Agent: ' . $userAgent), + )), + // Send a HEAD request + array('HEAD', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + CURLOPT_NOBODY => 1 + )), + // Send a GET using basic auth + array('GET', 'https://michael:123@127.0.0.1/index.html?q=2', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array( + 'Accept:', + 'Host: 127.0.0.1', + 'Authorization: Basic ' . $auth, + 'User-Agent: ' . $userAgent + ), + CURLOPT_PORT => 443 + )), + // Send a GET request with custom headers + array('GET', 'http://127.0.0.1:8124/', array( + 'x-test-data' => 'Guzzle' + ), null, array( + CURLOPT_PORT => 8124, + CURLOPT_HTTPHEADER => array( + 'Accept:', + 'Host: 127.0.0.1:8124', + 'x-test-data: Guzzle', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'x-test-data' => 'Guzzle' + )), + // Send a POST using a query string + array('POST', 'http://127.0.0.1:8124/post.php', null, $qs, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POSTFIELDS => 'x=y&z=a', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', + '!Transfer-Encoding' => null + )), + // Send a PUT using raw data + array('PUT', 'http://127.0.0.1:8124/put.php', null, EntityBody::factory(fopen($testFile, 'r+')), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_READFUNCTION => 'callback', + CURLOPT_INFILESIZE => filesize($testFile), + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + '!Expect' => null, + 'Content-Length' => $testFileSize, + '!Transfer-Encoding' => null + )), + // Send a POST request using an array of fields + array('POST', 'http://127.0.0.1:8124/post.php', null, array( + 'x' => 'y', + 'a' => 'b' + ), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => 'x=y&a=b', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data and a custom content-type + array('POST', 'http://127.0.0.1:8124/post.php', array( + 'Content-Type' => 'application/json' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_UPLOAD => true, + CURLOPT_INFILESIZE => 14, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/json', + 'User-Agent: ' . $userAgent + ), + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + '!Expect' => null, + 'Content-Length' => '14', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data, a custom content-type, and use chunked encoding + array('POST', 'http://127.0.0.1:8124/post.php', array( + 'Content-Type' => 'application/json', + 'Transfer-Encoding' => 'chunked' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_UPLOAD => true, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Transfer-Encoding: chunked', + 'Content-Type: application/json', + 'User-Agent: ' . $userAgent + ), + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + '!Expect' => null, + 'Transfer-Encoding' => 'chunked', + '!Content-Length' => '' + )), + // Send a POST request with no body + array('POST', 'http://127.0.0.1:8124/post.php', null, '', array( + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Transfer-Encoding' => null + )), + // Send a POST request with empty post fields + array('POST', 'http://127.0.0.1:8124/post.php', null, array(), array( + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Transfer-Encoding' => null + )), + // Send a PATCH request + array('PATCH', 'http://127.0.0.1:8124/patch.php', null, 'body', array( + CURLOPT_INFILESIZE => 4, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + )), + // Send a DELETE request with a body + array('DELETE', 'http://127.0.0.1:8124/delete.php', null, 'body', array( + CURLOPT_CUSTOMREQUEST => 'DELETE', + CURLOPT_INFILESIZE => 4, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '4', + '!Expect' => null, + '!Transfer-Encoding' => null + )), + + /** + * Send a request with empty path and a fragment - the fragment must be + * stripped out before sending it to curl + * + * @issue 453 + * @link https://github.com/guzzle/guzzle/issues/453 + */ + array('GET', 'http://www.google.com#head', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + )), + ); + + $postTest = array('POST', 'http://127.0.0.1:8124/post.php', null, $postBody, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => array( + 'file' => '@' . $testFile . ';filename=phpunit.xml.dist;type=application/octet-stream' + ), + CURLOPT_HTTPHEADER => array ( + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: multipart/form-data', + 'Expect: 100-Continue', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '*', + 'Expect' => '100-Continue', + 'Content-Type' => 'multipart/form-data; boundary=*', + '!Transfer-Encoding' => null + )); + + if (version_compare(phpversion(), '5.5.0', '>=')) { + $postTest[4][CURLOPT_POSTFIELDS] = array( + 'file' => new \CurlFile($testFile, 'application/octet-stream', 'phpunit.xml.dist') + ); + } + + $tests[] = $postTest; + + return $tests; + } + + /** + * @dataProvider dataProvider + */ + public function testFactoryCreatesCurlBasedOnRequest($method, $url, $headers, $body, $options, $expectedHeaders = null) + { + $client = new Client(); + $request = $client->createRequest($method, $url, $headers, $body); + $request->getCurlOptions()->set('debug', true); + + $originalRequest = clone $request; + $curlTest = clone $request; + $handle = CurlHandle::factory($curlTest); + + $this->assertInstanceOf('Guzzle\\Http\\Curl\\CurlHandle', $handle); + $o = $handle->getOptions()->getAll(); + + // Headers are case-insensitive + if (isset($o[CURLOPT_HTTPHEADER])) { + $o[CURLOPT_HTTPHEADER] = array_map('strtolower', $o[CURLOPT_HTTPHEADER]); + } + if (isset($options[CURLOPT_HTTPHEADER])) { + $options[CURLOPT_HTTPHEADER] = array_map('strtolower', $options[CURLOPT_HTTPHEADER]); + } + + $check = 0; + foreach ($options as $key => $value) { + $check++; + $this->assertArrayHasKey($key, $o, '-> Check number ' . $check); + if ($key != CURLOPT_HTTPHEADER && $key != CURLOPT_POSTFIELDS && (is_array($o[$key])) || $o[$key] instanceof \Closure) { + $this->assertEquals('callback', $value, '-> Check number ' . $check); + } else { + $this->assertTrue($value == $o[$key], '-> Check number ' . $check . ' - ' . var_export($value, true) . ' != ' . var_export($o[$key], true)); + } + } + + // If we are testing the actual sent headers + if ($expectedHeaders) { + + // Send the request to the test server + $client = new Client($this->getServer()->getUrl()); + $request->setClient($client); + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + // Get the request that was sent and create a request that we expected + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals($method, $requests[0]->getMethod()); + + $test = $this->compareHeaders($expectedHeaders, $requests[0]->getHeaders()); + $this->assertFalse($test, $test . "\nSent: \n" . $request . "\n\n" . $requests[0]); + + // Ensure only one Content-Length header is sent + if ($request->getHeader('Content-Length')) { + $this->assertEquals((string) $request->getHeader('Content-Length'), (string) $requests[0]->getHeader('Content-Length')); + } + } + } + + public function testFactoryUsesSpecifiedProtocol() + { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:8124/'); + $request->setProtocolVersion('1.1'); + $handle = CurlHandle::factory($request); + $options = $handle->getOptions(); + $this->assertEquals(CURL_HTTP_VERSION_1_1, $options[CURLOPT_HTTP_VERSION]); + } + + public function testUploadsPutData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->getCurlOptions()->set('debug', true); + $request->setBody(EntityBody::factory('test'), 'text/plain', false); + $request->getCurlOptions()->set('progress', true); + + $o = $this->getWildcardObserver($request); + $request->send(); + + // Make sure that the events were dispatched + $this->assertTrue($o->has('curl.callback.progress')); + + // Ensure that the request was received exactly as intended + $r = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); + $this->assertEquals(4, (string) $r[0]->getHeader('Content-Length')); + $sent = strtolower($r[0]); + $this->assertContains('put / http/1.1', $sent); + $this->assertContains('host: 127.0.0.1', $sent); + $this->assertContains('user-agent:', $sent); + $this->assertContains('content-type: text/plain', $sent); + } + + public function testUploadsPutDataUsingChunkedEncodingWhenLengthCannotBeDetermined() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->setBody(EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')), 'text/plain'); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[1]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[1]->hasHeader('Content-Length')); + } + + public function testUploadsPutDataUsingChunkedEncodingWhenForced() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', array('Transfer-Encoding' => 'chunked'), 'hi!'); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[0]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[0]->hasHeader('Content-Length')); + $this->assertEquals('hi!', $r[0]->getBody(true)); + } + + public function testSendsPostRequestsWithFields() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFields(array( + 'a' => 'b', + 'c' => 'ay! ~This is a test, isn\'t it?' + )); + $request->send(); + + // Make sure that the request was sent correctly + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('a=b&c=ay%21%20~This%20is%20a%20test%2C%20isn%27t%20it%3F', (string) $r[0]->getBody()); + $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); + $this->assertEquals(56, (string) $r[0]->getHeader('Content-Length')); + $sent = strtolower($r[0]); + $this->assertContains('post / http/1.1', $sent); + $this->assertContains('content-type: application/x-www-form-urlencoded; charset=utf-8', $sent); + } + + public function testSendsPostRequestsWithFiles() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFiles(array( + 'foo' => __FILE__, + )); + $request->addPostFields(array( + 'bar' => 'baz', + 'arr' => array('a' => 1, 'b' => 2), + )); + $this->updateForHandle($request); + $request->send(); + + // Ensure the CURLOPT_POSTFIELDS option was set properly + $options = $this->requestHandle->getOptions()->getAll(); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=CurlHandleTest.php;type=text/x-', $options[CURLOPT_POSTFIELDS]['foo']); + } else{ + $this->assertInstanceOf('CURLFile', $options[CURLOPT_POSTFIELDS]['foo']); + } + $this->assertEquals('baz', $options[CURLOPT_POSTFIELDS]['bar']); + $this->assertEquals('1', $options[CURLOPT_POSTFIELDS]['arr[a]']); + $this->assertEquals('2', $options[CURLOPT_POSTFIELDS]['arr[b]']); + // Ensure that a Content-Length header was sent by cURL + $this->assertTrue($request->hasHeader('Content-Length')); + } + + public function testCurlConfigurationOptionsAreSet() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->setClient(new Client('http://www.example.com')); + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 99); + $request->getCurlOptions()->set('curl.fake_opt', 99); + $request->getCurlOptions()->set(CURLOPT_PORT, 8181); + $handle = CurlHandle::factory($request); + $this->assertEquals(99, $handle->getOptions()->get(CURLOPT_CONNECTTIMEOUT)); + $this->assertEquals(8181, $handle->getOptions()->get(CURLOPT_PORT)); + $this->assertNull($handle->getOptions()->get('curl.fake_opt')); + $this->assertNull($handle->getOptions()->get('fake_opt')); + } + + public function testEnsuresRequestsHaveResponsesWhenUpdatingFromTransfer() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $handle = CurlHandle::factory($request); + $handle->updateRequestFromTransfer($request); + } + + public function testCanSendBodyAsString() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'foo'); + $request->getCurlOptions()->set('body_as_string', true); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('PUT /', $requests[0]); + $this->assertContains("\nfoo", $requests[0]); + $this->assertContains('content-length: 3', $requests[0]); + $this->assertNotContains('content-type', $requests[0]); + } + + public function testCanSendPostBodyAsString() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/', null, 'foo'); + $request->getCurlOptions()->set('body_as_string', true); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('POST /', $requests[0]); + $this->assertContains("\nfoo", $requests[0]); + $this->assertContains('content-length: 3', $requests[0]); + $this->assertNotContains('content-type', $requests[0]); + } + + public function testAllowsWireTransferInfoToBeEnabled() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $handle = CurlHandle::factory($request); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_STDERR)); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_VERBOSE)); + } + + public function testAddsCustomCurlOptions() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, 200); + $handle = CurlHandle::factory($request); + $this->assertEquals(200, $handle->getOptions()->get(CURLOPT_TIMEOUT)); + } + + public function testSendsPostUploadsWithContentDispositionHeaders() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $fileToUpload = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json'; + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post(); + $request->addPostFile('foo', $fileToUpload, 'application/json'); + $request->addPostFile('foo', __FILE__); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $body = (string) $requests[0]->getBody(); + + $this->assertContains('Content-Disposition: form-data; name="foo[0]"; filename="', $body); + $this->assertContains('Content-Type: application/json', $body); + $this->assertContains('Content-Type: text/x-', $body); + $this->assertContains('Content-Disposition: form-data; name="foo[1]"; filename="', $body); + } + + public function requestMethodProvider() + { + return array(array('POST'), array('PUT'), array('PATCH')); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSendsRequestsWithNoBodyUsingContentLengthZero($method) + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $client->createRequest($method)->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($requests[0]->hasHeader('Transfer-Encoding')); + $this->assertTrue($requests[0]->hasHeader('Content-Length')); + $this->assertEquals('0', (string) $requests[0]->getHeader('Content-Length')); + } + + /** + * @dataProvider provideCurlConfig + */ + public function testParseCurlConfigConvertsStringKeysToConstantKeys($options, $expected) + { + $actual = CurlHandle::parseCurlConfig($options); + $this->assertEquals($expected, $actual); + } + + /** + * Data provider for curl configurations + * + * @return array + */ + public function provideCurlConfig() + { + return array( + // Conversion of option name to constant value + array( + array( + 'CURLOPT_PORT' => 10, + 'CURLOPT_TIMEOUT' => 99 + ), + array( + CURLOPT_PORT => 10, + CURLOPT_TIMEOUT => 99 + ) + ), + // Keeps non constant options + array( + array('debug' => true), + array('debug' => true) + ), + // Conversion of constant names to constant values + array( + array('debug' => 'CURLPROXY_HTTP'), + array('debug' => CURLPROXY_HTTP) + ) + ); + } + + public function testSeeksToBeginningOfStreamWhenSending() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'test'); + $request->send(); + $request->send(); + + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($received)); + $this->assertEquals('test', (string) $received[0]->getBody()); + $this->assertEquals('test', (string) $received[1]->getBody()); + } + + public function testAllowsCurloptEncodingToBeSet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', null); + $request->getCurlOptions()->set(CURLOPT_ENCODING, ''); + $this->updateForHandle($request); + $request->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertSame('', $options[CURLOPT_ENCODING]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('accept: */*', $received[0]); + $this->assertContainsIns('accept-encoding: ', $received[0]); + } + + public function testSendsExpectHeaderWhenSizeIsGreaterThanCutoff() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'test'); + // Start sending the expect header to 2 bytes + $this->updateForHandle($request); + $request->setExpectHeaderCutoff(2)->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertContains('Expect: 100-Continue', $options[CURLOPT_HTTPHEADER]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('expect: 100-continue', $received[0]); + } + + public function testSetsCurloptEncodingWhenAcceptEncodingHeaderIsSet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', array( + 'Accept' => 'application/json', + 'Accept-Encoding' => 'gzip, deflate', + )); + $this->updateForHandle($request); + $request->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertSame('gzip, deflate', $options[CURLOPT_ENCODING]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('accept: application/json', $received[0]); + $this->assertContainsIns('accept-encoding: gzip, deflate', $received[0]); + } + + public function testSendsPostFieldsForNonPostRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client(); + $request = $client->put($this->getServer()->getUrl(), null, array( + 'foo' => 'baz', + 'baz' => 'bar' + )); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PUT', $requests[0]->getMethod()); + $this->assertEquals( + 'application/x-www-form-urlencoded; charset=utf-8', + (string) $requests[0]->getHeader('Content-Type') + ); + $this->assertEquals(15, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals('foo=baz&baz=bar', (string) $requests[0]->getBody()); + } + + public function testSendsPostFilesForNonPostRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client(); + $request = $client->put($this->getServer()->getUrl(), null, array( + 'foo' => '@' . __FILE__ + )); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PUT', $requests[0]->getMethod()); + $this->assertContains('multipart/form-data', (string) $requests[0]->getHeader('Content-Type')); + $this->assertContains('testSendsPostFilesForNonPostRequests', (string) $requests[0]->getBody()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php new file mode 100644 index 000000000..e04141c5f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php @@ -0,0 +1,110 @@ +multi = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + } + + public function tearDown() + { + unset($this->multi); + } + + public function testConstructorSetsMaxHandles() + { + $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + $this->assertEquals(self::MAX_HANDLES, $this->readAttribute($m, 'maxHandles')); + } + + public function testConstructorSetsSelectTimeout() + { + $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + $this->assertEquals(self::SELECT_TIMEOUT, $this->readAttribute($m, 'selectTimeout')); + } + + public function testAddingRequestsAddsToQueue() + { + $r = new Request('GET', 'http://www.foo.com'); + $this->assertSame($this->multi, $this->multi->add($r)); + $this->assertEquals(1, count($this->multi)); + $this->assertEquals(array($r), $this->multi->all()); + + $this->assertTrue($this->multi->remove($r)); + $this->assertFalse($this->multi->remove($r)); + $this->assertEquals(0, count($this->multi)); + } + + public function testResetClearsState() + { + $r = new Request('GET', 'http://www.foo.com'); + $this->multi->add($r); + $this->multi->reset(); + $this->assertEquals(0, count($this->multi)); + } + + public function testSendWillSendQueuedRequestsFirst() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $events = array(); + $client->getCurlMulti()->getEventDispatcher()->addListener( + CurlMultiProxy::ADD_REQUEST, + function ($e) use (&$events) { + $events[] = $e; + } + ); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.complete', function () use ($client) { + $client->get('/foo')->send(); + }); + $request->send(); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($received)); + $this->assertEquals($this->getServer()->getUrl(), $received[0]->getUrl()); + $this->assertEquals($this->getServer()->getUrl() . 'foo', $received[1]->getUrl()); + $this->assertEquals(2, count($events)); + } + + public function testTrimsDownMaxHandleCount() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $client->setCurlMulti(new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT)); + $request = $client->get(); + $request->send(); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $handles = $this->readAttribute($client->getCurlMulti(), 'handles'); + $this->assertEquals(2, count($handles)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php new file mode 100644 index 000000000..1272281f9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php @@ -0,0 +1,455 @@ +multi = new MockMulti(); + } + + public function tearDown() + { + unset($this->multi); + } + + public function testConstructorCreateMultiHandle() + { + $this->assertInternalType('resource', $this->multi->getHandle()); + $this->assertEquals('curl_multi', get_resource_type($this->multi->getHandle())); + } + + public function testDestructorClosesMultiHandle() + { + $handle = $this->multi->getHandle(); + $this->multi->__destruct(); + $this->assertFalse(is_resource($handle)); + } + + public function testRequestsCanBeAddedAndCounted() + { + $multi = new CurlMulti(); + $request1 = new Request('GET', 'http://www.google.com/'); + $multi->add($request1); + $this->assertEquals(array($request1), $multi->all()); + $request2 = new Request('POST', 'http://www.google.com/'); + $multi->add($request2); + $this->assertEquals(array($request1, $request2), $multi->all()); + $this->assertEquals(2, count($multi)); + } + + public function testRequestsCanBeRemoved() + { + $request1 = new Request('GET', 'http://www.google.com/'); + $this->multi->add($request1); + $request2 = new Request('PUT', 'http://www.google.com/'); + $this->multi->add($request2); + $this->assertEquals(array($request1, $request2), $this->multi->all()); + $this->assertTrue($this->multi->remove($request1)); + $this->assertFalse($this->multi->remove($request1)); + $this->assertEquals(array($request2), $this->multi->all()); + } + + public function testsResetRemovesRequestsAndResetsState() + { + $this->multi->add(new Request('GET', 'http://www.google.com/')); + $this->multi->reset(); + $this->assertEquals(array(), $this->multi->all()); + } + + public function testSendsRequestsThroughCurl() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n" . + "data" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $request2 = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->send(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + + $this->assertTrue($response1->getBody(true) == 'data' || $response2->getBody(true) == 'data'); + $this->assertTrue($response1->getBody(true) == '' || $response2->getBody(true) == ''); + $this->assertTrue($response1->getStatusCode() == '204' || $response2->getStatusCode() == '204'); + $this->assertNotEquals((string) $response1, (string) $response2); + } + + public function testSendsThroughCurlAndAggregatesRequestExceptions() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n" . + "data", + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n", + "HTTP/1.1 404 Not Found\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $request2 = new Request('HEAD', $this->getServer()->getUrl()); + $request3 = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->add($request3); + + try { + $this->multi->send(); + $this->fail('MultiTransferException not thrown when aggregating request exceptions'); + } catch (MultiTransferException $e) { + + $this->assertTrue($e->containsRequest($request1)); + $this->assertTrue($e->containsRequest($request2)); + $this->assertTrue($e->containsRequest($request3)); + $this->assertInstanceOf('ArrayIterator', $e->getIterator()); + $this->assertEquals(1, count($e)); + $exceptions = $e->getIterator(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + $response3 = $request3->getResponse(); + + $this->assertNotEquals((string) $response1, (string) $response2); + $this->assertNotEquals((string) $response3, (string) $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response3); + + $failed = $exceptions[0]->getResponse(); + $this->assertEquals(404, $failed->getStatusCode()); + $this->assertEquals(1, count($e)); + + // Test the IteratorAggregate functionality + foreach ($e as $except) { + $this->assertEquals($failed, $except->getResponse()); + } + + $this->assertEquals(1, count($e->getFailedRequests())); + $this->assertEquals(2, count($e->getSuccessfulRequests())); + $this->assertEquals(3, count($e->getAllRequests())); + } + } + + public function testCurlErrorsAreCaught() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + try { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $request->setClient(new Client()); + $request->getCurlOptions()->set(CURLOPT_FRESH_CONNECT, true); + $request->getCurlOptions()->set(CURLOPT_FORBID_REUSE, true); + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, 5); + $request->send(); + $this->fail('CurlException not thrown'); + } catch (CurlException $e) { + $m = $e->getMessage(); + $this->assertContains('[curl] ', $m); + $this->assertContains('[url] http://127.0.0.1:9876/', $m); + $this->assertInternalType('array', $e->getCurlInfo()); + } + } + + public function testRemovesQueuedRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $r = new Response(200); + $request->setClient(new Client()); + $request->setResponse($r, true); + $this->multi->add($request); + $this->multi->send(); + $this->assertSame($r, $request->getResponse()); + } + + public function testRemovesQueuedRequestsAddedInTransit() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.receive.status_line', function (Event $event) use ($client) { + // Create a request using a queued response + $request = $client->get()->setResponse(new Response(200), true); + $request->send(); + }); + $r->send(); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + public function testCatchesExceptionsBeforeSendingSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $multi = new CurlMulti(); + $client->setCurlMulti($multi); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function() { + throw new \RuntimeException('Testing!'); + }); + try { + $request->send(); + $this->fail('Did not throw'); + } catch (\RuntimeException $e) { + // Ensure it was removed + $this->assertEquals(0, count($multi)); + } + } + + /** + * @expectedException \Guzzle\Common\Exception\ExceptionCollection + * @expectedExceptionMessage Thrown before sending! + */ + public function testCatchesExceptionsBeforeSendingMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function() { + throw new \RuntimeException('Thrown before sending!'); + }); + $client->send(array($request)); + } + + public function testCatchesExceptionsWhenRemovingQueuedRequests() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.sent', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + try { + $r->send(); + $this->fail('Did not throw'); + } catch (BadResponseException $e) { + $this->assertCount(0, $client->getCurlMulti()); + } + } + + public function testCatchesExceptionsWhenRemovingQueuedRequestsBeforeSending() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.before_send', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + try { + $r->send(); + $this->fail('Did not throw'); + } catch (BadResponseException $e) { + $this->assertCount(0, $client->getCurlMulti()); + } + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage test + */ + public function testDoesNotCatchRandomExceptionsThrownDuringPerform() + { + $client = new Client($this->getServer()->getUrl()); + $multi = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('perform')); + $multi->expects($this->once()) + ->method('perform') + ->will($this->throwException(new \RuntimeException('test'))); + $multi->add($client->get()); + $multi->send(); + } + + public function testDoesNotSendRequestsDecliningToBeSent() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + // Create a client that is bound to fail connecting + $client = new Client('http://127.0.0.1:123', array( + 'curl.CURLOPT_PORT' => 123, + 'curl.CURLOPT_CONNECTTIMEOUT_MS' => 1, + )); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + + // Listen for request exceptions, and when they occur, first change the + // state of the request back to transferring, and then just allow it to + // exception out + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use ($multi) { + $retries = $event['request']->getParams()->get('retries'); + // Allow the first failure to retry + if ($retries == 0) { + $event['request']->setState('transfer'); + $event['request']->getParams()->set('retries', 1); + // Remove the request to try again + $multi->remove($event['request']); + $multi->add($event['request']); + } + }); + + try { + $multi->send(); + $this->fail('Did not throw an exception at all!?!'); + } catch (\Exception $e) { + $this->assertEquals(1, $request->getParams()->get('retries')); + } + } + + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithRetry() + { + $this->getServer()->flush(); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function(Event $event) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + } + + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithSuccess() + { + // Attempt a port that 99.9% is not listening + $client = new Client('http://127.0.0.1:123'); + $request = $client->get(); + // Ensure it times out quickly if needed + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, 1)->set(CURLOPT_CONNECTTIMEOUT_MS, 1); + + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use (&$count) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + + // Ensure that the exception was caught, and the response was set manually + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + } + + public function testHardResetReopensMultiHandle() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $stream = fopen('php://temp', 'w+'); + $client = new Client($this->getServer()->getUrl()); + $client->getConfig()->set('curl.CURLOPT_VERBOSE', true)->set('curl.CURLOPT_STDERR', $stream); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $multi->reset(true); + $multi->add($request); + $multi->send(); + + rewind($stream); + $this->assertNotContains('Re-using existing connection', stream_get_contents($stream)); + } + + public function testThrowsMeaningfulExceptionsForCurlMultiErrors() + { + $multi = new CurlMulti(); + + // Set the state of the multi object to sending to trigger the exception + $reflector = new \ReflectionMethod('Guzzle\Http\Curl\CurlMulti', 'checkCurlResult'); + $reflector->setAccessible(true); + + // Successful + $reflector->invoke($multi, 0); + + // Known error + try { + $reflector->invoke($multi, CURLM_BAD_HANDLE); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertContains('The passed-in handle is not a valid CURLM handle.', $e->getMessage()); + $this->assertContains('CURLM_BAD_HANDLE', $e->getMessage()); + $this->assertContains(strval(CURLM_BAD_HANDLE), $e->getMessage()); + } + + // Unknown error + try { + $reflector->invoke($multi, 255); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertEquals('Unexpected cURL error: 255', $e->getMessage()); + } + } + + public function testRequestBeforeSendIncludesContentLengthHeaderIfEmptyBody() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = new Request('PUT', $this->getServer()->getUrl()); + $that = $this; + $request->getEventDispatcher()->addListener('request.before_send', function ($event) use ($that) { + $that->assertEquals(0, $event['request']->getHeader('Content-Length')); + }); + $this->multi->add($request); + $this->multi->send(); + } + + public function testRemovesConflictingTransferEncodingHeader() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, fopen($this->getServer()->getUrl(), 'r')); + $request->setHeader('Content-Length', 4); + $request->send(); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($received[1]->hasHeader('Transfer-Encoding')); + $this->assertEquals(4, (string) $received[1]->getHeader('Content-Length')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php new file mode 100644 index 000000000..c7b5ee6e9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php @@ -0,0 +1,39 @@ +getProperty('version'); + $refProperty->setAccessible(true); + $refProperty->setValue($instance, array()); + + $this->assertEquals($info, $instance->getAll()); + $this->assertEquals($info, $instance->getAll()); + + $this->assertEquals($info['version'], $instance->get('version')); + $this->assertFalse($instance->get('foo')); + } + + public function testIsSingleton() + { + $refObject = new \ReflectionClass('Guzzle\Http\Curl\CurlVersion'); + $refProperty = $refObject->getProperty('instance'); + $refProperty->setAccessible(true); + $refProperty->setValue(null, null); + + $this->assertInstanceOf('Guzzle\Http\Curl\CurlVersion', CurlVersion::getInstance()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php new file mode 100644 index 000000000..c69e0c904 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php @@ -0,0 +1,67 @@ +events[] = $event; + } + + public function testEmitsEvents() + { + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('foo'); + $request->setResponse(new Response(200)); + + // Ensure that IO events are emitted + $request->getCurlOptions()->set('emit_io', true); + + // Attach listeners for each event type + $request->getEventDispatcher()->addListener('curl.callback.progress', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.read', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.write', array($this, 'event')); + + $mediator = new RequestMediator($request, true); + + $mediator->progress('a', 'b', 'c', 'd'); + $this->assertEquals(1, count($this->events)); + $this->assertEquals('curl.callback.progress', $this->events[0]->getName()); + + $this->assertEquals(3, $mediator->writeResponseBody('foo', 'bar')); + $this->assertEquals(2, count($this->events)); + $this->assertEquals('curl.callback.write', $this->events[1]->getName()); + $this->assertEquals('bar', $this->events[1]['write']); + $this->assertSame($request, $this->events[1]['request']); + + $this->assertEquals('foo', $mediator->readRequestBody('a', 'b', 3)); + $this->assertEquals(3, count($this->events)); + $this->assertEquals('curl.callback.read', $this->events[2]->getName()); + $this->assertEquals('foo', $this->events[2]['read']); + $this->assertSame($request, $this->events[2]['request']); + } + + public function testDoesNotUseRequestResponseBodyWhenNotCustom() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 307 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nHI", + "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nFI", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", + )); + $client = new Client($this->getServer()->getUrl()); + $response = $client->get()->send(); + $this->assertEquals('test', $response->getBody(true)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php new file mode 100644 index 000000000..124a44da6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php @@ -0,0 +1,182 @@ +assertEquals('data', (string) $body); + $this->assertEquals(4, $body->getContentLength()); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + + $handle = fopen(__DIR__ . '/../../../../phpunit.xml.dist', 'r'); + if (!$handle) { + $this->fail('Could not open test file'); + } + $body = EntityBody::factory($handle); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertTrue($body->isLocal()); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertEquals(filesize(__DIR__ . '/../../../../phpunit.xml.dist'), $body->getContentLength()); + + // make sure that a body will return as the same object + $this->assertTrue($body === EntityBody::factory($body)); + } + + public function testFactoryCreatesTempStreamByDefault() + { + $body = EntityBody::factory(''); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + $body = EntityBody::factory(); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + } + + public function testFactoryCanCreateFromObject() + { + $body = EntityBody::factory(new QueryString(array('foo' => 'bar'))); + $this->assertEquals('foo=bar', (string) $body); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testFactoryEnsuresObjectsHaveToStringMethod() + { + EntityBody::factory(new \stdClass('a')); + } + + public function testHandlesCompression() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must initially return FALSE'); + $size = $body->getContentLength(); + $body->compress(); + $this->assertEquals('gzip', $body->getContentEncoding(), '-> getContentEncoding() must return the correct encoding after compressing'); + $this->assertEquals(gzdeflate('testing 123...testing 123'), (string) $body); + $this->assertTrue($body->getContentLength() < $size); + $this->assertTrue($body->uncompress()); + $this->assertEquals('testing 123...testing 123', (string) $body); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must reset to FALSE'); + + if (in_array('bzip2.*', stream_get_filters())) { + $this->assertTrue($body->compress('bzip2.compress')); + $this->assertEquals('compress', $body->getContentEncoding(), '-> compress() must set \'compress\' as the Content-Encoding'); + } + + $this->assertFalse($body->compress('non-existent'), '-> compress() must return false when a non-existent stream filter is used'); + + // Release the body + unset($body); + + // Use gzip compression on the initial content. This will include a + // gzip header which will need to be stripped when deflating the stream + $body = EntityBody::factory(gzencode('test')); + $this->assertSame($body, $body->setStreamFilterContentEncoding('zlib.deflate')); + $this->assertTrue($body->uncompress('zlib.inflate')); + $this->assertEquals('test', (string) $body); + unset($body); + + // Test using a very long string + $largeString = ''; + for ($i = 0; $i < 25000; $i++) { + $largeString .= chr(rand(33, 126)); + } + $body = EntityBody::factory($largeString); + $this->assertEquals($largeString, (string) $body); + $this->assertTrue($body->compress()); + $this->assertNotEquals($largeString, (string) $body); + $compressed = (string) $body; + $this->assertTrue($body->uncompress()); + $this->assertEquals($largeString, (string) $body); + $this->assertEquals($compressed, gzdeflate($largeString)); + + $body = EntityBody::factory(fopen(__DIR__ . '/../TestData/compress_test', 'w')); + $this->assertFalse($body->compress()); + unset($body); + + unlink(__DIR__ . '/../TestData/compress_test'); + } + + public function testDeterminesContentType() + { + // Test using a string/temp stream + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertNull($body->getContentType()); + + // Use a local file + $body = EntityBody::factory(fopen(__FILE__, 'r')); + $this->assertContains('text/x-', $body->getContentType()); + } + + public function testCreatesMd5Checksum() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertEquals(md5('testing 123...testing 123'), $body->getContentMd5()); + + $server = $this->getServer()->enqueue( + "HTTP/1.1 200 OK" . "\r\n" . + "Content-Length: 3" . "\r\n\r\n" . + "abc" + ); + + $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); + $this->assertFalse($body->getContentMd5()); + } + + public function testSeeksToOriginalPosAfterMd5() + { + $body = EntityBody::factory('testing 123'); + $body->seek(4); + $this->assertEquals(md5('testing 123'), $body->getContentMd5()); + $this->assertEquals(4, $body->ftell()); + $this->assertEquals('ing 123', $body->read(1000)); + } + + public function testGetTypeFormBodyFactoring() + { + $body = EntityBody::factory(array('key1' => 'val1', 'key2' => 'val2')); + $this->assertEquals('key1=val1&key2=val2', (string) $body); + } + + public function testAllowsCustomRewind() + { + $body = EntityBody::factory('foo'); + $rewound = false; + $body->setRewindFunction(function ($body) use (&$rewound) { + $rewound = true; + return $body->seek(0); + }); + $body->seek(2); + $this->assertTrue($body->rewind()); + $this->assertTrue($rewound); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testCustomRewindFunctionMustBeCallable() + { + $body = EntityBody::factory(); + $body->setRewindFunction('foo'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php new file mode 100644 index 000000000..df3e4b791 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php @@ -0,0 +1,27 @@ +assertNull($e->getError()); + $this->assertNull($e->getErrorNo()); + $this->assertSame($e, $e->setError('test', 12)); + $this->assertEquals('test', $e->getError()); + $this->assertEquals(12, $e->getErrorNo()); + + $handle = new CurlHandle(curl_init(), array()); + $e->setCurlHandle($handle); + $this->assertSame($handle, $e->getCurlHandle()); + $handle->close(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php new file mode 100644 index 000000000..12cfd369e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php @@ -0,0 +1,66 @@ +setRequest($request); + $this->assertEquals($request, $e->getRequest()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException + */ + public function testBadResponseException() + { + $e = new BadResponseException('Message'); + $response = new Response(200); + $e->setResponse($response); + $this->assertEquals($response, $e->getResponse()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesGenericErrorExceptionOnError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(307); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\BadResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesClientErrorExceptionOnClientError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(404); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ClientErrorResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesServerErrorExceptionOnServerError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(503); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ServerErrorResponseException', $e); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php new file mode 100644 index 000000000..fa4ec2626 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php @@ -0,0 +1,51 @@ +addSuccessfulRequest($r1); + $e->addFailedRequest($r2); + $this->assertEquals(array($r1), $e->getSuccessfulRequests()); + $this->assertEquals(array($r2), $e->getSuccessfulRequests()); + $this->assertEquals(array($r1, $r2), $e->getAllRequests()); + $this->assertTrue($e->containsRequest($r1)); + $this->assertTrue($e->containsRequest($r2)); + $this->assertFalse($e->containsRequest(new Request('POST', '/foo'))); + } + + public function testCanSetRequests() + { + $s = array($r1 = new Request('GET', 'http://www.foo.com')); + $f = array($r2 = new Request('GET', 'http://www.foo.com')); + $e = new MultiTransferException(); + $e->setSuccessfulRequests($s); + $e->setFailedRequests($f); + $this->assertEquals(array($r1), $e->getSuccessfulRequests()); + $this->assertEquals(array($r2), $e->getSuccessfulRequests()); + } + + public function testAssociatesExceptionsWithRequests() + { + $r1 = new Request('GET', 'http://www.foo.com'); + $re1 = new \Exception('foo'); + $re2 = new \Exception('bar'); + $e = new MultiTransferException(); + $e->add($re2); + $e->addFailedRequestWithException($r1, $re1); + $this->assertSame($re1, $e->getExceptionForFailedRequest($r1)); + $this->assertNull($e->getExceptionForFailedRequest(new Request('POST', '/foo'))); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php new file mode 100644 index 000000000..cd6355f3b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php @@ -0,0 +1,47 @@ +decorated = EntityBody::factory('hello'); + $this->body = new IoEmittingEntityBody($this->decorated); + } + + public function testEmitsReadEvents() + { + $e = null; + $this->body->getEventDispatcher()->addListener('body.read', function ($event) use (&$e) { + $e = $event; + }); + $this->assertEquals('hel', $this->body->read(3)); + $this->assertEquals('hel', $e['read']); + $this->assertEquals(3, $e['length']); + $this->assertSame($this->body, $e['body']); + } + + public function testEmitsWriteEvents() + { + $e = null; + $this->body->getEventDispatcher()->addListener('body.write', function ($event) use (&$e) { + $e = $event; + }); + $this->body->seek(0, SEEK_END); + $this->assertEquals(5, $this->body->write('there')); + $this->assertEquals('there', $e['write']); + $this->assertEquals(5, $e['result']); + $this->assertSame($this->body, $e['body']); + $this->assertEquals('hellothere', (string) $this->body); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php new file mode 100644 index 000000000..9447d8c5d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php @@ -0,0 +1,136 @@ +mock = $this->getMockForAbstractClass('Guzzle\Http\Message\AbstractMessage'); + } + + public function tearDown() + { + $this->mock = $this->request = null; + } + + public function testGetParams() + { + $request = new Request('GET', 'http://example.com'); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $request->getParams()); + } + + public function testAddHeaders() + { + $this->mock->setHeader('A', 'B'); + + $this->assertEquals($this->mock, $this->mock->addHeaders(array( + 'X-Data' => '123' + ))); + + $this->assertTrue($this->mock->hasHeader('X-Data') !== false); + $this->assertTrue($this->mock->hasHeader('A') !== false); + } + + public function testAllowsHeaderToSetAsHeader() + { + $h = new Header('A', 'B'); + $this->mock->setHeader('A', $h); + $this->assertSame($h, $this->mock->getHeader('A')); + } + + public function testGetHeader() + { + $this->mock->setHeader('Test', '123'); + $this->assertEquals('123', $this->mock->getHeader('Test')); + } + + public function testGetHeaders() + { + $this->assertSame($this->mock, $this->mock->setHeaders(array('a' => 'b', 'c' => 'd'))); + $h = $this->mock->getHeaders(); + $this->assertArrayHasKey('a', $h->toArray()); + $this->assertArrayHasKey('c', $h->toArray()); + $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('a')); + $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('c')); + } + + public function testGetHeaderLinesUsesGlue() + { + $this->mock->setHeaders(array('a' => 'b', 'c' => 'd')); + $this->mock->addHeader('a', 'e'); + $this->mock->getHeader('a')->setGlue('!'); + $this->assertEquals(array( + 'a: b! e', + 'c: d' + ), $this->mock->getHeaderLines()); + } + + public function testHasHeader() + { + $this->assertFalse($this->mock->hasHeader('Foo')); + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->setHeader('foo', 'yoo'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->assertEquals(true, $this->mock->hasHeader('foo')); + $this->assertEquals(false, $this->mock->hasHeader('bar')); + } + + public function testRemoveHeader() + { + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->removeHeader('Foo'); + $this->assertFalse($this->mock->hasHeader('Foo')); + } + + public function testReturnsNullWhenHeaderIsNotFound() + { + $this->assertNull($this->mock->getHeader('foo')); + } + + public function testAddingHeadersPreservesOriginalHeaderCase() + { + $this->mock->addHeaders(array( + 'test' => '123', + 'Test' => 'abc' + )); + $this->mock->addHeader('test', '456'); + $this->mock->addHeader('test', '789'); + + $header = $this->mock->getHeader('test'); + $this->assertContains('123', $header->toArray()); + $this->assertContains('456', $header->toArray()); + $this->assertContains('789', $header->toArray()); + $this->assertContains('abc', $header->toArray()); + } + + public function testCanStoreEmptyHeaders() + { + $this->mock->setHeader('Content-Length', 0); + $this->assertTrue($this->mock->hasHeader('Content-Length')); + $this->assertEquals(0, (string) $this->mock->getHeader('Content-Length')); + } + + public function testCanSetCustomHeaderFactory() + { + $f = new Header\HeaderFactory(); + $this->mock->setHeaderFactory($f); + $this->assertSame($f, $this->readAttribute($this->mock, 'headerFactory')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php new file mode 100644 index 000000000..191b02223 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php @@ -0,0 +1,434 @@ +client = new Client(); + } + + public function tearDown() + { + $this->client = null; + } + + public function testConstructorConfiguresRequest() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com', array( + 'X-Test' => '123' + )); + $request->setBody('Test'); + $this->assertEquals('123', $request->getHeader('X-Test')); + $this->assertNull($request->getHeader('Expect')); + } + + public function testCanSetBodyWithoutOverridingContentType() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com', array('Content-Type' => 'foooooo')); + $request->setBody('{"a":"b"}'); + $this->assertEquals('foooooo', $request->getHeader('Content-Type')); + } + + public function testRequestIncludesBodyInMessage() + { + + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $this->assertEquals("PUT / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Length: 4\r\n\r\n" + . "data", (string) $request); + } + + public function testRequestIncludesPostBodyInMessageOnlyWhenNoPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => 'bar' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n" + . "foo=bar", (string) $request); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => '@' . __FILE__ + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: multipart/form-data\r\n" + . "Expect: 100-Continue\r\n\r\n", (string) $request); + } + + public function testAddsPostFieldsAndSetsContentLength() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'data' => '123' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n" + . "data=123", (string) $request); + } + + public function testAddsPostFilesAndSetsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/') + ->addPostFiles(array( + 'file' => __FILE__ + ))->addPostFields(array( + 'a' => 'b' + )); + $message = (string) $request; + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + $this->assertEquals('100-Continue', $request->getHeader('Expect')); + } + + public function testRequestBodyContainsPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/'); + $request->addPostFields(array( + 'test' => '123' + )); + $this->assertContains("\r\n\r\ntest=123", (string) $request); + } + + public function testRequestBodyAddsContentLength() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/'); + $request->setBody(EntityBody::factory('test')); + $this->assertEquals(4, (string) $request->getHeader('Content-Length')); + $this->assertFalse($request->hasHeader('Transfer-Encoding')); + } + + public function testRequestBodyDoesNotUseContentLengthWhenChunked() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'test'); + $this->assertNull($request->getHeader('Content-Length')); + $this->assertTrue($request->hasHeader('Transfer-Encoding')); + } + + public function testRequestHasMutableBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $body = $request->getBody(); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $body); + $this->assertSame($body, $request->getBody()); + + $newBody = EntityBody::factory('foobar'); + $request->setBody($newBody); + $this->assertEquals('foobar', (string) $request->getBody()); + $this->assertSame($newBody, $request->getBody()); + } + + public function testSetPostFields() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $request->getPostFields()); + + $fields = new QueryString(array( + 'a' => 'b' + )); + $request->addPostFields($fields); + $this->assertEquals($fields->getAll(), $request->getPostFields()->getAll()); + $this->assertEquals(array(), $request->getPostFiles()); + } + + public function testSetPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()) + ->setClient(new Client()) + ->addPostFiles(array(__FILE__)) + ->addPostFields(array( + 'test' => 'abc' + )); + + $request->getCurlOptions()->set('debug', true); + + $this->assertEquals(array( + 'test' => 'abc' + ), $request->getPostFields()->getAll()); + + $files = $request->getPostFiles(); + $post = $files['file'][0]; + $this->assertEquals('file', $post->getFieldName()); + $this->assertContains('text/x-', $post->getContentType()); + $this->assertEquals(__FILE__, $post->getFilename()); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + $this->assertNotNull($request->getHeader('Content-Length')); + $this->assertContains('multipart/form-data; boundary=', (string) $request->getHeader('Content-Type'), '-> cURL must add the boundary'); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testSetPostFilesThrowsExceptionWhenFileIsNotFound() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array( + 'file' => 'filenotfound.ini' + )); + } + + /** + * @expectedException Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenNonStringsAreAddedToPost() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', new \stdClass()); + } + + public function testAllowsContentTypeInPostUploads() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__, 'text/plain'); + + $this->assertEquals(array( + new PostFile('foo', __FILE__, 'text/plain') + ), $request->getPostFile('foo')); + } + + public function testGuessesContentTypeOfPostUpload() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__); + $file = $request->getPostFile('foo'); + $this->assertContains('text/x-', $file[0]->getContentType()); + } + + public function testAllowsContentDispositionFieldsInPostUploadsWhenSettingInBulk() + { + $postFile = new PostFile('foo', __FILE__, 'text/x-php'); + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array('foo' => $postFile)); + + $this->assertEquals(array($postFile), $request->getPostFile('foo')); + } + + public function testPostRequestsUseApplicationXwwwForUrlEncodedForArrays() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertContains("\r\n\r\na=b", (string) $request); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type')); + } + + public function testProcessMethodAddsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type')); + } + + public function testPostRequestsUseMultipartFormDataWithFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->addPostFiles(array('file' => __FILE__)); + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + } + + public function testCanSendMultipleRequestsUsingASingleRequestObject() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n", + )); + + // Send the first request + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()) + ->setBody('test') + ->setClient(new Client()); + $request->send(); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + + // Send the second request + $request->setBody('abcdefg', 'application/json', false); + $request->send(); + $this->assertEquals(201, $request->getResponse()->getStatusCode()); + + // Ensure that the same request was sent twice with different bodies + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($requests)); + $this->assertEquals(4, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals(7, (string) $requests[1]->getHeader('Content-Length')); + } + + public function testRemovingPostFieldRebuildsPostFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com'); + $request->setPostField('test', 'value'); + $request->removePostField('test'); + $this->assertNull($request->getPostField('test')); + } + + public function testUsesChunkedTransferWhenBodyLengthCannotBeDetermined() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenContentLengthCannotBeDeterminedAndUsingHttp1() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->setProtocolVersion('1.0'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + } + + public function testAllowsNestedPostData() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => array('b', 'c') + )); + $this->assertEquals(array( + 'a' => array('b', 'c') + ), $request->getPostFields()->getAll()); + } + + public function testAllowsEmptyFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '' + )); + $this->assertEquals(array( + 'a' => '' + ), $request->getPostFields()->getAll()); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + */ + public function testFailsOnInvalidFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'a' => new \stdClass() + )); + } + + public function testHandlesEmptyStrings() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + )); + $this->assertEquals(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + ), $request->getPostFields()->getAll()); + } + + public function testHoldsPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFile('foo', __FILE__); + $request->addPostFile(new PostFile('foo', __FILE__)); + + $this->assertArrayHasKey('foo', $request->getPostFiles()); + $foo = $request->getPostFile('foo'); + $this->assertEquals(2, count($foo)); + $this->assertEquals(__FILE__, $foo[0]->getFilename()); + $this->assertEquals(__FILE__, $foo[1]->getFilename()); + + $request->removePostFile('foo'); + $this->assertEquals(array(), $request->getPostFiles()); + } + + public function testAllowsAtPrefixWhenAddingPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'foo' => '@' . __FILE__ + )); + $foo = $request->getPostFile('foo'); + $this->assertEquals(__FILE__, $foo[0]->getFilename()); + } + + public function testSetStateToTransferWithEmptyBodySetsContentLengthToZero() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->setState($request::STATE_TRANSFER); + $this->assertEquals('0', (string) $request->getHeader('Content-Length')); + } + + public function testSettingExpectHeaderCutoffChangesRequest() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->setHeader('Expect', '100-Continue'); + $request->setExpectHeaderCutoff(false); + $this->assertNull($request->getHeader('Expect')); + // There is not body, so remove the expect header + $request->setHeader('Expect', '100-Continue'); + $request->setExpectHeaderCutoff(10); + $this->assertNull($request->getHeader('Expect')); + // The size is less than the cutoff + $request->setBody('foo'); + $this->assertNull($request->getHeader('Expect')); + // The size is greater than the cutoff + $request->setBody('foobazbarbamboo'); + $this->assertNotNull($request->getHeader('Expect')); + } + + public function testStrictRedirectsCanBeSpecifiedOnEntityEnclosingRequests() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->configureRedirects(true); + $this->assertTrue($request->getParams()->get(RedirectPlugin::STRICT_REDIRECTS)); + } + + public function testCanDisableRedirects() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->configureRedirects(false, false); + $this->assertTrue($request->getParams()->get(RedirectPlugin::DISABLE)); + } + + public function testSetsContentTypeWhenSettingBodyByGuessingFromEntityBody() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); + $request->setBody(EntityBody::factory(fopen(__FILE__, 'r'))); + $this->assertEquals('text/x-php', (string) $request->getHeader('Content-Type')); + } + + public function testDoesNotCloneBody() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); + $request->setBody('test'); + $newRequest = clone $request; + $newRequest->setBody('foo'); + $this->assertInternalType('string', (string) $request->getBody()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php new file mode 100644 index 000000000..62ca5559d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php @@ -0,0 +1,29 @@ +createHeader('Foo', 'Bar'); + $this->assertInstanceOf('Guzzle\Http\Message\Header', $h); + $this->assertEquals('Foo', $h->getName()); + $this->assertEquals('Bar', (string) $h); + } + + public function testCreatesSpecificHeaders() + { + $f = new HeaderFactory(); + $h = $f->createHeader('Link', '; rel="test"'); + $this->assertInstanceOf('Guzzle\Http\Message\Header\Link', $h); + $this->assertEquals('Link', $h->getName()); + $this->assertEquals('; rel="test"', (string) $h); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php new file mode 100644 index 000000000..c834d1016 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php @@ -0,0 +1,63 @@ +; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg", ; rel=side; type="image/jpeg"'); + $links = $link->getLinks(); + $this->assertEquals(array( + array( + 'rel' => 'front', + 'type' => 'image/jpeg', + 'url' => 'http:/.../front.jpeg', + ), + array( + 'rel' => 'back', + 'type' => 'image/jpeg', + 'url' => 'http://.../back.jpeg', + ), + array( + 'rel' => 'side', + 'type' => 'image/jpeg', + 'url' => 'http://.../side.jpeg?test=1' + ) + ), $links); + + $this->assertEquals(array( + 'rel' => 'back', + 'type' => 'image/jpeg', + 'url' => 'http://.../back.jpeg', + ), $link->getLink('back')); + + $this->assertTrue($link->hasLink('front')); + $this->assertFalse($link->hasLink('foo')); + } + + public function testCanAddLink() + { + $link = new Link('Link', '; rel=a; type="image/jpeg"'); + $link->addLink('http://test.com', 'test', array('foo' => 'bar')); + $this->assertEquals( + '; rel=a; type="image/jpeg", ; rel="test"; foo="bar"', + (string) $link + ); + } + + public function testCanParseLinksWithCommas() + { + $link = new Link('Link', '; rel="previous"; title="start, index"'); + $this->assertEquals(array( + array( + 'rel' => 'previous', + 'title' => 'start, index', + 'url' => 'http://example.com/TheBook/chapter1', + ) + ), $link->getLinks()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php new file mode 100644 index 000000000..a3f511bfc --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php @@ -0,0 +1,135 @@ +toArray(); + } + + foreach ($filteredHeaders as $k => $v) { + if ($k[0] == '_') { + // This header should be ignored + $ignore[] = str_replace('_', '', $k); + } elseif ($k[0] == '!') { + // This header must not be present + $absent[] = str_replace('!', '', $k); + } else { + $expected[$k] = $v; + } + } + + return $this->compareArray($expected, $actualHeaders, $ignore, $absent); + } + + /** + * Check if an array of HTTP headers matches another array of HTTP headers while taking * into account as a wildcard + * + * @param array $expected Expected HTTP headers (allows wildcard values) + * @param array|Collection $actual Actual HTTP header array + * @param array $ignore Headers to ignore from the comparison + * @param array $absent Array of headers that must not be present + * + * @return array|bool Returns an array of the differences or FALSE if none + */ + public function compareArray(array $expected, $actual, array $ignore = array(), array $absent = array()) + { + $differences = array(); + + // Add information about headers that were present but weren't supposed to be + foreach ($absent as $header) { + if ($this->hasKey($header, $actual)) { + $differences["++ {$header}"] = $actual[$header]; + unset($actual[$header]); + } + } + + // Check if expected headers are missing + foreach ($expected as $header => $value) { + if (!$this->hasKey($header, $actual)) { + $differences["- {$header}"] = $value; + } + } + + // Flip the ignore array so it works with the case insensitive helper + $ignore = array_flip($ignore); + // Allow case-insensitive comparisons in wildcards + $expected = array_change_key_case($expected); + + // Compare the expected and actual HTTP headers in no particular order + foreach ($actual as $key => $value) { + + // If this is to be ignored, the skip it + if ($this->hasKey($key, $ignore)) { + continue; + } + + // If the header was not expected + if (!$this->hasKey($key, $expected)) { + $differences["+ {$key}"] = $value; + continue; + } + + // Check values and take wildcards into account + $lkey = strtolower($key); + $pos = is_string($expected[$lkey]) ? strpos($expected[$lkey], '*') : false; + + foreach ((array) $actual[$key] as $v) { + if (($pos === false && $v != $expected[$lkey]) || $pos > 0 && substr($v, 0, $pos) != substr($expected[$lkey], 0, $pos)) { + $differences[$key] = "{$value} != {$expected[$lkey]}"; + } + } + } + + return empty($differences) ? false : $differences; + } + + /** + * Case insensitive check if an array have a key + * + * @param string $key Key to check + * @param array $array Array to check + * + * @return bool + */ + protected function hasKey($key, $array) + { + if ($array instanceof Collection) { + $keys = $array->getKeys(); + } else { + $keys = array_keys($array); + } + + foreach ($keys as $k) { + if (!strcasecmp($k, $key)) { + return true; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php new file mode 100644 index 000000000..86c4fe866 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php @@ -0,0 +1,115 @@ + 'Foo' + ), array( + 'Content-Length' => 'Foo' + ), false), + + // Missing header + array(array( + 'X-Foo' => 'Bar' + ), array(), array( + '- X-Foo' => 'Bar' + )), + + // Extra headers is present + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Bar', + 'X-Baz' => 'Jar' + ), array( + '+ X-Baz' => 'Jar' + )), + + // Header is present but must be absent + array(array( + '!X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), array( + '++ X-Foo' => 'Bar' + )), + + // Different values + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Baz' + ), array( + 'X-Foo' => 'Baz != Bar' + )), + + // Wildcard search passes + array(array( + 'X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), false), + + // Wildcard search fails + array(array( + 'X-Foo' => '*' + ), array(), array( + '- X-Foo' => '*' + )), + + // Ignore extra header if present + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz', + 'X-Bar' => 'Jar' + ), false), + + // Ignore extra header if present and is not + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz' + ), false), + + // Case insensitive + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + ), false), + + // Case insensitive with collection + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), new Collection(array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + )), false), + ); + } + + /** + * @dataProvider filterProvider + */ + public function testComparesHeaders($filters, $headers, $result) + { + $compare = new HeaderComparison(); + $this->assertEquals($result, $compare->compare($filters, $headers)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php new file mode 100644 index 000000000..c7502349f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php @@ -0,0 +1,162 @@ + array('foo', 'Foo'), + 'Zoo' => 'bar', + ); + + public function testStoresHeaderName() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('Zoo', $i->getName()); + } + + public function testConvertsToString() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('foo, Foo, bar', (string) $i); + $i->setGlue(';'); + $this->assertEquals('foo; Foo; bar', (string) $i); + } + + public function testNormalizesGluedHeaders() + { + $h = new Header('Zoo', array('foo, Faz', 'bar')); + $result = $h->normalize(true)->toArray(); + natsort($result); + $this->assertEquals(array('bar', 'foo', 'Faz'), $result); + } + + public function testCanSearchForValues() + { + $h = new Header('Zoo', $this->test); + $this->assertTrue($h->hasValue('foo')); + $this->assertTrue($h->hasValue('Foo')); + $this->assertTrue($h->hasValue('bar')); + $this->assertFalse($h->hasValue('moo')); + $this->assertFalse($h->hasValue('FoO')); + } + + public function testIsCountable() + { + $h = new Header('Zoo', $this->test); + $this->assertEquals(3, count($h)); + } + + public function testCanBeIterated() + { + $h = new Header('Zoo', $this->test); + $results = array(); + foreach ($h as $key => $value) { + $results[$key] = $value; + } + $this->assertEquals(array( + 'foo', 'Foo', 'bar' + ), $results); + } + + public function testAllowsFalseyValues() + { + // Allows 0 + $h = new Header('Foo', 0, ';'); + $this->assertEquals('0', (string) $h); + $this->assertEquals(1, count($h)); + $this->assertEquals(';', $h->getGlue()); + + // Does not add a null header by default + $h = new Header('Foo'); + $this->assertEquals('', (string) $h); + $this->assertEquals(0, count($h)); + + // Allows null array for a single null header + $h = new Header('Foo', array(null)); + $this->assertEquals('', (string) $h); + + // Allows empty string + $h = new Header('Foo', ''); + $this->assertEquals('', (string) $h); + $this->assertEquals(1, count($h)); + $this->assertEquals(1, count($h->normalize()->toArray())); + } + + public function testCanRemoveValues() + { + $h = new Header('Foo', array('Foo', 'baz', 'bar')); + $h->removeValue('bar'); + $this->assertTrue($h->hasValue('Foo')); + $this->assertFalse($h->hasValue('bar')); + $this->assertTrue($h->hasValue('baz')); + } + + public function testAllowsArrayInConstructor() + { + $h = new Header('Foo', array('Testing', '123', 'Foo=baz')); + $this->assertEquals(array('Testing', '123', 'Foo=baz'), $h->toArray()); + } + + public function parseParamsProvider() + { + $res1 = array( + array( + '' => '', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + array( + '' => '', + 'rel' => 'back', + 'type' => 'image/jpeg', + ), + ); + + return array( + array( + '; rel="front"; type="image/jpeg", ; rel=back; type="image/jpeg"', + $res1 + ), + array( + '; rel="front"; type="image/jpeg",; rel=back; type="image/jpeg"', + $res1 + ), + array( + 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', + array( + array('foo' => 'baz', 'bar' => '123'), + array('boo' => ''), + array('test' => '123'), + array('foobar' => 'foo;bar') + ) + ), + array( + '; rel="side"; type="image/jpeg",; rel=side; type="image/jpeg"', + array( + array('' => '', 'rel' => 'side', 'type' => 'image/jpeg'), + array('' => '', 'rel' => 'side', 'type' => 'image/jpeg') + ) + ), + array( + '', + array() + ) + ); + } + + /** + * @dataProvider parseParamsProvider + */ + public function testParseParams($header, $result) + { + $response = new Response(200, array('Link' => $header)); + $this->assertEquals($result, $response->getHeader('Link')->parseParams()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php new file mode 100644 index 000000000..be048cb95 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php @@ -0,0 +1,88 @@ +assertEquals('foo', $file->getFieldName()); + $this->assertEquals(__FILE__, $file->getFilename()); + $this->assertEquals('boo', $file->getPostName()); + $this->assertEquals('x-foo', $file->getContentType()); + } + + public function testRemovesLeadingAtSymbolFromPath() + { + $file = new PostFile('foo', '@' . __FILE__); + $this->assertEquals(__FILE__, $file->getFilename()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresFileIsReadable() + { + $file = new PostFile('foo', '/foo/baz/bar'); + } + + public function testCanChangeContentType() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setContentType('Boo'); + $this->assertEquals('Boo', $file->getContentType()); + } + + public function testCanChangeFieldName() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setFieldName('Boo'); + $this->assertEquals('Boo', $file->getFieldName()); + } + + public function testReturnsCurlValueString() + { + $file = new PostFile('foo', __FILE__); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=PostFileTest.php;type=text/x-', $file->getCurlValue()); + } else { + $c = $file->getCurlValue(); + $this->assertEquals(__FILE__, $c->getFilename()); + $this->assertEquals('PostFileTest.php', $c->getPostFilename()); + $this->assertContains('text/x-', $c->getMimeType()); + } + } + + public function testReturnsCurlValueStringAndPostname() + { + $file = new PostFile('foo', __FILE__, null, 'NewPostFileTest.php'); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=NewPostFileTest.php;type=text/x-', $file->getCurlValue()); + } else { + $c = $file->getCurlValue(); + $this->assertEquals(__FILE__, $c->getFilename()); + $this->assertEquals('NewPostFileTest.php', $c->getPostFilename()); + $this->assertContains('text/x-', $c->getMimeType()); + } + } + + public function testContentDispositionFilePathIsStripped() + { + $this->getServer()->flush(); + $client = new Client($this->getServer()->getUrl()); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = $client->post()->addPostFile('file', __FILE__); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('POST / HTTP/1.1', $requests[0]); + $this->assertContains('Content-Disposition: form-data; name="file"; filename="PostFileTest.php"', $requests[0]); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php new file mode 100644 index 000000000..80b8d5415 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php @@ -0,0 +1,616 @@ +assertSame($factory, RequestFactory::getInstance()); + } + + public function testCreatesNewGetRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://www.google.com/'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\MessageInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/', $request->getPath()); + $this->assertEquals('/', $request->getResource()); + + // Create a GET request with a custom receiving body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $b = EntityBody::factory(); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl(), null, $b); + $request->setClient(new Client()); + $response = $request->send(); + $this->assertSame($b, $response->getBody()); + } + + public function testCreatesPutRequests() + { + // Test using a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + unset($request); + + // Test using an EntityBody + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, EntityBody::factory('Data')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using a resource + $resource = fopen('php://temp', 'w+'); + fwrite($resource, 'Data'); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, $resource); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using an object that can be cast as a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, Url::factory('http://www.example.com/')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('http://www.example.com/', (string) $request->getBody()); + } + + public function testCreatesHeadAndDeleteRequests() + { + $request = RequestFactory::getInstance()->create('DELETE', 'http://www.test.com/'); + $this->assertEquals('DELETE', $request->getMethod()); + $request = RequestFactory::getInstance()->create('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + } + + public function testCreatesOptionsRequests() + { + $request = RequestFactory::getInstance()->create('OPTIONS', 'http://www.example.com/'); + $this->assertEquals('OPTIONS', $request->getMethod()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + } + + public function testCreatesNewPutRequestWithBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertEquals('Data', (string) $request->getBody()); + } + + public function testCreatesNewPostRequestWithFields() + { + // Use an array + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, array( + 'a' => 'b' + )); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + unset($request); + + // Use a collection + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new Collection(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + // Use a QueryString + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new QueryString(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/', null, array( + 'a' => 'b', + 'file' => '@' . __FILE__ + )); + + $this->assertEquals(array( + 'a' => 'b' + ), $request->getPostFields()->getAll()); + + $files = $request->getPostFiles(); + $this->assertInstanceOf('Guzzle\Http\Message\PostFile', $files['file'][0]); + } + + public function testCreatesFromParts() + { + $parts = parse_url('http://michael:123@www.google.com:8080/path?q=1&v=2'); + + $request = RequestFactory::getInstance()->fromParts('PUT', $parts, null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('123', $request->getPassword()); + $this->assertEquals('8080', $request->getPort()); + $this->assertEquals(array( + 'scheme' => 'http', + 'host' => 'www.google.com', + 'port' => 8080, + 'path' => '/path', + 'query' => 'q=1&v=2', + ), parse_url($request->getUrl())); + } + + public function testCreatesFromMessage() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com:8080\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization')); + $this->assertEquals('8080', $request->getPort()); + + // Test passing a blank message returns false + $this->assertFalse($request = RequestFactory::getInstance()->fromMessage('')); + + // Test passing a url with no port + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization')); + $this->assertEquals(80, $request->getPort()); + } + + public function testCreatesNewTraceRequest() + { + $request = RequestFactory::getInstance()->create('TRACE', 'http://www.google.com/'); + $this->assertFalse($request instanceof \Guzzle\Http\Message\EntityEnclosingRequest); + $this->assertEquals('TRACE', $request->getMethod()); + } + + public function testCreatesProperTransferEncodingRequests() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'hello'); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + public function testProperlyDealsWithDuplicateHeaders() + { + $parser = new MessageParser(); + + $message = "POST / http/1.1\r\n" + . "DATE:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "host:host.foo.com\r\n" + . "ZOO:abc\r\n" + . "ZOO:123\r\n" + . "ZOO:HI\r\n" + . "zoo:456\r\n\r\n"; + + $parts = $parser->parseRequest($message); + $this->assertEquals(array ( + 'DATE' => 'Mon, 09 Sep 2011 23:36:00 GMT', + 'host' => 'host.foo.com', + 'ZOO' => array('abc', '123', 'HI'), + 'zoo' => '456', + ), $parts['headers']); + + $request = RequestFactory::getInstance()->fromMessage($message); + + $this->assertEquals(array( + 'abc', '123', 'HI', '456' + ), $request->getHeader('zoo')->toArray()); + } + + public function testCreatesHttpMessagesWithBodiesAndNormalizesLineEndings() + { + $message = "POST / http/1.1\r\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\r\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "Host:host.foo.com\r\n\r\n" + . "foo=bar"; + + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf8', (string) $request->getHeader('Content-Type')); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "POST / http/1.1\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\n" + . "Host:host.foo.com\n\n" + . "foo=bar"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "PUT / HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertTrue($request->hasHeader('Content-Length')); + $this->assertEquals(0, (string) $request->getHeader('Content-Length')); + } + + public function testBugPathIncorrectlyHandled() + { + $message = "POST /foo\r\n\r\nBODY"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertSame('POST', $request->getMethod()); + $this->assertSame('/foo', $request->getPath()); + $this->assertSame('BODY', (string) $request->getBody()); + } + + public function testHandlesChunkedTransferEncoding() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.foo.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'Test'); + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.foo.com/', array( + 'transfer-encoding' => 'chunked' + ), array( + 'foo' => 'bar' + )); + + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + } + + public function testClonesRequestsWithMethodWithoutClient() + { + $f = RequestFactory::getInstance(); + $request = $f->create('GET', 'http://www.test.com', array('X-Foo' => 'Bar')); + $request->getParams()->replace(array('test' => '123')); + $request->getCurlOptions()->set('foo', 'bar'); + $cloned = $f->cloneRequestWithMethod($request, 'PUT'); + $this->assertEquals('PUT', $cloned->getMethod()); + $this->assertEquals('Bar', (string) $cloned->getHeader('X-Foo')); + $this->assertEquals('http://www.test.com', $cloned->getUrl()); + // Ensure params are cloned and cleaned up + $this->assertEquals(1, count($cloned->getParams()->getAll())); + $this->assertEquals('123', $cloned->getParams()->get('test')); + // Ensure curl options are cloned + $this->assertEquals('bar', $cloned->getCurlOptions()->get('foo')); + // Ensure event dispatcher is cloned + $this->assertNotSame($request->getEventDispatcher(), $cloned->getEventDispatcher()); + } + + public function testClonesRequestsWithMethodWithClient() + { + $f = RequestFactory::getInstance(); + $client = new Client(); + $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); + $cloned = $f->cloneRequestWithMethod($request, 'GET'); + $this->assertEquals('GET', $cloned->getMethod()); + $this->assertNull($cloned->getHeader('Content-Length')); + $this->assertEquals('http://www.test.com', $cloned->getUrl()); + $this->assertSame($request->getClient(), $cloned->getClient()); + } + + public function testClonesRequestsWithMethodWithClientWithEntityEnclosingChange() + { + $f = RequestFactory::getInstance(); + $client = new Client(); + $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); + $cloned = $f->cloneRequestWithMethod($request, 'POST'); + $this->assertEquals('POST', $cloned->getMethod()); + $this->assertEquals('test', (string) $cloned->getBody()); + } + + public function testCanDisableRedirects() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 307\r\nLocation: " . $this->getServer()->getUrl() . "\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $response = $client->get('/', array(), array('allow_redirects' => false))->send(); + $this->assertEquals(307, $response->getStatusCode()); + } + + public function testCanAddCookies() + { + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', array(), array('cookies' => array('Foo' => 'Bar'))); + $this->assertEquals('Bar', $request->getCookie('Foo')); + } + + public function testCanAddQueryString() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'query' => array('Foo' => 'Bar') + )); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + } + + public function testCanSetDefaultQueryString() + { + $request = new Request('GET', 'http://www.foo.com?test=abc'); + RequestFactory::getInstance()->applyOptions($request, array( + 'query' => array('test' => '123', 'other' => 't123') + ), RequestFactory::OPTIONS_AS_DEFAULTS); + $this->assertEquals('abc', $request->getQuery()->get('test')); + $this->assertEquals('t123', $request->getQuery()->get('other')); + } + + public function testCanAddBasicAuth() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'auth' => array('michael', 'test') + )); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + } + + public function testCanAddDigestAuth() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'auth' => array('michael', 'test', 'digest') + )); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + } + + public function testCanAddEvents() + { + $foo = null; + $client = new Client(); + $client->addSubscriber(new MockPlugin(array(new Response(200)))); + $request = $client->get($this->getServer()->getUrl(), array(), array( + 'events' => array( + 'request.before_send' => function () use (&$foo) { $foo = true; } + ) + )); + $request->send(); + $this->assertTrue($foo); + } + + public function testCanAddEventsWithPriority() + { + $foo = null; + $client = new Client(); + $client->addSubscriber(new MockPlugin(array(new Response(200)))); + $request = $client->get($this->getServer()->getUrl(), array(), array( + 'events' => array( + 'request.before_send' => array(function () use (&$foo) { $foo = true; }, 100) + ) + )); + $request->send(); + $this->assertTrue($foo); + } + + public function testCanAddPlugins() + { + $mock = new MockPlugin(array( + new Response(200), + new Response(200) + )); + $client = new Client(); + $client->addSubscriber($mock); + $request = $client->get('/', array(), array( + 'plugins' => array($mock) + )); + $request->send(); + } + + public function testCanDisableExceptions() + { + $client = new Client(); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(500)))), + 'exceptions' => false + )); + $this->assertEquals(500, $request->send()->getStatusCode()); + } + + public function testCanDisableExceptionsWithErrorListener() + { + $client = new Client(); + $client->getEventDispatcher()->addListener('request.error', function () {}); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(500)))), + 'exceptions' => false + )); + $this->assertEquals(500, $request->send()->getStatusCode()); + } + + public function testCanChangeSaveToLocation() + { + $r = EntityBody::factory(); + $client = new Client(); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(200, array(), 'testing')))), + 'save_to' => $r + )); + $request->send(); + $this->assertEquals('testing', (string) $r); + } + + public function testCanSetProxy() + { + $client = new Client(); + $request = $client->get('/', array(), array('proxy' => '192.168.16.121')); + $this->assertEquals('192.168.16.121', $request->getCurlOptions()->get(CURLOPT_PROXY)); + } + + public function testCanSetHeadersOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('headers' => array('Foo' => 'Bar'))); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + } + + public function testCanSetDefaultHeadersOptions() + { + $request = new Request('GET', 'http://www.foo.com', array('Foo' => 'Bar')); + RequestFactory::getInstance()->applyOptions($request, array( + 'headers' => array('Foo' => 'Baz', 'Bam' => 't123') + ), RequestFactory::OPTIONS_AS_DEFAULTS); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + $this->assertEquals('t123', (string) $request->getHeader('Bam')); + } + + public function testCanSetBodyOption() + { + $client = new Client(); + $request = $client->put('/', array(), null, array('body' => 'test')); + $this->assertEquals('test', (string) $request->getBody()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesBodyOption() + { + $client = new Client(); + $client->get('/', array(), array('body' => 'test')); + } + + public function testCanSetTimeoutOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('timeout' => 1.5)); + $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_TIMEOUT_MS)); + } + + public function testCanSetConnectTimeoutOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('connect_timeout' => 1.5)); + $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_CONNECTTIMEOUT_MS)); + } + + public function testCanSetDebug() + { + $client = new Client(); + $request = $client->get('/', array(), array('debug' => true)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_VERBOSE)); + } + + public function testCanSetVerifyToOff() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => false)); + $this->assertNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(0, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertFalse($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function testCanSetVerifyToOn() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => true)); + $this->assertNotNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function testCanSetVerifyToPath() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function inputValidation() + { + return array_map(function ($option) { return array($option); }, array( + 'headers', 'query', 'cookies', 'auth', 'events', 'plugins', 'params' + )); + } + + /** + * @dataProvider inputValidation + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesInput($option) + { + $client = new Client(); + $client->get('/', array(), array($option => 'foo')); + } + + public function testCanAddRequestParams() + { + $client = new Client(); + $request = $client->put('/', array(), null, array('params' => array('foo' => 'test'))); + $this->assertEquals('test', $request->getParams()->get('foo')); + } + + public function testCanAddSslKey() + { + $client = new Client(); + $request = $client->get('/', array(), array('ssl_key' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); + } + + public function testCanAddSslKeyPassword() + { + $client = new Client(); + $request = $client->get('/', array(), array('ssl_key' => array('/foo.pem', 'bar'))); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); + $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLKEYPASSWD)); + } + + public function testCanAddSslCert() + { + $client = new Client(); + $request = $client->get('/', array(), array('cert' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); + } + + public function testCanAddSslCertPassword() + { + $client = new Client(); + $request = $client->get('/', array(), array('cert' => array('/foo.pem', 'bar'))); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); + $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLCERTPASSWD)); + } + + public function testCreatesBodyWithoutZeroString() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://test.com', array(), '0'); + $this->assertSame('0', (string) $request->getBody()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php new file mode 100644 index 000000000..5bf624829 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php @@ -0,0 +1,639 @@ +client = new Client($this->getServer()->getUrl()); + $this->request = $this->client->get(); + } + + public function tearDown() + { + unset($this->request); + unset($this->client); + } + + public function testConstructorBuildsRequestWithArrayHeaders() + { + // Test passing an array of headers + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'foo' => 'bar' + )); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http://www.guzzle-project.com/', $request->getUrl()); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', Request::getAllEvents()); + } + + public function testConstructorBuildsRequestWithCollectionHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', new Collection(array( + 'foo' => 'bar' + ))); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testConstructorBuildsRequestWithNoHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', null); + $this->assertFalse($request->hasHeader('foo')); + } + + public function testConstructorHandlesNonBasicAuth() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'Authorization' => 'Foo bar' + )); + $this->assertNull($request->getUserName()); + $this->assertNull($request->getPassword()); + $this->assertEquals('Foo bar', (string) $request->getHeader('Authorization')); + } + + public function testRequestsCanBeConvertedToRawMessageStrings() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\n" + . "Host: www.google.com\r\n" + . "Authorization: Basic {$auth}\r\n" + . "Content-Length: 4\r\n\r\nData"; + + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', array( + 'Authorization' => 'Basic ' . $auth + ), 'Data'); + + $this->assertEquals($message, $request->__toString()); + } + + /** + * Add authorization after the fact and see that it was put in the message + */ + public function testRequestStringsIncludeAuth() + { + $auth = base64_encode('michael:123'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl(), null, 'Data') + ->setClient($this->client) + ->setAuth('michael', '123', CURLAUTH_BASIC); + $request->send(); + + $this->assertContains('Authorization: Basic ' . $auth, (string) $request); + } + + public function testGetEventDispatcher() + { + $d = $this->request->getEventDispatcher(); + $this->assertInstanceOf('Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', $d); + $this->assertEquals($d, $this->request->getEventDispatcher()); + } + + public function testRequestsManageClients() + { + $request = new Request('GET', 'http://test.com'); + $this->assertNull($request->getClient()); + $request->setClient($this->client); + $this->assertSame($this->client, $request->getClient()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage A client must be set on the request + */ + public function testRequestsRequireClients() + { + $request = new Request('GET', 'http://test.com'); + $request->send(); + } + + public function testSend() + { + $response = new Response(200, array( + 'Content-Length' => 3 + ), 'abc'); + $this->request->setResponse($response, true); + $r = $this->request->send(); + + $this->assertSame($response, $r); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $this->request->getResponse()); + $this->assertSame($r, $this->request->getResponse()); + $this->assertEquals('complete', $this->request->getState()); + } + + public function testGetResponse() + { + $this->assertNull($this->request->getResponse()); + $response = new Response(200, array('Content-Length' => 3), 'abc'); + + $this->request->setResponse($response); + $this->assertEquals($response, $this->request->getResponse()); + + $client = new Client('http://www.google.com'); + $request = $client->get('http://www.google.com/'); + $request->setResponse($response, true); + $request->send(); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + + // Try again, making sure it's still the same response + $this->assertSame($requestResponse, $request->getResponse()); + + $response = new Response(204); + $request = $client->get(); + $request->setResponse($response, true); + $request->send(); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + } + + public function testRequestThrowsExceptionOnBadResponse() + { + try { + $this->request->setResponse(new Response(404, array('Content-Length' => 3), 'abc'), true); + $this->request->send(); + $this->fail('Expected exception not thrown'); + } catch (BadResponseException $e) { + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $e->getRequest()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $e->getResponse()); + $this->assertContains('Client error response', $e->getMessage()); + } + } + + public function testManagesQuery() + { + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $this->request->getQuery()); + $this->request->getQuery()->set('test', '123'); + $this->assertEquals('test=123', $this->request->getQuery(true)); + } + + public function testRequestHasMethod() + { + $this->assertEquals('GET', $this->request->getMethod()); + } + + public function testRequestHasScheme() + { + $this->assertEquals('http', $this->request->getScheme()); + $this->assertEquals($this->request, $this->request->setScheme('https')); + $this->assertEquals('https', $this->request->getScheme()); + } + + public function testRequestHasHost() + { + $this->assertEquals('127.0.0.1', $this->request->getHost()); + $this->assertEquals('127.0.0.1:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www2.google.com')); + $this->assertEquals('www2.google.com', $this->request->getHost()); + $this->assertEquals('www2.google.com:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www.test.com:8081')); + $this->assertEquals('www.test.com', $this->request->getHost()); + $this->assertEquals(8081, $this->request->getPort()); + } + + public function testRequestHasProtocol() + { + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.1')); + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.0')); + $this->assertEquals('1.0', $this->request->getProtocolVersion()); + } + + public function testRequestHasPath() + { + $this->assertEquals('/', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('/index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + } + + public function testPermitsFalsyComponents() + { + $request = new Request('GET', 'http://0/0?0'); + $this->assertSame('0', $request->getHost()); + $this->assertSame('/0', $request->getPath()); + $this->assertSame('0', $request->getQuery(true)); + + $request = new Request('GET', '0'); + $this->assertEquals('/0', $request->getPath()); + } + + public function testRequestHasPort() + { + $this->assertEquals(8124, $this->request->getPort()); + $this->assertEquals('127.0.0.1:8124', $this->request->getHeader('Host')); + + $this->assertEquals($this->request, $this->request->setPort('8080')); + $this->assertEquals('8080', $this->request->getPort()); + $this->assertEquals('127.0.0.1:8080', $this->request->getHeader('Host')); + + $this->request->setPort(80); + $this->assertEquals('127.0.0.1', $this->request->getHeader('Host')); + } + + public function testRequestHandlesAuthorization() + { + // Uninitialized auth + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Set an auth + $this->assertSame($this->request, $this->request->setAuth('michael', '123')); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('123', $this->request->getPassword()); + + // Set an auth with blank password + $this->assertSame($this->request, $this->request->setAuth('michael', '')); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('', $this->request->getPassword()); + + // Remove the auth + $this->request->setAuth(false); + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Make sure that the cURL based auth works too + $request = new Request('GET', $this->getServer()->getUrl()); + $request->setAuth('michael', 'password', CURLAUTH_DIGEST); + $this->assertEquals('michael:password', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesAuth() + { + $this->request->setAuth('foo', 'bar', 'bam'); + } + + public function testGetResourceUri() + { + $this->assertEquals('/', $this->request->getResource()); + $this->request->setPath('/index.html'); + $this->assertEquals('/index.html', $this->request->getResource()); + $this->request->getQuery()->add('v', '1'); + $this->assertEquals('/index.html?v=1', $this->request->getResource()); + } + + public function testRequestHasMutableUrl() + { + $url = 'http://www.test.com:8081/path?q=123#fragment'; + $u = Url::factory($url); + $this->assertSame($this->request, $this->request->setUrl($url)); + $this->assertEquals($url, $this->request->getUrl()); + + $this->assertSame($this->request, $this->request->setUrl($u)); + $this->assertEquals($url, $this->request->getUrl()); + } + + public function testRequestHasState() + { + $this->assertEquals(RequestInterface::STATE_NEW, $this->request->getState()); + $this->request->setState(RequestInterface::STATE_TRANSFER); + $this->assertEquals(RequestInterface::STATE_TRANSFER, $this->request->getState()); + } + + public function testSetManualResponse() + { + $response = new Response(200, array( + 'Date' => 'Sat, 16 Oct 2010 17:27:14 GMT', + 'Expires' => '-1', + 'Cache-Control' => 'private, max-age=0', + 'Content-Type' => 'text/html; charset=ISO-8859-1', + ), 'response body'); + + $this->assertSame($this->request, $this->request->setResponse($response), '-> setResponse() must use a fluent interface'); + $this->assertEquals('complete', $this->request->getState(), '-> setResponse() must change the state of the request to complete'); + $this->assertSame($response, $this->request->getResponse(), '-> setResponse() must set the exact same response that was passed in to it'); + } + + public function testRequestCanHaveManuallySetResponseBody() + { + $file = __DIR__ . '/../../TestData/temp.out'; + if (file_exists($file)) { + unlink($file); + } + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $entityBody = EntityBody::factory(fopen($file, 'w+')); + $request->setResponseBody($entityBody); + $response = $request->send(); + $this->assertSame($entityBody, $response->getBody()); + + $this->assertTrue(file_exists($file)); + $this->assertEquals('data', file_get_contents($file)); + unlink($file); + + $this->assertEquals('data', $response->getBody(true)); + } + + public function testHoldsCookies() + { + $this->assertNull($this->request->getCookie('test')); + + // Set a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + + // Multiple cookies by setting the Cookie header + $this->request->setHeader('Cookie', '__utma=1.638370270.1344367610.1374365610.1944450276.2; __utmz=1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); hl=de; PHPSESSID=ak93pqashi5uubuoq8fjv60897'); + $this->assertEquals('1.638370270.1344367610.1374365610.1944450276.2', $this->request->getCookie('__utma')); + $this->assertEquals('1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', $this->request->getCookie('__utmz')); + $this->assertEquals('de', $this->request->getCookie('hl')); + $this->assertEquals('ak93pqashi5uubuoq8fjv60897', $this->request->getCookie('PHPSESSID')); + + // Unset the cookies by setting the Cookie header to null + $this->request->setHeader('Cookie', null); + $this->assertNull($this->request->getCookie('test')); + $this->request->removeHeader('Cookie'); + + // Set and remove a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + $this->assertSame($this->request, $this->request->removeCookie('test')); + $this->assertNull($this->request->getCookie('test')); + + // Remove the cookie header + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->request->removeHeader('Cookie'); + $this->assertEquals('', (string) $this->request->getHeader('Cookie')); + + // Remove a cookie value + $this->request->addCookie('foo', 'bar')->addCookie('baz', 'boo'); + $this->request->removeCookie('foo'); + $this->assertEquals(array( + 'baz' => 'boo' + ), $this->request->getCookies()); + + $this->request->addCookie('foo', 'bar'); + $this->assertEquals('baz=boo; foo=bar', (string) $this->request->getHeader('Cookie')); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + * @expectedExceptionMessage Error completing request + */ + public function testRequestThrowsExceptionWhenSetToCompleteWithNoResponse() + { + $this->request->setState(RequestInterface::STATE_COMPLETE); + } + + public function testClonedRequestsUseNewInternalState() + { + $p = new AsyncPlugin(); + $this->request->getEventDispatcher()->addSubscriber($p); + $h = $this->request->getHeader('Host'); + + $r = clone $this->request; + $this->assertEquals(RequestInterface::STATE_NEW, $r->getState()); + $this->assertNotSame($r->getQuery(), $this->request->getQuery()); + $this->assertNotSame($r->getCurlOptions(), $this->request->getCurlOptions()); + $this->assertNotSame($r->getEventDispatcher(), $this->request->getEventDispatcher()); + $this->assertEquals($r->getHeaders(), $this->request->getHeaders()); + $this->assertNotSame($h, $r->getHeader('Host')); + $this->assertNotSame($r->getParams(), $this->request->getParams()); + $this->assertTrue($this->request->getEventDispatcher()->hasListeners('request.sent')); + } + + public function testRecognizesBasicAuthCredentialsInUrls() + { + $this->request->setUrl('http://michael:test@test.com/'); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('test', $this->request->getPassword()); + } + + public function testRequestCanBeSentUsingCurl() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 404 Not Found\r\nContent-Encoding: application/xml\r\nContent-Length: 48\r\n\r\nFile not found" + )); + + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $response = $request->send(); + + $this->assertEquals('data', $response->getBody(true)); + $this->assertEquals(200, (int) $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, $response->getContentLength()); + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $response->getExpires()); + + // Test that the same handle can be sent twice without setting state to new + $response2 = $request->send(); + $this->assertNotSame($response, $response2); + + try { + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl() . 'index.html'); + $request->setClient($this->client); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + } + + $requests = $this->getServer()->getReceivedRequests(true); + $messages = $this->getServer()->getReceivedRequests(false); + $port = $this->getServer()->getPort(); + + $userAgent = $this->client->getDefaultUserAgent(); + + $this->assertEquals('127.0.0.1:' . $port, $requests[0]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[1]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[2]->getHeader('Host')); + + $this->assertEquals('/', $requests[0]->getPath()); + $this->assertEquals('/', $requests[1]->getPath()); + $this->assertEquals('/index.html', $requests[2]->getPath()); + + $parts = explode("\r\n", $messages[0]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[1]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[2]); + $this->assertEquals('GET /index.html HTTP/1.1', $parts[0]); + } + + public function testThrowsExceptionsWhenUnsuccessfulResponseIsReceivedByDefault() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"); + + try { + $request = $this->client->get('/index.html'); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + $this->assertContains('Client error response', $e->getMessage()); + $this->assertContains('[status code] 404', $e->getMessage()); + $this->assertContains('[reason phrase] Not found', $e->getMessage()); + } + } + + public function testCanShortCircuitErrorHandling() + { + $request = $this->request; + $response = new Response(404); + $request->setResponse($response, true); + $out = ''; + $that = $this; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$out, $that) { + $out .= $event['request'] . "\n" . $event['response'] . "\n"; + $event->stopPropagation(); + }); + $request->send(); + $this->assertContains((string) $request, $out); + $this->assertContains((string) $request->getResponse(), $out); + $this->assertSame($response, $request->getResponse()); + } + + public function testCanOverrideUnsuccessfulResponses() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 404 NOT FOUND\r\n" . + "Content-Length: 0\r\n" . + "\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $newResponse = null; + + $request = $this->request; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$newResponse) { + if ($event['response']->getStatusCode() == 404) { + $newRequest = clone $event['request']; + $newResponse = $newRequest->send(); + // Override the original response and bypass additional response processing + $event['response'] = $newResponse; + // Call $event['request']->setResponse($newResponse); to re-apply events + $event->stopPropagation(); + } + }); + + $request->send(); + + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertSame($newResponse, $request->getResponse()); + $this->assertEquals(2, count($this->getServer()->getReceivedRequests())); + } + + public function testCanRetrieveUrlObject() + { + $request = new Request('GET', 'http://www.example.com/foo?abc=d'); + $this->assertInstanceOf('Guzzle\Http\Url', $request->getUrl(true)); + $this->assertEquals('http://www.example.com/foo?abc=d', $request->getUrl()); + $this->assertEquals('http://www.example.com/foo?abc=d', (string) $request->getUrl(true)); + } + + public function testUnresolvedRedirectsReturnResponse() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 SEE OTHER\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n" + )); + $request = $this->request; + $this->assertEquals(303, $request->send()->getStatusCode()); + $request->getParams()->set(RedirectPlugin::DISABLE, true); + $this->assertEquals(301, $request->send()->getStatusCode()); + } + + public function testCanSendCustomRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = $this->client->createRequest('PROPFIND', $this->getServer()->getUrl(), array( + 'Content-Type' => 'text/plain' + ), 'foo'); + $response = $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PROPFIND', $requests[0]->getMethod()); + $this->assertEquals(3, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals('foo', (string) $requests[0]->getBody()); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Warning + */ + public function testEnsuresFileCanBeCreated() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->client->get('/')->setResponseBody('/wefwefefefefwewefwe/wefwefwefefwe/wefwefewfw.txt')->send(); + } + + public function testAllowsFilenameForDownloadingContent() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $name = sys_get_temp_dir() . '/foo.txt'; + $this->client->get('/')->setResponseBody($name)->send(); + $this->assertEquals('test', file_get_contents($name)); + unlink($name); + } + + public function testUsesCustomResponseBodyWhenItIsCustom() + { + $en = EntityBody::factory(); + $request = $this->client->get(); + $request->setResponseBody($en); + $request->setResponse(new Response(200, array(), 'foo')); + $this->assertEquals('foo', (string) $en); + } + + public function testCanChangePortThroughScheme() + { + $request = new Request('GET', 'http://foo.com'); + $request->setScheme('https'); + $this->assertEquals('https://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + $request->setScheme('http'); + $this->assertEquals('http://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + $request->setPort(null); + $this->assertEquals('http://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php new file mode 100644 index 000000000..08b4df878 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php @@ -0,0 +1,677 @@ +response = new Response(200, new Collection(array( + 'Accept-Ranges' => 'bytes', + 'Age' => '12', + 'Allow' => 'GET, HEAD', + 'Cache-Control' => 'no-cache', + 'Content-Encoding' => 'gzip', + 'Content-Language' => 'da', + 'Content-Length' => '348', + 'Content-Location' => '/index.htm', + 'Content-Disposition' => 'attachment; filename=fname.ext', + 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==', + 'Content-Range' => 'bytes 21010-47021/47022', + 'Content-Type' => 'text/html; charset=utf-8', + 'Date' => 'Tue, 15 Nov 1994 08:12:31 GMT', + 'ETag' => '737060cd8c284d8af7ad3082f209582d', + 'Expires' => 'Thu, 01 Dec 1994 16:00:00 GMT', + 'Last-Modified' => 'Tue, 15 Nov 1994 12:45:26 GMT', + 'Location' => 'http://www.w3.org/pub/WWW/People.html', + 'Pragma' => 'no-cache', + 'Proxy-Authenticate' => 'Basic', + 'Retry-After' => '120', + 'Server' => 'Apache/1.3.27 (Unix) (Red-Hat/Linux)', + 'Set-Cookie' => 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'Trailer' => 'Max-Forwards', + 'Transfer-Encoding' => 'chunked', + 'Vary' => '*', + 'Via' => '1.0 fred, 1.1 nowhere.com (Apache/1.1)', + 'Warning' => '199 Miscellaneous warning', + 'WWW-Authenticate' => 'Basic' + )), 'body'); + } + + public function tearDown() + { + unset($this->response); + } + + public function testConstructor() + { + $params = new Collection(); + $body = EntityBody::factory(''); + $response = new Response(200, $params, $body); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals($body, $response->getBody()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Make sure Content-Length is set automatically + $response = new Response(200, $params); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Pass bodies to the response + $response = new Response(200, null, 'data'); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $response = new Response(200, null, EntityBody::factory('data')); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $this->assertEquals('data', $response->getBody(true)); + $response = new Response(200, null, '0'); + $this->assertSame('0', $response->getBody(true), 'getBody(true) should return "0" if response body is "0".'); + + // Make sure the proper exception is thrown + try { + //$response = new Response(200, null, array('foo' => 'bar')); + //$this->fail('Response did not throw exception when passing invalid body'); + } catch (HttpException $e) { + } + + // Ensure custom codes can be set + $response = new Response(2); + $this->assertEquals(2, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + // Make sure the proper exception is thrown when sending invalid headers + try { + $response = new Response(200, 'adidas'); + $this->fail('Response did not throw exception when passing invalid $headers'); + } catch (BadResponseException $e) { + } + } + + public function test__toString() + { + $response = new Response(200); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); + + // Add another header + $response = new Response(200, array( + 'X-Test' => 'Guzzle' + )); + $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); + + $response = new Response(200, array( + 'Content-Length' => 4 + ), 'test'); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); + } + + public function testFactory() + { + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + + // Make sure that automatic Content-Length works + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + } + + public function testFactoryCanCreateHeadResponses() + { + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('', $response->getBody(true)); + } + + public function testFactoryRequiresMessage() + { + $this->assertFalse(Response::fromMessage('')); + } + + public function testGetBody() + { + $body = EntityBody::factory(''); + $response = new Response(403, new Collection(), $body); + $this->assertEquals($body, $response->getBody()); + $response->setBody('foo'); + $this->assertEquals('foo', $response->getBody(true)); + } + + public function testManagesStatusCode() + { + $response = new Response(403); + $this->assertEquals(403, $response->getStatusCode()); + } + + public function testGetMessage() + { + $response = new Response(200, new Collection(array( + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nbody", $response->getMessage()); + } + + public function testGetRawHeaders() + { + $response = new Response(200, new Collection(array( + 'Keep-Alive' => 155, + 'User-Agent' => 'Guzzle', + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nKeep-Alive: 155\r\nUser-Agent: Guzzle\r\nContent-Length: 4\r\n\r\n", $response->getRawHeaders()); + } + + public function testHandlesStatusAndStatusCodes() + { + $response = new Response(200, new Collection(), 'body'); + $this->assertEquals('OK', $response->getReasonPhrase()); + + $this->assertSame($response, $response->setStatus(204)); + $this->assertEquals('No Content', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $this->assertSame($response, $response->setStatus(204, 'Testing!')); + $this->assertEquals('Testing!', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $response->setStatus(2000); + $this->assertEquals(2000, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + $response->setStatus(200, 'Foo'); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('Foo', $response->getReasonPhrase()); + } + + public function testIsClientError() + { + $response = new Response(403); + $this->assertTrue($response->isClientError()); + $response = new Response(200); + $this->assertFalse($response->isClientError()); + } + + public function testIsError() + { + $response = new Response(403); + $this->assertTrue($response->isError()); + $response = new Response(200); + $this->assertFalse($response->isError()); + $response = new Response(500); + $this->assertTrue($response->isError()); + } + + public function testIsInformational() + { + $response = new Response(100); + $this->assertTrue($response->isInformational()); + $response = new Response(200); + $this->assertFalse($response->isInformational()); + } + + public function testIsRedirect() + { + $response = new Response(301); + $this->assertTrue($response->isRedirect()); + $response = new Response(200); + $this->assertFalse($response->isRedirect()); + } + + public function testIsServerError() + { + $response = new Response(500); + $this->assertTrue($response->isServerError()); + $response = new Response(400); + $this->assertFalse($response->isServerError()); + } + + public function testIsSuccessful() + { + $response = new Response(200); + $this->assertTrue($response->isSuccessful()); + $response = new Response(403); + $this->assertFalse($response->isSuccessful()); + } + + public function testGetAcceptRanges() + { + $this->assertEquals('bytes', $this->response->getAcceptRanges()); + } + + public function testCalculatesAge() + { + $this->assertEquals(12, $this->response->calculateAge()); + + $this->response->removeHeader('Age'); + $this->response->removeHeader('Date'); + $this->assertNull($this->response->calculateAge()); + + $this->response->setHeader('Date', gmdate(ClientInterface::HTTP_DATE, strtotime('-1 minute'))); + // If the test runs slowly, still pass with a +5 second allowance + $this->assertTrue($this->response->getAge() - 60 <= 5); + } + + public function testGetAllow() + { + $this->assertEquals('GET, HEAD', $this->response->getAllow()); + } + + public function testGetCacheControl() + { + $this->assertEquals('no-cache', $this->response->getCacheControl()); + } + + public function testGetContentEncoding() + { + $this->assertEquals('gzip', $this->response->getContentEncoding()); + } + + public function testGetContentLanguage() + { + $this->assertEquals('da', $this->response->getContentLanguage()); + } + + public function testGetContentLength() + { + $this->assertEquals('348', $this->response->getContentLength()); + } + + public function testGetContentLocation() + { + $this->assertEquals('/index.htm', $this->response->getContentLocation()); + } + + public function testGetContentDisposition() + { + $this->assertEquals('attachment; filename=fname.ext', $this->response->getContentDisposition()); + } + + public function testGetContentMd5() + { + $this->assertEquals('Q2hlY2sgSW50ZWdyaXR5IQ==', $this->response->getContentMd5()); + } + + public function testGetContentRange() + { + $this->assertEquals('bytes 21010-47021/47022', $this->response->getContentRange()); + } + + public function testGetContentType() + { + $this->assertEquals('text/html; charset=utf-8', $this->response->getContentType()); + } + + public function testGetDate() + { + $this->assertEquals('Tue, 15 Nov 1994 08:12:31 GMT', $this->response->getDate()); + } + + public function testGetEtag() + { + $this->assertEquals('737060cd8c284d8af7ad3082f209582d', $this->response->getEtag()); + } + + public function testGetExpires() + { + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $this->response->getExpires()); + } + + public function testGetLastModified() + { + $this->assertEquals('Tue, 15 Nov 1994 12:45:26 GMT', $this->response->getLastModified()); + } + + public function testGetLocation() + { + $this->assertEquals('http://www.w3.org/pub/WWW/People.html', $this->response->getLocation()); + } + + public function testGetPragma() + { + $this->assertEquals('no-cache', $this->response->getPragma()); + } + + public function testGetProxyAuthenticate() + { + $this->assertEquals('Basic', $this->response->getProxyAuthenticate()); + } + + public function testGetServer() + { + $this->assertEquals('Apache/1.3.27 (Unix) (Red-Hat/Linux)', $this->response->getServer()); + } + + public function testGetSetCookie() + { + $this->assertEquals('UserID=JohnDoe; Max-Age=3600; Version=1', $this->response->getSetCookie()); + } + + public function testGetMultipleSetCookie() + { + $this->response->addHeader('Set-Cookie', 'UserID=Mike; Max-Age=200'); + $this->assertEquals(array( + 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'UserID=Mike; Max-Age=200', + ), $this->response->getHeader('Set-Cookie')->toArray()); + } + + public function testGetSetCookieNormalizesHeaders() + { + $this->response->addHeaders(array( + 'Set-Cooke' => 'boo', + 'set-cookie' => 'foo' + )); + + $this->assertEquals(array( + 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'foo' + ), $this->response->getHeader('Set-Cookie')->toArray()); + + $this->response->addHeaders(array( + 'set-cookie' => 'fubu' + )); + $this->assertEquals( + array('UserID=JohnDoe; Max-Age=3600; Version=1', 'foo', 'fubu'), + $this->response->getHeader('Set-Cookie')->toArray() + ); + } + + public function testGetTrailer() + { + $this->assertEquals('Max-Forwards', $this->response->getTrailer()); + } + + public function testGetTransferEncoding() + { + $this->assertEquals('chunked', $this->response->getTransferEncoding()); + } + + public function testGetVary() + { + $this->assertEquals('*', $this->response->getVary()); + } + + public function testReturnsViaHeader() + { + $this->assertEquals('1.0 fred, 1.1 nowhere.com (Apache/1.1)', $this->response->getVia()); + } + public function testGetWarning() + { + $this->assertEquals('199 Miscellaneous warning', $this->response->getWarning()); + } + + public function testReturnsWwwAuthenticateHeader() + { + $this->assertEquals('Basic', $this->response->getWwwAuthenticate()); + } + + public function testReturnsConnectionHeader() + { + $this->assertEquals(null, $this->response->getConnection()); + $this->response->setHeader('Connection', 'close'); + $this->assertEquals('close', $this->response->getConnection()); + } + + public function testReturnsHeaders() + { + $this->assertEquals('Basic', $this->response->getHeader('WWW-Authenticate', null, true)); + $this->assertEquals('chunked', $this->response->getHeader('Transfer-Encoding', null, false)); + } + + public function testHasTransferInfo() + { + $stats = array ( + 'url' => 'http://www.google.com/', + 'content_type' => 'text/html; charset=ISO-8859-1', + 'http_code' => 200, + 'header_size' => 606, + 'request_size' => 53, + 'filetime' => -1, + 'ssl_verify_result' => 0, + 'redirect_count' => 0, + 'total_time' => 0.093284, + 'namelookup_time' => 0.001349, + 'connect_time' => 0.01635, + 'pretransfer_time' => 0.016358, + 'size_upload' => 0, + 'size_download' => 10330, + 'speed_download' => 110737, + 'speed_upload' => 0, + 'download_content_length' => -1, + 'upload_content_length' => 0, + 'starttransfer_time' => 0.07066, + 'redirect_time' => 0, + ); + + // Uninitialized state + $this->assertNull($this->response->getInfo('url')); + $this->assertEquals(array(), $this->response->getInfo()); + + // Set the stats + $this->response->setInfo($stats); + $this->assertEquals($stats, $this->response->getInfo()); + $this->assertEquals(606, $this->response->getInfo('header_size')); + $this->assertNull($this->response->getInfo('does_not_exist')); + } + + /** + * @return Response + */ + private function getResponse($code, array $headers = null, EntityBody $body = null) + { + return new Response($code, $headers, $body); + } + + public function testDeterminesIfItCanBeCached() + { + $this->assertTrue($this->getResponse(200)->canCache()); + $this->assertTrue($this->getResponse(410)->canCache()); + $this->assertFalse($this->getResponse(404)->canCache()); + $this->assertTrue($this->getResponse(200, array( + 'Cache-Control' => 'public' + ))->canCache()); + + // This has the no-store directive + $this->assertFalse($this->getResponse(200, array( + 'Cache-Control' => 'private, no-store' + ))->canCache()); + + // The body cannot be read, so it cannot be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertFalse($this->getResponse(200, array( + 'Transfer-Encoding' => 'chunked' + ), EntityBody::factory($resource, 10))->canCache()); + unlink($tmp); + + // The body is 0 length, cannot be read, so it can be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertTrue($this->getResponse(200, array(array( + 'Content-Length' => 0 + )), EntityBody::factory($resource, 0))->canCache()); + unlink($tmp); + } + + public function testDeterminesResponseMaxAge() + { + $this->assertEquals(null, $this->getResponse(200)->getMaxAge()); + + // Uses the response's s-maxage + $this->assertEquals(140, $this->getResponse(200, array( + 'Cache-Control' => 's-maxage=140' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120', + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + } + + public function testDeterminesIfItCanValidate() + { + $response = new Response(200); + $this->assertFalse($response->canValidate()); + $response->setHeader('ETag', '123'); + $this->assertTrue($response->canValidate()); + $response->removeHeader('ETag'); + $this->assertFalse($response->canValidate()); + $response->setHeader('Last-Modified', '123'); + $this->assertTrue($response->canValidate()); + } + + public function testCalculatesFreshness() + { + $response = new Response(200); + $this->assertNull($response->isFresh()); + $this->assertNull($response->getFreshness()); + + $response->setHeader('Cache-Control', 'max-age=120'); + $response->setHeader('Age', 100); + $this->assertEquals(20, $response->getFreshness()); + $this->assertTrue($response->isFresh()); + + $response->setHeader('Age', 120); + $this->assertEquals(0, $response->getFreshness()); + $this->assertTrue($response->isFresh()); + + $response->setHeader('Age', 150); + $this->assertEquals(-30, $response->getFreshness()); + $this->assertFalse($response->isFresh()); + } + + public function testHandlesProtocols() + { + $this->assertSame($this->response, $this->response->setProtocol('HTTP', '1.0')); + $this->assertEquals('HTTP', $this->response->getProtocol()); + $this->assertEquals('1.0', $this->response->getProtocolVersion()); + } + + public function testComparesContentType() + { + $response = new Response(200, array( + 'Content-Type' => 'text/html; charset=ISO-8859-4' + )); + + $this->assertTrue($response->isContentType('text/html')); + $this->assertTrue($response->isContentType('TExT/html')); + $this->assertTrue($response->isContentType('charset=ISO-8859-4')); + $this->assertFalse($response->isContentType('application/xml')); + } + + public function testResponseDeterminesIfMethodIsAllowedBaseOnAllowHeader() + { + $response = new Response(200, array( + 'Allow' => 'OPTIONS, POST, deletE,GET' + )); + + $this->assertTrue($response->isMethodAllowed('get')); + $this->assertTrue($response->isMethodAllowed('GET')); + $this->assertTrue($response->isMethodAllowed('options')); + $this->assertTrue($response->isMethodAllowed('post')); + $this->assertTrue($response->isMethodAllowed('Delete')); + $this->assertFalse($response->isMethodAllowed('put')); + $this->assertFalse($response->isMethodAllowed('PUT')); + + $response = new Response(200); + $this->assertFalse($response->isMethodAllowed('get')); + } + + public function testParsesJsonResponses() + { + $response = new Response(200, array(), '{"foo": "bar"}'); + $this->assertEquals(array('foo' => 'bar'), $response->json()); + // Return array when null is a service response + $response = new Response(200); + $this->assertEquals(array(), $response->json()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Unable to parse response body into JSON: 4 + */ + public function testThrowsExceptionWhenFailsToParseJsonResponse() + { + $response = new Response(200, array(), '{"foo": "'); + $response->json(); + } + + public function testParsesXmlResponses() + { + $response = new Response(200, array(), 'bar'); + $this->assertEquals('bar', (string) $response->xml()->foo); + // Always return a SimpleXMLElement from the xml method + $response = new Response(200); + $this->assertEmpty((string) $response->xml()->foo); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML + */ + public function testThrowsExceptionWhenFailsToParseXmlResponse() + { + $response = new Response(200, array(), 'xml(); + } + + public function testResponseIsSerializable() + { + $response = new Response(200, array('Foo' => 'bar'), 'test'); + $r = unserialize(serialize($response)); + $this->assertEquals(200, $r->getStatusCode()); + $this->assertEquals('bar', (string) $r->getHeader('Foo')); + $this->assertEquals('test', (string) $r->getBody()); + } + + public function testPreventsComplexExternalEntities() + { + $xml = ']>&test;'; + $response = new Response(200, array(), $xml); + + $oldCwd = getcwd(); + chdir(__DIR__); + try { + $xml = $response->xml(); + chdir($oldCwd); + $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); + } catch (\Exception $e) { + chdir($oldCwd); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php new file mode 100644 index 000000000..722845340 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php @@ -0,0 +1,31 @@ +assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); + } + + public function testGetsFromFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); + } + + public function testGetsFromCaseInsensitiveFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); + } + + public function testReturnsNullWhenNoMatchFound() + { + $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php new file mode 100644 index 000000000..549d3edd9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php @@ -0,0 +1,30 @@ +aggregate($key, $value, $query); + $this->assertEquals(array('test%20123' => 'foo%20123,baz,bar'), $result); + } + + public function testEncodes() + { + $query = new QueryString(); + $query->useUrlEncoding(false); + $a = new Ag(); + $key = 'test 123'; + $value = array('foo 123', 'baz', 'bar'); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array('test 123' => 'foo 123,baz,bar'), $result); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php new file mode 100644 index 000000000..6a4d9d95b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php @@ -0,0 +1,30 @@ +aggregate($key, $value, $query); + $this->assertEquals(array('facet%201' => array('size%20a', 'width%20b')), $result); + } + + public function testEncodes() + { + $query = new QueryString(); + $query->useUrlEncoding(false); + $a = new Ag(); + $key = 'facet 1'; + $value = array('size a', 'width b'); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array('facet 1' => array('size a', 'width b')), $result); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php new file mode 100644 index 000000000..1e7f0c27a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php @@ -0,0 +1,32 @@ +useUrlEncoding(false); + $a = new Ag(); + $key = 't'; + $value = array( + 'v1' => 'a', + 'v2' => 'b', + 'v3' => array( + 'v4' => 'c', + 'v5' => 'd', + ) + ); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array( + 't[v1]' => 'a', + 't[v2]' => 'b', + 't[v3][v4]' => 'c', + 't[v3][v5]' => 'd', + ), $result); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php new file mode 100644 index 000000000..948db442d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php @@ -0,0 +1,233 @@ +q = new QueryString(); + } + + public function testGetFieldSeparator() + { + $this->assertEquals('&', $this->q->getFieldSeparator()); + } + + public function testGetValueSeparator() + { + $this->assertEquals('=', $this->q->getValueSeparator()); + } + + public function testIsUrlEncoding() + { + $this->assertEquals('RFC 3986', $this->q->getUrlEncoding()); + $this->assertTrue($this->q->isUrlEncoding()); + $this->assertEquals('foo%20bar', $this->q->encodeValue('foo bar')); + + $this->q->useUrlEncoding(QueryString::FORM_URLENCODED); + $this->assertTrue($this->q->isUrlEncoding()); + $this->assertEquals(QueryString::FORM_URLENCODED, $this->q->getUrlEncoding()); + $this->assertEquals('foo+bar', $this->q->encodeValue('foo bar')); + + $this->assertSame($this->q, $this->q->useUrlEncoding(false)); + $this->assertFalse($this->q->isUrlEncoding()); + $this->assertFalse($this->q->isUrlEncoding()); + } + + public function testSetFieldSeparator() + { + $this->assertEquals($this->q, $this->q->setFieldSeparator('/')); + $this->assertEquals('/', $this->q->getFieldSeparator()); + } + + public function testSetValueSeparator() + { + $this->assertEquals($this->q, $this->q->setValueSeparator('/')); + $this->assertEquals('/', $this->q->getValueSeparator()); + } + + public function testUrlEncode() + { + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array('v1', 'v2', 'v3'), + 'ሴ' => 'bar' + ); + $encoded = array( + 'test' => 'value', + 'test%202' => rawurlencode('this is a test?'), + 'test3%5B0%5D' => 'v1', + 'test3%5B1%5D' => 'v2', + 'test3%5B2%5D' => 'v3', + '%E1%88%B4' => 'bar' + ); + $this->q->replace($params); + $this->assertEquals($encoded, $this->q->urlEncode()); + + // Disable encoding + $testData = array('test 2' => 'this is a test'); + $this->q->replace($testData); + $this->q->useUrlEncoding(false); + $this->assertEquals($testData, $this->q->urlEncode()); + } + + public function testToString() + { + // Check with no parameters + $this->assertEquals('', $this->q->__toString()); + + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array('v1', 'v2', 'v3'), + 'test4' => null, + ); + $this->q->replace($params); + $this->assertEquals('test=value&test%202=this%20is%20a%20test%3F&test3%5B0%5D=v1&test3%5B1%5D=v2&test3%5B2%5D=v3&test4', $this->q->__toString()); + $this->q->useUrlEncoding(false); + $this->assertEquals('test=value&test 2=this is a test?&test3[0]=v1&test3[1]=v2&test3[2]=v3&test4', $this->q->__toString()); + + // Use an alternative aggregator + $this->q->setAggregator(new CommaAggregator()); + $this->assertEquals('test=value&test 2=this is a test?&test3=v1,v2,v3&test4', $this->q->__toString()); + } + + public function testAllowsMultipleValuesPerKey() + { + $q = new QueryString(); + $q->add('facet', 'size'); + $q->add('facet', 'width'); + $q->add('facet.field', 'foo'); + // Use the duplicate aggregator + $q->setAggregator(new DuplicateAggregator()); + $this->assertEquals('facet=size&facet=width&facet.field=foo', $q->__toString()); + } + + public function testAllowsNestedQueryData() + { + $this->q->replace(array( + 'test' => 'value', + 't' => array( + 'v1' => 'a', + 'v2' => 'b', + 'v3' => array( + 'v4' => 'c', + 'v5' => 'd', + ) + ) + )); + + $this->q->useUrlEncoding(false); + $this->assertEquals('test=value&t[v1]=a&t[v2]=b&t[v3][v4]=c&t[v3][v5]=d', $this->q->__toString()); + } + + public function parseQueryProvider() + { + return array( + // Ensure that multiple query string values are allowed per value + array('q=a&q=b', array('q' => array('a', 'b'))), + // Ensure that PHP array style query string values are parsed + array('q[]=a&q[]=b', array('q' => array('a', 'b'))), + // Ensure that a single PHP array style query string value is parsed into an array + array('q[]=a', array('q' => array('a'))), + // Ensure that decimals are allowed in query strings + array('q.a=a&q.b=b', array( + 'q.a' => 'a', + 'q.b' => 'b' + )), + // Ensure that query string values are percent decoded + array('q%20a=a%20b', array('q a' => 'a b')), + // Ensure null values can be added + array('q&a', array('q' => false, 'a' => false)), + ); + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesQueryStrings($query, $data) + { + $query = QueryString::fromString($query); + $this->assertEquals($data, $query->getAll()); + } + + public function testProperlyDealsWithDuplicateQueryStringValues() + { + $query = QueryString::fromString('foo=a&foo=b&?µ=c'); + $this->assertEquals(array('a', 'b'), $query->get('foo')); + $this->assertEquals('c', $query->get('?µ')); + } + + public function testAllowsBlankQueryStringValues() + { + $query = QueryString::fromString('foo'); + $this->assertEquals('foo', (string) $query); + $query->set('foo', QueryString::BLANK); + $this->assertEquals('foo', (string) $query); + } + + public function testAllowsFalsyQueryStringValues() + { + $query = QueryString::fromString('0'); + $this->assertEquals('0', (string) $query); + $query->set('0', QueryString::BLANK); + $this->assertSame('0', (string) $query); + } + + public function testFromStringIgnoresQuestionMark() + { + $query = QueryString::fromString('foo=baz&bar=boo'); + $this->assertEquals('foo=baz&bar=boo', (string) $query); + } + + public function testConvertsPlusSymbolsToSpaces() + { + $query = QueryString::fromString('var=foo+bar'); + $this->assertEquals('foo bar', $query->get('var')); + } + + public function testFromStringDoesntMangleZeroes() + { + $query = QueryString::fromString('var=0'); + $this->assertSame('0', $query->get('var')); + } + + public function testAllowsZeroValues() + { + $query = new QueryString(array( + 'foo' => 0, + 'baz' => '0', + 'bar' => null, + 'boo' => false, + 'bam' => '' + )); + $this->assertEquals('foo=0&baz=0&bar&boo&bam=', (string) $query); + } + + public function testFromStringDoesntStripTrailingEquals() + { + $query = QueryString::fromString('data=mF0b3IiLCJUZWFtIERldiJdfX0='); + $this->assertEquals('mF0b3IiLCJUZWFtIERldiJdfX0=', $query->get('data')); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsed() + { + $query = QueryString::fromString('test=a&test=b'); + $this->assertEquals('test=a&test=b', (string) $query); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsedAndChecksForPhpStyle() + { + $query = QueryString::fromString('test[]=a&test[]=b'); + $this->assertEquals('test%5B0%5D=a&test%5B1%5D=b', (string) $query); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php new file mode 100644 index 000000000..6bb3fed18 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php @@ -0,0 +1,81 @@ +decorated = EntityBody::factory(fopen(__FILE__, 'r')); + $this->body = new ReadLimitEntityBody($this->decorated, 10, 3); + } + + public function testReturnsSubsetWhenCastToString() + { + $body = EntityBody::factory('foo_baz_bar'); + $limited = new ReadLimitEntityBody($body, 3, 4); + $this->assertEquals('baz', (string) $limited); + } + + public function testReturnsSubsetOfEmptyBodyWhenCastToString() + { + $body = EntityBody::factory(''); + $limited = new ReadLimitEntityBody($body, 0, 10); + $this->assertEquals('', (string) $limited); + } + + public function testSeeksWhenConstructed() + { + $this->assertEquals(3, $this->body->ftell()); + } + + public function testAllowsBoundedSeek() + { + $this->body->seek(100); + $this->assertEquals(13, $this->body->ftell()); + $this->body->seek(0); + $this->assertEquals(3, $this->body->ftell()); + $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); + } + + public function testReadsOnlySubsetOfData() + { + $data = $this->body->read(100); + $this->assertEquals(10, strlen($data)); + $this->assertFalse($this->body->read(1000)); + + $this->body->setOffset(10); + $newData = $this->body->read(100); + $this->assertEquals(10, strlen($newData)); + $this->assertNotSame($data, $newData); + } + + public function testClaimsConsumedWhenReadLimitIsReached() + { + $this->assertFalse($this->body->isConsumed()); + $this->body->read(1000); + $this->assertTrue($this->body->isConsumed()); + } + + public function testContentLengthIsBounded() + { + $this->assertEquals(10, $this->body->getContentLength()); + } + + public function testContentMd5IsBasedOnSubsection() + { + $this->assertNotSame($this->body->getContentMd5(), $this->decorated->getContentMd5()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php new file mode 100755 index 000000000..886236ddd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php @@ -0,0 +1,277 @@ +getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + // Create a client that uses the default redirect behavior + $client = new Client($this->getServer()->getUrl()); + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + $request = $client->get('/foo'); + $response = $request->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + // Ensure that two requests were sent + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/foo', $requests[0]->getResource()); + $this->assertEquals('GET', $requests[0]->getMethod()); + $this->assertEquals('/redirect1', $requests[1]->getResource()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('/redirect2', $requests[2]->getResource()); + $this->assertEquals('GET', $requests[2]->getMethod()); + + // Ensure that the redirect count was incremented + $this->assertEquals(2, $request->getParams()->get(RedirectPlugin::REDIRECT_COUNT)); + $this->assertCount(3, $history); + $requestHistory = $history->getAll(); + + $this->assertEquals(301, $requestHistory[0]['response']->getStatusCode()); + $this->assertEquals('/redirect1', (string) $requestHistory[0]['response']->getHeader('Location')); + $this->assertEquals(301, $requestHistory[1]['response']->getStatusCode()); + $this->assertEquals('/redirect2', (string) $requestHistory[1]['response']->getHeader('Location')); + $this->assertEquals(200, $requestHistory[2]['response']->getStatusCode()); + } + + public function testCanLimitNumberOfRedirects() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" + )); + + try { + $client = new Client($this->getServer()->getUrl()); + $client->get('/foo')->send(); + $this->fail('Did not throw expected exception'); + } catch (TooManyRedirectsException $e) { + $this->assertContains( + "5 redirects were issued for this request:\nGET /foo HTTP/1.1\r\n", + $e->getMessage() + ); + } + } + + public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $client->post('/foo', array('X-Baz' => 'bar'), 'testing')->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('GET', $requests[2]->getMethod()); + } + + public function testCanRedirectWithStrictRfcCompliance() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo', array('X-Baz' => 'bar'), 'testing'); + $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('POST', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('POST', $requests[2]->getMethod()); + } + + public function testRedirect303WithGet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo'); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRedirect303WithGetWithStrictRfcCompliance() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo'); + $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRewindsStreamWhenRedirectingIfNeeded() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(true); + $body = EntityBody::factory('foo'); + $body->read(1); + $request->setBody($body); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('foo', (string) $requests[0]->getBody()); + } + + /** + * @expectedException \Guzzle\Http\Exception\CouldNotRewindStreamException + */ + public function testThrowsExceptionWhenStreamCannotBeRewound() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(true); + $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); + $body->read(1); + $request->setBody($body)->send(); + } + + public function testRedirectsCanBeDisabledPerRequest() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(false, 0); + $this->assertEquals(301, $request->send()->getStatusCode()); + } + + public function testCanRedirectWithNoLeadingSlashAndQuery() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect?foo=bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('?foo=bar'); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $requests[0]->getUrl()); + $this->assertEquals($this->getServer()->getUrl() . 'redirect?foo=bar', $requests[1]->getUrl()); + // Ensure that the history on the actual request is correct + $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $request->getUrl()); + } + + public function testRedirectWithStrictRfc386Compliance() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/foo'); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/redirect', $requests[1]->getResource()); + } + + public function testResetsHistoryEachSend() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + // Create a client that uses the default redirect behavior + $client = new Client($this->getServer()->getUrl()); + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + $request = $client->get('/foo'); + $response = $request->send(); + $this->assertEquals(3, count($history)); + $this->assertTrue($request->getParams()->hasKey('redirect.count')); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + $request->send(); + $this->assertFalse($request->getParams()->hasKey('redirect.count')); + } + + public function testHandlesRedirectsWithSpacesProperly() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/foo'); + $request->send(); + $reqs = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/redirect%201', $reqs[1]->getResource()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php new file mode 100644 index 000000000..94eb59a4d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php @@ -0,0 +1,191 @@ +port = $port ?: self::DEFAULT_PORT; + $this->client = new Client($this->getUrl()); + register_shutdown_function(array($this, 'stop')); + } + + /** + * Flush the received requests from the server + * @throws RuntimeException + */ + public function flush() + { + $this->client->delete('guzzle-server/requests')->send(); + } + + /** + * Queue an array of responses or a single response on the server. + * + * Any currently queued responses will be overwritten. Subsequent requests + * on the server will return queued responses in FIFO order. + * + * @param array|Response $responses A single or array of Responses to queue + * @throws BadResponseException + */ + public function enqueue($responses) + { + $data = array(); + foreach ((array) $responses as $response) { + + // Create the response object from a string + if (is_string($response)) { + $response = Response::fromMessage($response); + } elseif (!($response instanceof Response)) { + throw new BadResponseException('Responses must be strings or implement Response'); + } + + $data[] = array( + 'statusCode' => $response->getStatusCode(), + 'reasonPhrase' => $response->getReasonPhrase(), + 'headers' => $response->getHeaders()->toArray(), + 'body' => $response->getBody(true) + ); + } + + $request = $this->client->put('guzzle-server/responses', null, json_encode($data)); + $request->send(); + } + + /** + * Check if the server is running + * + * @return bool + */ + public function isRunning() + { + if ($this->running) { + return true; + } + + try { + $this->client->get('guzzle-server/perf', array(), array('timeout' => 5))->send(); + $this->running = true; + return true; + } catch (\Exception $e) { + return false; + } + } + + /** + * Get the URL to the server + * + * @return string + */ + public function getUrl() + { + return 'http://127.0.0.1:' . $this->getPort() . '/'; + } + + /** + * Get the port that the server is listening on + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Get all of the received requests + * + * @param bool $hydrate Set to TRUE to turn the messages into + * actual {@see RequestInterface} objects. If $hydrate is FALSE, + * requests will be returned as strings. + * + * @return array + * @throws RuntimeException + */ + public function getReceivedRequests($hydrate = false) + { + $response = $this->client->get('guzzle-server/requests')->send(); + $data = array_filter(explode(self::REQUEST_DELIMITER, $response->getBody(true))); + if ($hydrate) { + $data = array_map(function($message) { + return RequestFactory::getInstance()->fromMessage($message); + }, $data); + } + + return $data; + } + + /** + * Start running the node.js server in the background + */ + public function start() + { + if (!$this->isRunning()) { + exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR + . 'server.js ' . $this->port + . ' >> /tmp/server.log 2>&1 &'); + // Wait at most 5 seconds for the server the setup before + // proceeding. + $start = time(); + while (!$this->isRunning() && time() - $start < 5); + if (!$this->running) { + throw new RuntimeException( + 'Unable to contact server.js. Have you installed node.js v0.5.0+? node must be in your path.' + ); + } + } + } + + /** + * Stop running the node.js server + */ + public function stop() + { + if (!$this->isRunning()) { + return false; + } + + $this->running = false; + $this->client->delete('guzzle-server')->send(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php new file mode 100644 index 000000000..091314bb7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php @@ -0,0 +1,67 @@ +assertTrue(class_exists('FooBazBar')); + $this->assertSame($client, $this->readAttribute('Guzzle\Http\StaticClient', 'client')); + } + + public function requestProvider() + { + return array_map( + function ($m) { return array($m); }, + array('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS') + ); + } + + /** + * @dataProvider requestProvider + */ + public function testSendsRequests($method) + { + $mock = new MockPlugin(array(new Response(200))); + call_user_func('Guzzle\Http\StaticClient::' . $method, 'http://foo.com', array( + 'plugins' => array($mock) + )); + $requests = $mock->getReceivedRequests(); + $this->assertCount(1, $requests); + $this->assertEquals($method, $requests[0]->getMethod()); + } + + public function testCanCreateStreamsUsingDefaultFactory() + { + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); + $stream = StaticClient::get($this->getServer()->getUrl(), array('stream' => true)); + $this->assertInstanceOf('Guzzle\Stream\StreamInterface', $stream); + $this->assertEquals('test', (string) $stream); + } + + public function testCanCreateStreamsUsingCustomFactory() + { + $stream = $this->getMockBuilder('Guzzle\Stream\StreamRequestFactoryInterface') + ->setMethods(array('fromRequest')) + ->getMockForAbstractClass(); + $resource = new Stream(fopen('php://temp', 'r+')); + $stream->expects($this->once()) + ->method('fromRequest') + ->will($this->returnValue($resource)); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); + $result = StaticClient::get($this->getServer()->getUrl(), array('stream' => $stream)); + $this->assertSame($resource, $result); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php new file mode 100644 index 000000000..28f26718b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php @@ -0,0 +1,303 @@ +assertEquals('', (string) $url); + } + + public function testPortIsDeterminedFromScheme() + { + $this->assertEquals(80, Url::factory('http://www.test.com/')->getPort()); + $this->assertEquals(443, Url::factory('https://www.test.com/')->getPort()); + $this->assertEquals(null, Url::factory('ftp://www.test.com/')->getPort()); + $this->assertEquals(8192, Url::factory('http://www.test.com:8192/')->getPort()); + } + + public function testCloneCreatesNewInternalObjects() + { + $u1 = Url::factory('http://www.test.com/'); + $u2 = clone $u1; + $this->assertNotSame($u1->getQuery(), $u2->getQuery()); + } + + public function testValidatesUrlPartsInFactory() + { + $url = Url::factory('/index.php'); + $this->assertEquals('/index.php', (string) $url); + $this->assertFalse($url->isAbsolute()); + + $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; + $u = Url::factory($url); + $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); + $this->assertTrue($u->isAbsolute()); + } + + public function testAllowsFalsyUrlParts() + { + $url = Url::factory('http://0:50/0?0#0'); + $this->assertSame('0', $url->getHost()); + $this->assertEquals(50, $url->getPort()); + $this->assertSame('/0', $url->getPath()); + $this->assertEquals('0', (string) $url->getQuery()); + $this->assertSame('0', $url->getFragment()); + $this->assertEquals('http://0:50/0?0#0', (string) $url); + + $url = Url::factory(''); + $this->assertSame('', (string) $url); + + $url = Url::factory('0'); + $this->assertSame('0', (string) $url); + } + + public function testBuildsRelativeUrlsWithFalsyParts() + { + $url = Url::buildUrl(array( + 'host' => '0', + 'path' => '0', + )); + + $this->assertSame('//0/0', $url); + + $url = Url::buildUrl(array( + 'path' => '0', + )); + $this->assertSame('0', $url); + } + + public function testUrlStoresParts() + { + $url = Url::factory('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); + $this->assertEquals('http', $url->getScheme()); + $this->assertEquals('test', $url->getUsername()); + $this->assertEquals('pass', $url->getPassword()); + $this->assertEquals('www.test.com', $url->getHost()); + $this->assertEquals(8081, $url->getPort()); + $this->assertEquals('/path/path2/', $url->getPath()); + $this->assertEquals('fragment', $url->getFragment()); + $this->assertEquals('a=1&b=2', (string) $url->getQuery()); + + $this->assertEquals(array( + 'fragment' => 'fragment', + 'host' => 'www.test.com', + 'pass' => 'pass', + 'path' => '/path/path2/', + 'port' => 8081, + 'query' => 'a=1&b=2', + 'scheme' => 'http', + 'user' => 'test' + ), $url->getParts()); + } + + public function testHandlesPathsCorrectly() + { + $url = Url::factory('http://www.test.com'); + $this->assertEquals('', $url->getPath()); + $url->setPath('test'); + $this->assertEquals('test', $url->getPath()); + + $url->setPath('/test/123/abc'); + $this->assertEquals(array('test', '123', 'abc'), $url->getPathSegments()); + + $parts = parse_url('http://www.test.com/test'); + $parts['path'] = ''; + $this->assertEquals('http://www.test.com', Url::buildUrl($parts)); + $parts['path'] = 'test'; + $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); + } + + public function testAddsQueryStringIfPresent() + { + $this->assertEquals('?foo=bar', Url::buildUrl(array( + 'query' => 'foo=bar' + ))); + } + + public function testAddsToPath() + { + // Does nothing here + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(false)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(null)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(array())); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(new \stdClass())); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('')); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/')); + $this->assertEquals('http://e.com/baz/foo', (string) Url::factory('http://e.com/baz/')->addPath('foo')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('relative')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/relative')); + $this->assertEquals('http://e.com/base/0', (string) Url::factory('http://e.com/base')->addPath('0')); + $this->assertEquals('http://e.com/base/0/1', (string) Url::factory('http://e.com/base')->addPath('0')->addPath('1')); + } + + /** + * URL combination data provider + * + * @return array + */ + public function urlCombineDataProvider() + { + return array( + array('http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'), + array('http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'), + array('http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'), + array('http://www.example.com/path', 'more', 'http://www.example.com/path/more'), + array('http://www.example.com/path', 'more?q=1', 'http://www.example.com/path/more?q=1'), + array('http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'), + array('http://www.example.com/path', 'http://test.com', 'http://test.com'), + array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com'), + array('http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'), + array('http://u:a@www.example.com/path', 'test', 'http://u:a@www.example.com/path/test'), + array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'), + array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'), + array('http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'), + array('http://www.example.com/?foo=bar', 'some/path', 'http://www.example.com/some/path?foo=bar'), + array('http://www.example.com/?foo=bar', 'some/path?boo=moo', 'http://www.example.com/some/path?boo=moo&foo=bar'), + array('http://www.example.com/some/', 'path?foo=bar&foo=baz', 'http://www.example.com/some/path?foo=bar&foo=baz'), + ); + } + + /** + * @dataProvider urlCombineDataProvider + */ + public function testCombinesUrls($a, $b, $c) + { + $this->assertEquals($c, (string) Url::factory($a)->combine($b)); + } + + public function testHasGettersAndSetters() + { + $url = Url::factory('http://www.test.com/'); + $this->assertEquals('example.com', $url->setHost('example.com')->getHost()); + $this->assertEquals('8080', $url->setPort(8080)->getPort()); + $this->assertEquals('/foo/bar', $url->setPath(array('foo', 'bar'))->getPath()); + $this->assertEquals('a', $url->setPassword('a')->getPassword()); + $this->assertEquals('b', $url->setUsername('b')->getUsername()); + $this->assertEquals('abc', $url->setFragment('abc')->getFragment()); + $this->assertEquals('https', $url->setScheme('https')->getScheme()); + $this->assertEquals('a=123', (string) $url->setQuery('a=123')->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?a=123#abc', (string) $url); + $this->assertEquals('b=boo', (string) $url->setQuery(new QueryString(array( + 'b' => 'boo' + )))->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?b=boo#abc', (string) $url); + } + + public function testSetQueryAcceptsArray() + { + $url = Url::factory('http://www.test.com'); + $url->setQuery(array('a' => 'b')); + $this->assertEquals('http://www.test.com?a=b', (string) $url); + } + + public function urlProvider() + { + return array( + array('/foo/..', '/'), + array('//foo//..', '/'), + array('/foo/../..', '/'), + array('/foo/../.', '/'), + array('/./foo/..', '/'), + array('/./foo', '/foo'), + array('/./foo/', '/foo/'), + array('/./foo/bar/baz/pho/../..', '/foo/bar'), + array('*', '*'), + array('/foo', '/foo'), + array('/abc/123/../foo/', '/abc/foo/'), + array('/a/b/c/./../../g', '/a/g'), + array('/b/c/./../../g', '/g'), + array('/b/c/./../../g', '/g'), + array('/c/./../../g', '/g'), + array('/./../../g', '/g'), + ); + } + + /** + * @dataProvider urlProvider + */ + public function testNormalizesPaths($path, $result) + { + $url = Url::factory('http://www.example.com/'); + $url->setPath($path)->normalizePath(); + $this->assertEquals($result, $url->getPath()); + } + + public function testSettingHostWithPortModifiesPort() + { + $url = Url::factory('http://www.example.com'); + $url->setHost('foo:8983'); + $this->assertEquals('foo', $url->getHost()); + $this->assertEquals(8983, $url->getPort()); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesUrlCanBeParsed() + { + Url::factory('foo:////'); + } + + public function testConvertsSpecialCharsInPathWhenCastingToString() + { + $url = Url::factory('http://foo.com/baz bar?a=b'); + $url->addPath('?'); + $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); + } + + /** + * @link http://tools.ietf.org/html/rfc3986#section-5.4.1 + */ + public function rfc3986UrlProvider() + { + $result = array( + array('g', 'http://a/b/c/g'), + array('./g', 'http://a/b/c/g'), + array('g/', 'http://a/b/c/g/'), + array('/g', 'http://a/g'), + array('?y', 'http://a/b/c/d;p?y'), + array('g?y', 'http://a/b/c/g?y'), + array('#s', 'http://a/b/c/d;p?q#s'), + array('g#s', 'http://a/b/c/g#s'), + array('g?y#s', 'http://a/b/c/g?y#s'), + array(';x', 'http://a/b/c/;x'), + array('g;x', 'http://a/b/c/g;x'), + array('g;x?y#s', 'http://a/b/c/g;x?y#s'), + array('', 'http://a/b/c/d;p?q'), + array('.', 'http://a/b/c'), + array('./', 'http://a/b/c/'), + array('..', 'http://a/b'), + array('../', 'http://a/b/'), + array('../g', 'http://a/b/g'), + array('../..', 'http://a/'), + array('../../', 'http://a/'), + array('../../g', 'http://a/g') + ); + + // This support was added in PHP 5.4.7: https://bugs.php.net/bug.php?id=62844 + if (version_compare(PHP_VERSION, '5.4.7', '>=')) { + $result[] = array('//g', 'http://g'); + } + + return $result; + } + + /** + * @dataProvider rfc3986UrlProvider + */ + public function testCombinesUrlsUsingRfc3986($relative, $result) + { + $a = Url::factory('http://a/b/c/d;p?q'); + $b = Url::factory($relative); + $this->assertEquals($result, trim((string) $a->combine($b, true), '=')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js new file mode 100644 index 000000000..4156f1aad --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js @@ -0,0 +1,146 @@ +/** + * Guzzle node.js test server to return queued responses to HTTP requests and + * expose a RESTful API for enqueueing responses and retrieving the requests + * that have been received. + * + * - Delete all requests that have been received: + * DELETE /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Enqueue responses + * PUT /guzzle-server/responses + * Host: 127.0.0.1:8124 + * + * [{ "statusCode": 200, "reasonPhrase": "OK", "headers": {}, "body": "" }] + * + * - Get the received requests + * GET /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Shutdown the server + * DELETE /guzzle-server + * Host: 127.0.0.1:8124 + * + * @package Guzzle PHP + * @license See the LICENSE file that was distributed with this source code. + */ + +var http = require("http"); + +/** + * Guzzle node.js server + * @class + */ +var GuzzleServer = function(port, log) { + + this.port = port; + this.log = log; + this.responses = []; + this.requests = []; + var that = this; + + var controlRequest = function(request, req, res) { + if (req.url == '/guzzle-server/perf') { + res.writeHead(200, "OK", {"Content-Length": 16}); + res.end("Body of response"); + } else if (req.method == "DELETE") { + if (req.url == "/guzzle-server/requests") { + // Clear the received requests + that.requests = []; + res.writeHead(200, "OK", { "Content-Length": 0 }); + res.end(); + if (this.log) { + console.log("Flushing requests"); + } + } else if (req.url == "/guzzle-server") { + // Shutdown the server + res.writeHead(200, "OK", { "Content-Length": 0, "Connection": "close" }); + res.end(); + if (this.log) { + console.log("Shutting down"); + } + that.server.close(); + } + } else if (req.method == "GET") { + if (req.url === "/guzzle-server/requests") { + // Get received requests + var data = that.requests.join("\n----[request]\n"); + res.writeHead(200, "OK", { "Content-Length": data.length }); + res.end(data); + if (that.log) { + console.log("Sending receiving requests"); + } + } + } else if (req.method == "PUT") { + if (req.url == "/guzzle-server/responses") { + if (that.log) { + console.log("Adding responses..."); + } + // Received response to queue + var data = request.split("\r\n\r\n")[1]; + if (!data) { + if (that.log) { + console.log("No response data was provided"); + } + res.writeHead(400, "NO RESPONSES IN REQUEST", { "Content-Length": 0 }); + } else { + that.responses = eval("(" + data + ")"); + if (that.log) { + console.log(that.responses); + } + res.writeHead(200, "OK", { "Content-Length": 0 }); + } + res.end(); + } + } + }; + + var receivedRequest = function(request, req, res) { + if (req.url.indexOf("/guzzle-server") === 0) { + controlRequest(request, req, res); + } else if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) { + res.writeHead(500); + res.end("No responses in queue"); + } else { + var response = that.responses.shift(); + res.writeHead(response.statusCode, response.reasonPhrase, response.headers); + res.end(response.body); + that.requests.push(request); + } + }; + + this.start = function() { + + that.server = http.createServer(function(req, res) { + + var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n"; + for (var i in req.headers) { + request += i + ": " + req.headers[i] + "\r\n"; + } + request += "\r\n"; + + // Receive each chunk of the request body + req.addListener("data", function(chunk) { + request += chunk; + }); + + // Called when the request completes + req.addListener("end", function() { + receivedRequest(request, req, res); + }); + }); + that.server.listen(port, "127.0.0.1"); + + if (this.log) { + console.log("Server running at http://127.0.0.1:8124/"); + } + }; +}; + +// Get the port from the arguments +port = process.argv.length >= 3 ? process.argv[2] : 8124; +log = process.argv.length >= 4 ? process.argv[3] : false; + +// Start the server +server = new GuzzleServer(port, log); +server.start(); diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php new file mode 100644 index 000000000..990c0af66 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php @@ -0,0 +1,37 @@ +assertSame(Inflector::getDefault(), Inflector::getDefault()); + } + + public function testSnake() + { + $this->assertEquals('camel_case', Inflector::getDefault()->snake('camelCase')); + $this->assertEquals('camel_case', Inflector::getDefault()->snake('CamelCase')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCaseWords')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCase_words')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('expect100_continue', Inflector::getDefault()->snake('Expect100Continue')); + } + + public function testCamel() + { + $this->assertEquals('CamelCase', Inflector::getDefault()->camel('camel_case')); + $this->assertEquals('CamelCaseWords', Inflector::getDefault()->camel('camel_case_words')); + $this->assertEquals('Test', Inflector::getDefault()->camel('test')); + $this->assertEquals('Expect100Continue', ucfirst(Inflector::getDefault()->camel('expect100_continue'))); + // Get from cache + $this->assertEquals('Test', Inflector::getDefault()->camel('test', false)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php new file mode 100644 index 000000000..f00b7fad8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php @@ -0,0 +1,46 @@ +getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->will($this->returnValue('foo_bar')); + $mock->expects($this->once())->method('camel')->will($this->returnValue('FooBar')); + + $inflector = new MemoizingInflector($mock); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + } + + public function testProtectsAgainstCacheOverflow() + { + $inflector = new MemoizingInflector(new Inflector(), 10); + for ($i = 1; $i < 11; $i++) { + $inflector->camel('foo_' . $i); + $inflector->snake('Foo' . $i); + } + + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(10, count($cache['snake'])); + $this->assertEquals(10, count($cache['camel'])); + + $inflector->camel('baz!'); + $inflector->snake('baz!'); + + // Now ensure that 20% of the cache was removed (2), then the item was added + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(9, count($cache['snake'])); + $this->assertEquals(9, count($cache['camel'])); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php new file mode 100644 index 000000000..ff2654cf6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->with('Test')->will($this->returnValue('test')); + $mock->expects($this->once())->method('camel')->with('Test')->will($this->returnValue('Test')); + $inflector = new PreComputedInflector($mock, array('FooBar' => 'foo_bar'), array('foo_bar' => 'FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('Test', $inflector->camel('Test')); + $this->assertEquals('test', $inflector->snake('Test')); + } + + public function testMirrorsPrecomputedValues() + { + $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array(), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + } + + public function testMirrorsPrecomputedValuesByMerging() + { + $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array('foo' => 'Foo'), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + $this->assertEquals('Foo', $inflector->camel('foo')); + $this->assertEquals('foo', $inflector->snake('Foo')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php new file mode 100644 index 000000000..8d6ae845a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php @@ -0,0 +1,29 @@ + 1, + 'b' => 2 + )); + $b = new \ArrayIterator(array()); + $c = new \ArrayIterator(array( + 'c' => 3, + 'd' => 4 + )); + $i = new AppendIterator(); + $i->append($a); + $i->append($b); + $i->append($c); + $this->assertEquals(array(1, 2, 3, 4), iterator_to_array($i, false)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php new file mode 100644 index 000000000..ec4c1294c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php @@ -0,0 +1,52 @@ +assertEquals(11, count($chunks)); + foreach ($chunks as $j => $chunk) { + $this->assertEquals(range($j * 10, min(100, $j * 10 + 9)), $chunk); + } + } + + public function testChunksIteratorWithOddValues() + { + $chunked = new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2); + $chunks = iterator_to_array($chunked, false); + $this->assertEquals(3, count($chunks)); + $this->assertEquals(array(1, 2), $chunks[0]); + $this->assertEquals(array(3, 4), $chunks[1]); + $this->assertEquals(array(5), $chunks[2]); + } + + public function testMustNotTerminateWithTraversable() + { + $traversable = simplexml_load_string('')->foo; + $chunked = new ChunkedIterator($traversable, 2); + $actual = iterator_to_array($chunked, false); + $this->assertCount(2, $actual); + } + + public function testSizeOfZeroMakesIteratorInvalid() { + $chunked = new ChunkedIterator(new \ArrayIterator(range(1, 5)), 0); + $chunked->rewind(); + $this->assertFalse($chunked->valid()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSizeLowerZeroThrowsException() { + new ChunkedIterator(new \ArrayIterator(range(1, 5)), -1); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php new file mode 100644 index 000000000..73b4f6987 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(1, 99, 2), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new FilterIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php new file mode 100644 index 000000000..4de4a6bc1 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(0, 1000, 10), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new MapIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php new file mode 100644 index 000000000..5bcf06fb0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php @@ -0,0 +1,28 @@ +append('a'); + $proxy->append('b'); + $this->assertEquals(array('a', 'b'), $i->getArrayCopy()); + $this->assertEquals(array('a', 'b'), $proxy->getArrayCopy()); + } + + public function testUsesInnerIterator() + { + $i = new MethodProxyIterator(new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2)); + $this->assertEquals(3, count(iterator_to_array($i, false))); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php new file mode 100644 index 000000000..a66882f6f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php @@ -0,0 +1,23 @@ +log('test', \LOG_NOTICE, '127.0.0.1'); + $this->assertEquals(array(array('message' => 'test', 'priority' => \LOG_NOTICE, 'extras' => '127.0.0.1')), $adapter->getLogs()); + } + + public function testClearLog() + { + $adapter = new ArrayLogAdapter(); + $adapter->log('test', \LOG_NOTICE, '127.0.0.1'); + $adapter->clearLogs(); + $this->assertEquals(array(), $adapter->getLogs()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php new file mode 100644 index 000000000..0177dc071 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php @@ -0,0 +1,30 @@ +adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that, &$modified) { + $modified = array($message, $priority, $extras); + }); + $this->adapter->log('test', LOG_NOTICE, '127.0.0.1'); + $this->assertEquals(array('test', LOG_NOTICE, '127.0.0.1'), $modified); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenNotCallable() + { + $this->adapter = new ClosureLogAdapter(123); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php new file mode 100644 index 000000000..3ff4b0737 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php @@ -0,0 +1,143 @@ +request = new EntityEnclosingRequest('POST', 'http://foo.com?q=test', array( + 'X-Foo' => 'bar', + 'Authorization' => 'Baz' + )); + $this->request->setBody(EntityBody::factory('Hello')); + + $this->response = new Response(200, array( + 'X-Test' => 'Abc' + ), 'Foo'); + + $this->handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') + ->disableOriginalConstructor() + ->setMethods(array('getError', 'getErrorNo', 'getStderr', 'getInfo')) + ->getMock(); + + $this->handle->expects($this->any()) + ->method('getError') + ->will($this->returnValue('e')); + + $this->handle->expects($this->any()) + ->method('getErrorNo') + ->will($this->returnValue('123')); + + $this->handle->expects($this->any()) + ->method('getStderr') + ->will($this->returnValue('testing')); + + $this->handle->expects($this->any()) + ->method('getInfo') + ->will($this->returnValueMap(array( + array(CURLINFO_CONNECT_TIME, '123'), + array(CURLINFO_TOTAL_TIME, '456') + ))); + } + + public function logProvider() + { + return array( + // Uses the cache for the second time + array('{method} - {method}', 'POST - POST'), + array('{url}', 'http://foo.com?q=test'), + array('{port}', '80'), + array('{resource}', '/?q=test'), + array('{host}', 'foo.com'), + array('{hostname}', gethostname()), + array('{protocol}/{version}', 'HTTP/1.1'), + array('{code} {phrase}', '200 OK'), + array('{req_header_Foo}', ''), + array('{req_header_X-Foo}', 'bar'), + array('{req_header_Authorization}', 'Baz'), + array('{res_header_foo}', ''), + array('{res_header_X-Test}', 'Abc'), + array('{req_body}', 'Hello'), + array('{res_body}', 'Foo'), + array('{curl_stderr}', 'testing'), + array('{curl_error}', 'e'), + array('{curl_code}', '123'), + array('{connect_time}', '123'), + array('{total_time}', '456') + ); + } + + /** + * @dataProvider logProvider + */ + public function testFormatsMessages($template, $output) + { + $formatter = new MessageFormatter($template); + $this->assertEquals($output, $formatter->format($this->request, $this->response, $this->handle)); + } + + public function testFormatsRequestsAndResponses() + { + $formatter = new MessageFormatter(); + $formatter->setTemplate('{request}{response}'); + $this->assertEquals($this->request . $this->response, $formatter->format($this->request, $this->response)); + } + + public function testAddsTimestamp() + { + $formatter = new MessageFormatter('{ts}'); + $this->assertNotEmpty($formatter->format($this->request, $this->response)); + } + + public function testUsesResponseWhenNoHandleAndGettingCurlInformation() + { + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setConstructorArgs(array(200)) + ->setMethods(array('getInfo')) + ->getMock(); + $response->expects($this->exactly(2)) + ->method('getInfo') + ->will($this->returnValueMap(array( + array('connect_time', '1'), + array('total_time', '2'), + ))); + $this->assertEquals('1/2', $formatter->format($this->request, $response)); + } + + public function testUsesEmptyStringWhenNoHandleAndNoResponse() + { + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $this->assertEquals('/', $formatter->format($this->request)); + } + + public function testInjectsTotalTime() + { + $out = ''; + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $adapter = new ClosureLogAdapter(function ($m) use (&$out) { $out .= $m; }); + $log = new LogPlugin($adapter, $formatter); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI"); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber($log); + $client->get('/')->send(); + $this->assertNotEquals('/', $out); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php new file mode 100644 index 000000000..7b72dd6a0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php @@ -0,0 +1,25 @@ +pushHandler($handler); + $adapter = new PsrLogAdapter($log); + $adapter->log('test!', LOG_INFO); + $this->assertTrue($handler->hasInfoRecords()); + $this->assertSame($log, $adapter->getLogObject()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php new file mode 100644 index 000000000..1b6128365 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php @@ -0,0 +1,51 @@ +stream = fopen('php://temp', 'r+'); + $this->log = new Logger(); + $this->log->addWriter(new Stream($this->stream)); + $this->adapter = new Zf2LogAdapter($this->log); + + } + + public function testLogsMessagesToAdaptedObject() + { + // Test without a priority + $this->adapter->log('Zend_Test!', \LOG_NOTICE); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(1, substr_count($contents, 'Zend_Test!')); + + // Test with a priority + $this->adapter->log('Zend_Test!', \LOG_ALERT); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(2, substr_count($contents, 'Zend_Test!')); + } + + public function testExposesAdaptedLogObject() + { + $this->assertEquals($this->log, $this->adapter->getLogObject()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php new file mode 100644 index 000000000..3fb6527be --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php @@ -0,0 +1,21 @@ +command = $command; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php new file mode 100644 index 000000000..aabb15f9c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php @@ -0,0 +1,25 @@ +command = $command; + $this->response = $response; + $this->message = 'Error from ' . $response; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php new file mode 100644 index 000000000..97a197487 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php @@ -0,0 +1,11 @@ +multiHandle; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php new file mode 100644 index 000000000..11e22eb46 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php @@ -0,0 +1,65 @@ +events as $event) { + if ($event->getName() == $eventName) { + return true; + } + } + + return false; + } + + public function getLastEvent() + { + return end($this->events); + } + + public function count() + { + return count($this->events); + } + + public function getGrouped() + { + $events = array(); + foreach ($this->events as $event) { + if (!isset($events[$event->getName()])) { + $events[$event->getName()] = array(); + } + $events[$event->getName()][] = $event; + } + + return $events; + } + + public function getData($event, $key, $occurrence = 0) + { + $grouped = $this->getGrouped(); + if (isset($grouped[$event])) { + return $grouped[$event][$occurrence][$key]; + } + + return null; + } + + public function update(Event $event) + { + $this->events[] = $event; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php new file mode 100644 index 000000000..e011959bb --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php @@ -0,0 +1,7 @@ + 'allseeing-i.com', + 'path' => '/', + 'data' => array( + 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c' + ), + 'max_age' => NULL, + 'expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', + 'version' => NULL, + 'secure' => NULL, + 'discard' => NULL, + 'port' => NULL, + 'cookies' => array( + 'ASIHTTPRequestTestCookie' => 'This+is+the+value' + ), + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array('', false), + array('foo', false), + // Test setting a blank value for a cookie + array(array( + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), + array( + 'cookies' => array( + 'foo' => '' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting a value and removing quotes + array(array( + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), + array( + 'cookies' => array( + 'foo' => '1' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting multiple values + array(array( + 'foo=1; bar=2;', 'foo =1; bar = "2"', 'foo=1; bar=2'), + array( + 'cookies' => array( + 'foo' => '1', + 'bar' => '2', + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Tests getting the domain and path from a reference request + array(array( + 'foo=1; port="80,8081"; httponly', 'foo=1; port="80,8081"; domain=www.test.com; HttpOnly;', 'foo=1; ; domain=www.test.com; path=/path; port="80,8081"; HttpOnly;'), + array( + 'cookies' => array( + 'foo' => 1 + ), + 'data' => array(), + 'discard' => null, + 'domain' => 'www.test.com', + 'expires' => null, + 'max_age' => null, + 'path' => '/path', + 'port' => array('80', '8081'), + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => true + ), + 'http://www.test.com/path/' + ), + // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php + array( + 'justacookie=foo; domain=example.com', + array( + 'cookies' => array( + 'justacookie' => 'foo' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', + array( + 'cookies' => array( + 'expires' => 'tomorrow' + ), + 'domain' => '.example.com', + 'path' => '/Space Out/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'data' => array(), + 'discard' => null, + 'port' => null, + 'secure' => true, + 'version' => null, + 'max_age' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', + array( + 'cookies' => array( + 'domain' => 'unittests' + ), + 'domain' => 'example.com', + 'path' => '/some value/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', + array( + 'cookies' => array( + 'path' => 'indexAction' + ), + 'domain' => '.foo.com', + 'path' => '/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', + array( + 'cookies' => array( + 'secure' => 'sha1' + ), + 'domain' => 'some.really.deep.domain.com', + 'path' => '/', + 'secure' => true, + 'data' => array(), + 'discard' => null, + 'expires' => time() + 86400, + 'max_age' => 86400, + 'port' => null, + 'version' => 1, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', + array( + 'cookies' => array( + 'PHPSESSID' => '123456789+abcd%2Cef' + ), + 'domain' => '.localdomain', + 'path' => '/foo/baz', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => true, + 'data' => array(), + 'discard' => true, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // rfc6265#section-5.1.4 + array( + 'cookie=value', + array( + 'cookies' => array( + 'cookie' => 'value' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/some/path', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/some/path/test.html' + ), + array( + 'empty=path', + array( + 'cookies' => array( + 'empty' => 'path' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/test.html' + ), + array( + 'baz=qux', + array( + 'cookies' => array( + 'baz' => 'qux' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com?query=here' + ), + array( + 'test=noSlashPath; path=someString', + array( + 'cookies' => array( + 'test' => 'noSlashPath' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/real/path', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/real/path/' + ), + ); + } + + /** + * @dataProvider cookieParserDataProvider + */ + public function testParseCookie($cookie, $parsed, $url = null) + { + $c = $this->cookieParserClass; + $parser = new $c(); + + $request = null; + if ($url) { + $url = Url::factory($url); + $host = $url->getHost(); + $path = $url->getPath(); + } else { + $host = ''; + $path = ''; + } + + foreach ((array) $cookie as $c) { + $p = $parser->parseCookie($c, $host, $path); + + // Remove expires values from the assertion if they are relatively equal by allowing a 5 minute difference + if ($p['expires'] != $parsed['expires']) { + if (abs($p['expires'] - $parsed['expires']) < 300) { + unset($p['expires']); + unset($parsed['expires']); + } + } + + if (is_array($parsed)) { + foreach ($parsed as $key => $value) { + $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + + foreach ($p as $key => $value) { + $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + } else { + $this->assertEquals($parsed, $p); + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php new file mode 100644 index 000000000..75d336fa5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php @@ -0,0 +1,22 @@ +parseCookie('foo=baz+bar', null, null, true); + $this->assertEquals(array( + 'foo' => 'baz bar' + ), $result['cookies']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php new file mode 100644 index 000000000..da58bb465 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php @@ -0,0 +1,225 @@ + 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => '', + 'port' => '', + 'path' => '/', + 'query' => '' + ), + 'headers' => array(), + 'body' => '' + )), + // Path and query string, multiple header values per header and case sensitive storage + array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( + 'method' => 'HEAD', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '', + 'path' => '/path', + 'query' => 'query=foo' + ), + 'headers' => array( + 'Host' => 'example.com', + 'X-Foo' => array('foo', 'foo', 'Baz'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + // Includes a body + array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( + 'method' => 'PUT', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'https', + 'host' => 'example.com', + 'port' => '443', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'host' => 'example.com:443', + 'Content-Length' => '4' + ), + 'body' => 'test' + )), + // Includes Authorization headers + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'Authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + // Include authorization header + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + ); + } + + public function responseProvider() + { + return array( + // Empty request + array('', false), + + array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'OK', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '400', + 'reason_phrase' => 'Bad Request', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 100 Continue\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '100', + 'reason_phrase' => 'Continue', + 'headers' => array(), + 'body' => '' + )), + array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '204', + 'reason_phrase' => 'No Content', + 'headers' => array( + 'X-Foo' => array('foo', 'foo'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'Ok that is great!', + 'headers' => array( + 'Content-Length' => 4 + ), + 'body' => 'Test' + )), + ); + } + + public function compareRequestResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['method'], $expected['method']); + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['request_url'], $expected['request_url']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + public function compareResponseResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['code'], $expected['code']); + $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + protected function normalizeHeaders($headers) + { + $normalized = array(); + foreach ($headers as $key => $value) { + $key = strtolower($key); + if (!isset($normalized[$key])) { + $normalized[$key] = $value; + } elseif (!is_array($normalized[$key])) { + $normalized[$key] = array($value); + } else { + $normalized[$key][] = $value; + } + } + + foreach ($normalized as $key => &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $normalized; + } + + public function compareHttpHeaders($result, $expected) + { + // Aggregate all headers case-insensitively + $result = $this->normalizeHeaders($result); + $expected = $this->normalizeHeaders($expected); + $this->assertEquals($result, $expected); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php new file mode 100644 index 000000000..2f5222893 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php @@ -0,0 +1,58 @@ +compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new MessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } + + public function testParsesRequestsWithMissingProtocol() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } + + public function testParsesRequestsWithMissingVersion() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } + + public function testParsesResponsesWithMissingReasonPhrase() + { + $parser = new MessageParser(); + $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); + $this->assertEquals('200', $parts['code']); + $this->assertEquals('', $parts['reason_phrase']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php new file mode 100644 index 000000000..6706e2063 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php @@ -0,0 +1,36 @@ +markTestSkipped('pecl_http is not available.'); + } + } + + /** + * @dataProvider requestProvider + */ + public function testParsesRequests($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php new file mode 100644 index 000000000..7675efb96 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php @@ -0,0 +1,33 @@ +registerParser('foo', $c); + $this->assertSame($c, $r->getParser('foo')); + } + + public function testReturnsNullWhenNotFound() + { + $r = new ParserRegistry(); + $this->assertNull($r->getParser('FOO')); + } + + public function testReturnsLazyLoadedDefault() + { + $r = new ParserRegistry(); + $c = $r->getParser('cookie'); + $this->assertInstanceOf('Guzzle\Parser\Cookie\CookieParser', $c); + $this->assertSame($c, $r->getParser('cookie')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php new file mode 100644 index 000000000..a05fc2e4d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php @@ -0,0 +1,113 @@ + 'value', + 'hello' => 'Hello World!', + 'empty' => '', + 'path' => '/foo/bar', + 'x' => '1024', + 'y' => '768', + 'null' => null, + 'list' => array('red', 'green', 'blue'), + 'keys' => array( + "semi" => ';', + "dot" => '.', + "comma" => ',' + ), + 'empty_keys' => array(), + ); + + return array_map(function($t) use ($params) { + $t[] = $params; + return $t; + }, array( + array('foo', 'foo'), + array('{var}', 'value'), + array('{hello}', 'Hello%20World%21'), + array('{+var}', 'value'), + array('{+hello}', 'Hello%20World!'), + array('{+path}/here', '/foo/bar/here'), + array('here?ref={+path}', 'here?ref=/foo/bar'), + array('X{#var}', 'X#value'), + array('X{#hello}', 'X#Hello%20World!'), + array('map?{x,y}', 'map?1024,768'), + array('{x,hello,y}', '1024,Hello%20World%21,768'), + array('{+x,hello,y}', '1024,Hello%20World!,768'), + array('{+path,x}/here', '/foo/bar,1024/here'), + array('{#x,hello,y}', '#1024,Hello%20World!,768'), + array('{#path,x}/here', '#/foo/bar,1024/here'), + array('X{.var}', 'X.value'), + array('X{.x,y}', 'X.1024.768'), + array('{/var}', '/value'), + array('{/var,x}/here', '/value/1024/here'), + array('{;x,y}', ';x=1024;y=768'), + array('{;x,y,empty}', ';x=1024;y=768;empty'), + array('{?x,y}', '?x=1024&y=768'), + array('{?x,y,empty}', '?x=1024&y=768&empty='), + array('?fixed=yes{&x}', '?fixed=yes&x=1024'), + array('{&x,y,empty}', '&x=1024&y=768&empty='), + array('{var:3}', 'val'), + array('{var:30}', 'value'), + array('{list}', 'red,green,blue'), + array('{list*}', 'red,green,blue'), + array('{keys}', 'semi,%3B,dot,.,comma,%2C'), + array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), + array('{+path:6}/here', '/foo/b/here'), + array('{+list}', 'red,green,blue'), + array('{+list*}', 'red,green,blue'), + array('{+keys}', 'semi,;,dot,.,comma,,'), + array('{+keys*}', 'semi=;,dot=.,comma=,'), + array('{#path:6}/here', '#/foo/b/here'), + array('{#list}', '#red,green,blue'), + array('{#list*}', '#red,green,blue'), + array('{#keys}', '#semi,;,dot,.,comma,,'), + array('{#keys*}', '#semi=;,dot=.,comma=,'), + array('X{.var:3}', 'X.val'), + array('X{.list}', 'X.red,green,blue'), + array('X{.list*}', 'X.red.green.blue'), + array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), + array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), + array('{/var:1,var}', '/v/value'), + array('{/list}', '/red,green,blue'), + array('{/list*}', '/red/green/blue'), + array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), + array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), + array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), + array('{;hello:5}', ';hello=Hello'), + array('{;list}', ';list=red,green,blue'), + array('{;list*}', ';list=red;list=green;list=blue'), + array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), + array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), + array('{?var:3}', '?var=val'), + array('{?list}', '?list=red,green,blue'), + array('{?list*}', '?list=red&list=green&list=blue'), + array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), + array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), + array('{&var:3}', '&var=val'), + array('{&list}', '&list=red,green,blue'), + array('{&list*}', '&list=red&list=green&list=blue'), + array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), + array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), + array('{.null}', ''), + array('{.null,var}', '.value'), + array('X{.empty_keys*}', 'X'), + array('X{.empty_keys}', 'X'), + // Test that missing expansions are skipped + array('test{&missing*}', 'test'), + // Test that multiple expansions can be set + array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + // Test more complex query string stuff + array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php new file mode 100644 index 000000000..633c5d539 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php @@ -0,0 +1,27 @@ +markTestSkipped('uri_template PECL extension must be installed to test PeclUriTemplate'); + } + } + + /** + * @dataProvider templateProvider + */ + public function testExpandsUriTemplates($template, $expansion, $params) + { + $uri = new PeclUriTemplate($template); + $this->assertEquals($expansion, $uri->expand($template, $params)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php new file mode 100644 index 000000000..5130d6f4b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php @@ -0,0 +1,106 @@ +assertEquals($expansion, $uri->expand($template, $params)); + } + + public function expressionProvider() + { + return array( + array( + '{+var*}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'var', 'modifier' => '*') + ) + ), + ), + array( + '{?keys,var,val}', array( + 'operator' => '?', + 'values' => array( + array('value' => 'keys', 'modifier' => ''), + array('value' => 'var', 'modifier' => ''), + array('value' => 'val', 'modifier' => '') + ) + ), + ), + array( + '{+x,hello,y}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'x', 'modifier' => ''), + array('value' => 'hello', 'modifier' => ''), + array('value' => 'y', 'modifier' => '') + ) + ) + ) + ); + } + + /** + * @dataProvider expressionProvider + */ + public function testParsesExpressions($exp, $data) + { + $template = new UriTemplate($exp); + + // Access the config object + $class = new \ReflectionClass($template); + $method = $class->getMethod('parseExpression'); + $method->setAccessible(true); + + $exp = substr($exp, 1, -1); + $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/90 + */ + public function testAllowsNestedArrayExpansion() + { + $template = new UriTemplate(); + + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => array('fun', 'ice cream') + ), + 'foo' => array( + 'baz' => array( + 'bar' => 'fizz', + 'test' => 'buzz' + ), + 'bam' => 'boo' + ) + )); + + $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/426 + */ + public function testSetRegex() + { + $template = new UriTemplate(); + $template->setRegex('/\<\$(.+)\>/'); + $this->assertSame('/foo', $template->expand('/<$a>', array('a' => 'foo'))); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php new file mode 100644 index 000000000..16990a5a8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php @@ -0,0 +1,93 @@ +assertArrayHasKey('request.before_send', $events); + $this->assertArrayHasKey('request.exception', $events); + $this->assertArrayHasKey('curl.callback.progress', $events); + } + + public function testEnablesProgressCallbacks() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $event = new Event(array( + 'request' => $request + )); + $p->onBeforeSend($event); + $this->assertEquals(true, $request->getCurlOptions()->get('progress')); + } + + public function testAddsTimesOutAfterSending() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $handle = CurlHandle::factory($request); + $event = new Event(array( + 'request' => $request, + 'handle' => $handle->getHandle(), + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + } + + public function testEnsuresRequestIsSet() + { + $p = new AsyncPlugin(); + $event = new Event(array( + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + } + + public function testMasksCurlExceptions() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $e = new CurlException('Error'); + $event = new Event(array( + 'request' => $request, + 'exception' => $e + )); + $p->onRequestTimeout($event); + $this->assertEquals(RequestInterface::STATE_COMPLETE, $request->getState()); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + } + + public function testEnsuresIntegration() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 204 FOO\r\nContent-Length: 4\r\n\r\ntest"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/', null, array( + 'foo' => 'bar' + )); + $request->getEventDispatcher()->addSubscriber(new AsyncPlugin()); + $request->send(); + $this->assertEquals('', $request->getResponse()->getBody(true)); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $received[0]->getMethod()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php new file mode 100644 index 000000000..72af26308 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php @@ -0,0 +1,86 @@ +getMockBuilder('Guzzle\Plugin\Backoff\AbstractBackoffStrategy') + ->setMethods(array('getDelay', 'makesDecision')) + ->getMockForAbstractClass(); + } + + public function testReturnsZeroWhenNoNextAndGotNull() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); + $this->assertEquals(0, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsFalse() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(false)); + $this->assertEquals(false, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsNextValueWhenNullOrTrue() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); + $mock->expects($this->any())->method('makesDecision')->will($this->returnValue(false)); + + $mock2 = $this->getMockStrategy(); + $mock2->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(10)); + $mock2->expects($this->atLeastOnce())->method('makesDecision')->will($this->returnValue(true)); + $mock->setNext($mock2); + + $this->assertEquals(10, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsFalseWhenNullAndNoNext() + { + $request = new Request('GET', 'http://www.foo.com'); + $s = new TruncatedBackoffStrategy(2); + $this->assertFalse($s->getBackoffPeriod(0, $request)); + } + + public function testHasNext() + { + $a = new TruncatedBackoffStrategy(2); + $b = new TruncatedBackoffStrategy(2); + $a->setNext($b); + $this->assertSame($b, $a->getNext()); + } + + public function testSkipsOtherDecisionsInChainWhenOneReturnsTrue() + { + $a = new CallbackBackoffStrategy(function () { return null; }, true); + $b = new CallbackBackoffStrategy(function () { return true; }, true); + $c = new CallbackBackoffStrategy(function () { return null; }, true); + $d = new CallbackBackoffStrategy(function () { return 10; }, false); + $a->setNext($b); + $b->setNext($c); + $c->setNext($d); + $this->assertEquals(10, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); + } + + public function testReturnsZeroWhenDecisionMakerReturnsTrueButNoFurtherStrategiesAreInTheChain() + { + $a = new CallbackBackoffStrategy(function () { return null; }, true); + $b = new CallbackBackoffStrategy(function () { return true; }, true); + $a->setNext($b); + $this->assertSame(0, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php new file mode 100644 index 000000000..a64dd826e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php @@ -0,0 +1,110 @@ +message = ''; + } + + public function testHasEventList() + { + $this->assertEquals(1, count(BackoffLogger::getSubscribedEvents())); + } + + public function testLogsEvents() + { + list($logPlugin, $request, $response) = $this->getMocks(); + + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setConstructorArgs(array(503)) + ->setMethods(array('getInfo')) + ->getMock(); + + $response->expects($this->any()) + ->method('getInfo') + ->will($this->returnValue(2)); + + $handle = $this->getMockHandle(); + + $event = new Event(array( + 'request' => $request, + 'response' => $response, + 'retries' => 1, + 'delay' => 3, + 'handle' => $handle + )); + + $logPlugin->onRequestRetry($event); + $this->assertContains( + '] PUT http://www.example.com - 503 Service Unavailable - Retries: 1, Delay: 3, Time: 2, 2, cURL: 30 Foo', + $this->message + ); + } + + public function testCanSetTemplate() + { + $l = new BackoffLogger(new ClosureLogAdapter(function () {})); + $l->setTemplate('foo'); + $t = $this->readAttribute($l, 'formatter'); + $this->assertEquals('foo', $this->readAttribute($t, 'template')); + } + + /** + * @return array + */ + protected function getMocks() + { + $that = $this; + $logger = new ClosureLogAdapter(function ($message) use ($that) { + $that->message .= $message . "\n"; + }); + $logPlugin = new BackoffLogger($logger); + $response = new Response(503); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com', array( + 'Content-Length' => 3, + 'Foo' => 'Bar' + )); + + return array($logPlugin, $request, $response); + } + + /** + * @return CurlHandle + */ + protected function getMockHandle() + { + $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') + ->disableOriginalConstructor() + ->setMethods(array('getError', 'getErrorNo', 'getInfo')) + ->getMock(); + + $handle->expects($this->once()) + ->method('getError') + ->will($this->returnValue('Foo')); + + $handle->expects($this->once()) + ->method('getErrorNo') + ->will($this->returnValue(30)); + + $handle->expects($this->any()) + ->method('getInfo') + ->will($this->returnValue(2)); + + return $handle; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php new file mode 100644 index 000000000..496e49eb2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php @@ -0,0 +1,297 @@ +retried = false; + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + public function onRequestRetry(Event $event) + { + $this->retried = $event; + } + + public function testHasEventList() + { + $this->assertEquals(1, count(BackoffPlugin::getAllEvents())); + } + + public function testCreatesDefaultExponentialBackoffPlugin() + { + $plugin = BackoffPlugin::getExponentialBackoff(3, array(204), array(10)); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\BackoffPlugin', $plugin); + $strategy = $this->readAttribute($plugin, 'strategy'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\TruncatedBackoffStrategy', $strategy); + $this->assertEquals(3, $this->readAttribute($strategy, 'max')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\HttpBackoffStrategy', $strategy); + $this->assertEquals(array(204 => true), $this->readAttribute($strategy, 'errorCodes')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\CurlBackoffStrategy', $strategy); + $this->assertEquals(array(10 => true), $this->readAttribute($strategy, 'errorCodes')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\ExponentialBackoffStrategy', $strategy); + } + + public function testDoesNotRetryUnlessStrategyReturnsNumber() + { + $request = new Request('GET', 'http://www.example.com'); + $request->setState('transfer'); + + $mock = $this->getMockBuilder('Guzzle\Plugin\Backoff\BackoffStrategyInterface') + ->setMethods(array('getBackoffPeriod')) + ->getMockForAbstractClass(); + + $mock->expects($this->once()) + ->method('getBackoffPeriod') + ->will($this->returnValue(false)); + + $plugin = new BackoffPlugin($mock); + $plugin->addSubscriber($this); + $plugin->onRequestSent(new Event(array('request' => $request))); + $this->assertFalse($this->retried); + } + + public function testUpdatesRequestForRetry() + { + $request = new Request('GET', 'http://www.example.com'); + $request->setState('transfer'); + $response = new Response(500); + $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')->disableOriginalConstructor()->getMock(); + $e = new CurlException(); + $e->setCurlHandle($handle); + + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); + $plugin->addSubscriber($this); + + $event = new Event(array( + 'request' => $request, + 'response' => $response, + 'exception' => $e + )); + + $plugin->onRequestSent($event); + $this->assertEquals(array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle, + 'retries' => 1, + 'delay' => 10 + ), $this->readAttribute($this->retried, 'context')); + + $plugin->onRequestSent($event); + $this->assertEquals(array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle, + 'retries' => 2, + 'delay' => 10 + ), $this->readAttribute($this->retried, 'context')); + } + + public function testDoesNothingWhenNotRetryingAndPollingRequest() + { + $request = new Request('GET', 'http://www.foo.com'); + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); + $plugin->onRequestPoll(new Event(array('request' => $request))); + } + + public function testRetriesRequests() + { + // Create a script to return several 500 and 503 response codes + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new BackoffPlugin( + new TruncatedBackoffStrategy(3, + new HttpBackoffStrategy(null, + new CurlBackoffStrategy(null, + new ConstantBackoffStrategy(0.05) + ) + ) + ) + ); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + + // Make sure it eventually completed successfully + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('data', $request->getResponse()->getBody(true)); + + // Check that three requests were made to retry this request + $this->assertEquals(3, count($this->getServer()->getReceivedRequests(false))); + $this->assertEquals(2, $request->getParams()->get(BackoffPlugin::RETRY_PARAM)); + } + + /** + * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException + */ + public function testFailsOnTruncation() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n" + )); + + $plugin = new BackoffPlugin( + new TruncatedBackoffStrategy(2, + new HttpBackoffStrategy(null, + new ConstantBackoffStrategy(0.05) + ) + ) + ); + + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber($plugin); + $client->get()->send(); + } + + public function testRetriesRequestsWhenInParallel() + { + // Create a script to return several 500 and 503 response codes + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new BackoffPlugin( + new HttpBackoffStrategy(null, + new TruncatedBackoffStrategy(3, + new CurlBackoffStrategy(null, + new ConstantBackoffStrategy(0.1) + ) + ) + ) + ); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $requests = array(); + for ($i = 0; $i < 5; $i++) { + $requests[] = $client->get(); + } + $client->send($requests); + + $this->assertEquals(15, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Plugin\Backoff\BackoffPlugin + * @covers Guzzle\Http\Curl\CurlMulti + */ + public function testRetriesPooledRequestsUsingDelayAndPollingEvent() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + // Need to sleep for some time ensure that the polling works correctly in the observer + $plugin = new BackoffPlugin(new HttpBackoffStrategy(null, + new TruncatedBackoffStrategy(1, + new ConstantBackoffStrategy(0.5)))); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + // Make sure it eventually completed successfully + $this->assertEquals('data', $request->getResponse()->getBody(true)); + // Check that two requests were made to retry this request + $this->assertEquals(2, count($this->getServer()->getReceivedRequests(false))); + } + + public function testSeeksToBeginningOfRequestBodyWhenRetrying() + { + // Create a request with a body + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('abc'); + // Set the retry time to be something that will be retried always + $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2); + // Seek to the end of the stream + $request->getBody()->seek(3); + $this->assertEquals('', $request->getBody()->read(1)); + // Create a plugin that does not delay when retrying + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0)); + $plugin->onRequestPoll($this->getMockEvent($request)); + // Ensure that the stream was seeked to 0 + $this->assertEquals('a', $request->getBody()->read(1)); + } + + public function testDoesNotSeekOnRequestsWithNoBodyWhenRetrying() + { + // Create a request with a body + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2); + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0)); + $plugin->onRequestPoll($this->getMockEvent($request)); + } + + protected function getMockEvent(RequestInterface $request) + { + // Create a mock curl multi object + $multi = $this->getMockBuilder('Guzzle\Http\Curl\CurlMulti') + ->setMethods(array('remove', 'add')) + ->getMock(); + + // Create an event that is expected for the Poll event + $event = new Event(array( + 'request' => $request, + 'curl_multi' => $multi + )); + $event->setName(CurlMultiInterface::POLLING_REQUEST); + + return $event; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php new file mode 100644 index 000000000..c0ce10d5c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php @@ -0,0 +1,31 @@ +getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $strategy = new CallbackBackoffStrategy(function () { return 10; }, true); + $this->assertTrue($strategy->makesDecision()); + $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request)); + // Ensure it chains correctly when null is returned + $strategy = new CallbackBackoffStrategy(function () { return null; }, false); + $this->assertFalse($strategy->makesDecision()); + $this->assertFalse($strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php new file mode 100644 index 000000000..703eb4a22 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php @@ -0,0 +1,20 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(3.5, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(3.5, $strategy->getBackoffPeriod(1, $request)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php new file mode 100644 index 000000000..0a5c3e28d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php @@ -0,0 +1,36 @@ +assertNotEmpty(CurlBackoffStrategy::getDefaultFailureCodes()); + $strategy = new CurlBackoffStrategy(); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $e = new CurlException(); + $e->setError('foo', CURLE_BAD_CALLING_ORDER); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, null, $e)); + + foreach (CurlBackoffStrategy::getDefaultFailureCodes() as $code) { + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, null, $e->setError('foo', $code))); + } + } + + public function testIgnoresNonErrors() + { + $strategy = new CurlBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, new Response(200))); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php new file mode 100644 index 000000000..09965bcbf --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php @@ -0,0 +1,23 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(1, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(2, $strategy->getBackoffPeriod(1, $request)); + $this->assertEquals(4, $strategy->getBackoffPeriod(2, $request)); + $this->assertEquals(8, $strategy->getBackoffPeriod(3, $request)); + $this->assertEquals(16, $strategy->getBackoffPeriod(4, $request)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php new file mode 100644 index 000000000..ae68a4eb8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php @@ -0,0 +1,47 @@ +assertNotEmpty(HttpBackoffStrategy::getDefaultFailureCodes()); + $strategy = new HttpBackoffStrategy(); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + + $response = new Response(200); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(400); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + + foreach (HttpBackoffStrategy::getDefaultFailureCodes() as $code) { + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response->setStatus($code))); + } + } + + public function testAllowsCustomCodes() + { + $strategy = new HttpBackoffStrategy(array(204)); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $response = new Response(204); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(500); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + } + + public function testIgnoresNonErrors() + { + $strategy = new HttpBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php new file mode 100644 index 000000000..b4ce8e4af --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php @@ -0,0 +1,21 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(5, $strategy->getBackoffPeriod(1, $request)); + $this->assertEquals(10, $strategy->getBackoffPeriod(2, $request)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php new file mode 100644 index 000000000..dea5a6878 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php @@ -0,0 +1,32 @@ +assertEmpty(ReasonPhraseBackoffStrategy::getDefaultFailureCodes()); + $strategy = new ReasonPhraseBackoffStrategy(array('Foo', 'Internal Server Error')); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $response = new Response(200); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(200, 'Foo'); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); + } + + public function testIgnoresNonErrors() + { + $strategy = new ReasonPhraseBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php new file mode 100644 index 000000000..5590dfb1c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php @@ -0,0 +1,30 @@ +assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertFalse($strategy->getBackoffPeriod(0, $request)); + $this->assertFalse($strategy->getBackoffPeriod(1, $request)); + $this->assertFalse($strategy->getBackoffPeriod(2, $request)); + + $response = new Response(500); + $strategy->setNext(new HttpBackoffStrategy(null, new ConstantBackoffStrategy(10))); + $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request, $response)); + $this->assertEquals(10, $strategy->getBackoffPeriod(1, $request, $response)); + $this->assertFalse($strategy->getBackoffPeriod(2, $request, $response)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php new file mode 100644 index 000000000..69da60a93 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php @@ -0,0 +1,441 @@ +assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + } + + public function testAddsDefaultCollaborators() + { + $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); + $plugin = new CachePlugin(array( + 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass() + )); + $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\CanCacheStrategyInterface', + $this->readAttribute($plugin, 'canCache') + ); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\RevalidationInterface', + $this->readAttribute($plugin, 'revalidation') + ); + } + + public function testAddsCallbackCollaborators() + { + $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); + $plugin = new CachePlugin(array('can_cache' => function () {})); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\CallbackCanCacheStrategy', + $this->readAttribute($plugin, 'canCache') + ); + } + + public function testCanPassCacheAsOnlyArgumentToConstructor() + { + $p = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); + $p = new CachePlugin(new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()))); + } + + public function testUsesCreatedCacheStorage() + { + $plugin = new CachePlugin(array( + 'adapter' => $this->getMockBuilder('Guzzle\Cache\CacheAdapterInterface')->getMockForAbstractClass() + )); + $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + } + + public function testUsesProvidedOptions() + { + $can = $this->getMockBuilder('Guzzle\Plugin\Cache\CanCacheStrategyInterface')->getMockForAbstractClass(); + $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\RevalidationInterface')->getMockForAbstractClass(); + $plugin = new CachePlugin(array( + 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(), + 'can_cache' => $can, + 'revalidation' => $revalidate + )); + $this->assertSame($can, $this->readAttribute($plugin, 'canCache')); + $this->assertSame($revalidate, $this->readAttribute($plugin, 'revalidation')); + } + + public function satisfyProvider() + { + $req1 = new Request('GET', 'http://foo.com', array('Cache-Control' => 'no-cache')); + + return array( + // The response is too old to satisfy the request + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-age=20')), new Response(200, array('Age' => 100)), false, false), + // The response cannot satisfy the request because it is stale + array(new Request('GET', 'http://foo.com'), new Response(200, array('Cache-Control' => 'max-age=10', 'Age' => 100)), false, false), + // Allows the expired response to satisfy the request because of the max-stale + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=15')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), true, false), + // Max stale is > than the allowed staleness + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=5')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), false, false), + // Performs cache revalidation + array($req1, new Response(200), true, true), + // Performs revalidation due to ETag on the response and no cache-control on the request + array(new Request('GET', 'http://foo.com'), new Response(200, array( + 'ETag' => 'ABC', + 'Expires' => date('c', strtotime('+1 year')) + )), true, true), + ); + } + + /** + * @dataProvider satisfyProvider + */ + public function testChecksIfResponseCanSatisfyRequest($request, $response, $can, $revalidates) + { + $didRevalidate = false; + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(); + $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') + ->setMethods(array('revalidate')) + ->setConstructorArgs(array($storage)) + ->getMockForAbstractClass(); + + $revalidate->expects($this->any()) + ->method('revalidate') + ->will($this->returnCallback(function () use (&$didRevalidate) { + $didRevalidate = true; + return true; + })); + + $plugin = new CachePlugin(array( + 'storage' => $storage, + 'revalidation' => $revalidate + )); + + $this->assertEquals($can, $plugin->canResponseSatisfyRequest($request, $response)); + $this->assertEquals($didRevalidate, $revalidates); + } + + public function satisfyFailedProvider() + { + return array( + // Neither has stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100)), false), + // Request has stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), + // Request has valid stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), + // Request has expired stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), false), + // Response has permanent stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error', )), true), + // Response has valid stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), true), + // Response has expired stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), + // Request has valid stale-if-error but response does not + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), + // Response has valid stale-if-error but request does not + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), false), + ); + } + + /** + * @dataProvider satisfyFailedProvider + */ + public function testChecksIfResponseCanSatisfyFailedRequest($request, $response, $can) + { + $plugin = new CachePlugin(); + + $this->assertEquals($can, $plugin->canResponseSatisfyFailedRequest($request, $response)); + } + + public function testDoesNothingWhenRequestIsNotCacheable() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->never())->method('fetch'); + + $plugin = new CachePlugin(array( + 'storage' => $storage, + 'can_cache' => new CallbackCanCacheStrategy(function () { return false; }) + )); + + $plugin->onRequestBeforeSend(new Event(array( + 'request' => new Request('GET', 'http://foo.com') + ))); + } + + public function satisfiableProvider() + { + $date = new \DateTime('-10 seconds'); + + return array( + // Fresh response + array(new Response(200, array(), 'foo')), + // Stale response + array(new Response(200, array('Date' => $date->format('c'), 'Cache-Control' => 'max-age=5'), 'foo')) + ); + } + + /** + * @dataProvider satisfiableProvider + */ + public function testInjectsSatisfiableResponses($response) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + + $storage->expects($this->once())->method('fetch')->will($this->returnValue($response)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + $plugin->onRequestSent(new Event(array('request' => $request, 'response' => $request->getResponse()))); + $this->assertEquals($response->getStatusCode(), $request->getResponse()->getStatusCode()); + $this->assertEquals((string) $response->getBody(), (string) $request->getResponse()->getBody()); + $this->assertTrue($request->getResponse()->hasHeader('Age')); + if ($request->getResponse()->isFresh() === false) { + $this->assertContains('110', (string) $request->getResponse()->getHeader('Warning')); + } + $this->assertSame( + sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), + (string) $request->getHeader('Via') + ); + $this->assertSame( + sprintf('%s GuzzleCache/%s',$request->getProtocolVersion(), Version::VERSION), + (string) $request->getResponse()->getHeader('Via') + ); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertTrue($request->getParams()->get('cache.hit')); + $this->assertTrue($request->getResponse()->hasHeader('X-Cache-Lookup')); + $this->assertTrue($request->getResponse()->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache-Lookup')); + } + + public function satisfiableOnErrorProvider() + { + $date = new \DateTime('-10 seconds'); + return array( + array( + new Response(200, array( + 'Date' => $date->format('c'), + 'Cache-Control' => 'max-age=5, stale-if-error' + ), 'foo'), + ) + ); + } + + /** + * @dataProvider satisfiableOnErrorProvider + */ + public function testInjectsSatisfiableResponsesOnError($cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + $plugin->onRequestError( + $event = new Event(array( + 'request' => $request, + 'response' => $request->getResponse(), + )) + ); + $response = $event['response']; + $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); + $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); + $this->assertTrue($response->hasHeader('Age')); + if ($response->isFresh() === false) { + $this->assertContains('110', (string) $response->getHeader('Warning')); + } + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertSame('error', $request->getParams()->get('cache.hit')); + $this->assertTrue($response->hasHeader('X-Cache-Lookup')); + $this->assertTrue($response->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + /** + * @dataProvider satisfiableOnErrorProvider + */ + public function testInjectsSatisfiableResponsesOnException($cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestException( + new Event(array( + 'request' => $request, + 'response' => $request->getResponse(), + 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), + )) + ); + $plugin->onRequestSent( + new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + )) + ); + $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); + $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); + $this->assertTrue($response->hasHeader('Age')); + if ($response->isFresh() === false) { + $this->assertContains('110', (string) $response->getHeader('Warning')); + } + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertSame('error', $request->getParams()->get('cache.hit')); + $this->assertTrue($response->hasHeader('X-Cache-Lookup')); + $this->assertTrue($response->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + public function unsatisfiableOnErrorProvider() + { + $date = new \DateTime('-10 seconds'); + + return array( + // no-store on request + array( + false, + array('Cache-Control' => 'no-store'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), + ), + // request expired + array( + true, + array('Cache-Control' => 'stale-if-error=4'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), + ), + // response expired + array( + true, + array('Cache-Control' => 'stale-if-error'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error=4'), 'foo'), + ), + ); + } + + /** + * @dataProvider unsatisfiableOnErrorProvider + */ + public function testDoesNotInjectUnsatisfiableResponsesOnError($requestCanCache, $requestHeaders, $cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', $requestHeaders); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestError( + $event = new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + )) + ); + + $this->assertSame($response, $event['response']); + } + + /** + * @dataProvider unsatisfiableOnErrorProvider + */ + public function testDoesNotInjectUnsatisfiableResponsesOnException($requestCanCache, $requestHeaders, $responseParts) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($responseParts)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', $requestHeaders); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestException( + $event = new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), + )) + ); + + $this->assertSame($response, $request->getResponse()); + } + + public function testCachesResponsesWhenCacheable() + { + $cache = new ArrayCache(); + $plugin = new CachePlugin($cache); + + $request = new Request('GET', 'http://foo.com'); + $response = new Response(200, array(), 'Foo'); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestSent(new Event(array( + 'request' => $request, + 'response' => $response + ))); + $data = $this->readAttribute($cache, 'data'); + $this->assertNotEmpty($data); + } + + public function testPurgesRequests() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('purge')) + ->getMockForAbstractClass(); + $storage->expects($this->atLeastOnce())->method('purge'); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('X-Foo' => 'Bar')); + $plugin->purge($request); + } + + public function testAutoPurgesRequests() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('purge')) + ->getMockForAbstractClass(); + $storage->expects($this->atLeastOnce())->method('purge'); + $plugin = new CachePlugin(array('storage' => $storage, 'auto_purge' => true)); + $client = new Client(); + $request = $client->put('http://foo.com', array('X-Foo' => 'Bar')); + $request->addSubscriber($plugin); + $request->setResponse(new Response(200), true); + $request->send(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php new file mode 100644 index 000000000..f3d9bafe2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php @@ -0,0 +1,72 @@ +assertTrue($c->canCacheRequest(new Request('DELETE', 'http://www.foo.com'))); + } + + /** + * The following is a bit of an integration test to ensure that the CachePlugin honors a + * custom can cache strategy. + */ + public function testIntegrationWithCachePlugin() + { + $c = new CallbackCanCacheStrategy( + function ($request) { return true; }, + function ($response) { return true; } + ); + + // Make a request and response that have no business being cached + $request = new Request('DELETE', 'http://www.foo.com'); + $response = Response::fromMessage( + "HTTP/1.1 200 OK\r\n" + . "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" + . "Last-Modified: Wed, 09 Jan 2013 08:48:53 GMT\r\n" + . "Content-Length: 2\r\n" + . "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n\r\n" + . "hi" + ); + + $this->assertTrue($c->canCacheRequest($request)); + $this->assertTrue($c->canCacheResponse($response)); + + $s = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultCacheStorage') + ->setConstructorArgs(array(new DoctrineCacheAdapter(new ArrayCache()))) + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + + $s->expects($this->once()) + ->method('fetch') + ->will($this->returnValue($response)); + + $plugin = new CachePlugin(array('can_cache' => $c, 'storage' => $s)); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('hi', $request->getResponse()->getBody(true)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php new file mode 100644 index 000000000..701a0155f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php @@ -0,0 +1,193 @@ + 'application/json')); + $response = new Response(200, array( + 'Content-Type' => 'application/json', + 'Connection' => 'close', + 'X-Foo' => 'Bar', + 'Vary' => 'Accept' + ), 'test'); + $s->cache($request, $response); + $data = $this->readAttribute($a, 'data'); + + return array( + 'cache' => $a, + 'adapter' => $c, + 'storage' => $s, + 'request' => $request, + 'response' => $response, + 'serialized' => end($data) + ); + } + + public function testReturnsNullForCacheMiss() + { + $cache = $this->getCache(); + $this->assertNull($cache['storage']->fetch(new Request('GET', 'http://test.com'))); + } + + public function testCachesRequests() + { + $cache = $this->getCache(); + $foundRequest = $foundBody = $bodyKey = false; + foreach ($this->readAttribute($cache['cache'], 'data') as $key => $v) { + if (strpos($v, 'foo.com')) { + $foundRequest = true; + $data = unserialize($v); + $bodyKey = $data[0][3]; + $this->assertInternalType('integer', $data[0][4]); + $this->assertFalse(isset($data[0][0]['connection'])); + $this->assertEquals('foo.com', $data[0][0]['host']); + } elseif ($v == 'test') { + $foundBody = $key; + } + } + $this->assertContains($bodyKey, $foundBody); + $this->assertTrue($foundRequest); + } + + public function testFetchesResponse() + { + $cache = $this->getCache(); + $response = $cache['storage']->fetch($cache['request']); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertFalse($response->hasHeader('Connection')); + $this->assertEquals('Bar', (string) $response->getHeader('X-Foo')); + $this->assertEquals('test', (string) $response->getBody()); + $this->assertTrue(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + } + + public function testDeletesRequestItemsAndBody() + { + $cache = $this->getCache(); + $cache['storage']->delete($cache['request']); + $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); + $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + } + + public function testCachesMultipleRequestsWithVary() + { + $cache = $this->getCache(); + $cache['request']->setHeader('Accept', 'application/xml'); + $response = $cache['response']->setHeader('Content-Type', 'application/xml'); + $response->setBody('123'); + $cache['storage']->cache($cache['request'], $response); + $data = $this->readAttribute($cache['cache'], 'data'); + foreach ($data as $v) { + if (strpos($v, 'foo.com')) { + $u = unserialize($v); + $this->assertEquals(2, count($u)); + $this->assertEquals($u[0][0]['accept'], 'application/xml'); + $this->assertEquals($u[0][1]['content-type'], 'application/xml'); + $this->assertEquals($u[1][0]['accept'], 'application/json'); + $this->assertEquals($u[1][1]['content-type'], 'application/json'); + $this->assertNotSame($u[0][3], $u[1][3]); + break; + } + } + } + + public function testPurgeRemovesAllMethodCaches() + { + $cache = $this->getCache(); + foreach (array('HEAD', 'POST', 'PUT', 'DELETE') as $method) { + $request = RequestFactory::getInstance()->cloneRequestWithMethod($cache['request'], $method); + $cache['storage']->cache($request, $cache['response']); + } + $cache['storage']->purge('http://foo.com'); + $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); + $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + $this->assertEquals( + array('DoctrineNamespaceCacheKey[]'), + array_keys($this->readAttribute($cache['cache'], 'data')) + ); + } + + public function testRemovesExpiredResponses() + { + $cache = $this->getCache(); + $request = new Request('GET', 'http://xyz.com'); + $response = new Response(200, array('Age' => 1000, 'Cache-Control' => 'max-age=-10000')); + $cache['storage']->cache($request, $response); + $this->assertNull($cache['storage']->fetch($request)); + $data = $this->readAttribute($cache['cache'], 'data'); + $this->assertFalse(in_array('xyz.com', $data)); + $this->assertTrue(in_array($cache['serialized'], $data)); + } + + public function testUsesVaryToDetermineResult() + { + $cache = $this->getCache(); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $cache['storage']->fetch($cache['request'])); + $request = new Request('GET', 'http://foo.com', array('Accept' => 'application/xml')); + $this->assertNull($cache['storage']->fetch($request)); + } + + public function testEnsuresResponseIsStillPresent() + { + $cache = $this->getCache(); + $data = $this->readAttribute($cache['cache'], 'data'); + $key = array_search('test', $data); + $cache['cache']->delete(substr($key, 1, -4)); + $this->assertNull($cache['storage']->fetch($cache['request'])); + } + + public function staleProvider() + { + return array( + array( + new Request('GET', 'http://foo.com', array('Accept' => 'foo')), + new Response(200, array('Cache-Control' => 'stale-if-error=100', 'Vary' => 'Accept')) + ), + array( + new Request('GET', 'http://foo.com', array('Accept' => 'foo')), + new Response(200, array('Cache-Control' => 'stale-if-error', 'Vary' => 'Accept')) + ) + ); + } + + /** + * @dataProvider staleProvider + */ + public function testUsesStaleTimeDirectiveForTtd($request, $response) + { + $cache = $this->getCache(); + $cache['storage']->cache($request, $response); + $data = $this->readAttribute($cache['cache'], 'data'); + foreach ($data as $v) { + if (strpos($v, 'foo.com')) { + $u = unserialize($v); + $this->assertGreaterThan($u[1][4], $u[0][4]); + break; + } + } + } + + public function testCanFilterCacheKeys() + { + $cache = $this->getCache(); + $cache['request']->getQuery()->set('auth', 'foo'); + $this->assertNull($cache['storage']->fetch($cache['request'])); + $cache['request']->getParams()->set('cache.key_filter', 'auth'); + $this->assertNotNull($cache['storage']->fetch($cache['request'])); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php new file mode 100644 index 000000000..de4d182a2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php @@ -0,0 +1,40 @@ +assertTrue($strategy->canCacheRequest($request)); + } + + public function testDoesNotCacheNoStore() + { + $strategy = new DefaultCanCacheStrategy(); + $request = new Request('GET', 'http://foo.com', array('cache-control' => 'no-store')); + $this->assertFalse($strategy->canCacheRequest($request)); + } + + public function testCanCacheResponse() + { + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setMethods(array('canCache')) + ->setConstructorArgs(array(200)) + ->getMock(); + $response->expects($this->once()) + ->method('canCache') + ->will($this->returnValue(true)); + $strategy = new DefaultCanCacheStrategy(); + $this->assertTrue($strategy->canCacheResponse($response)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php new file mode 100644 index 000000000..0699cb266 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php @@ -0,0 +1,248 @@ +getHttpDate('-100 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nCache-Control: max-age=2000000\r\nContent-Length: 0\r\n\r\n", + ), + // Forces revalidation that overwrites what is in cache + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: must-revalidate, no-cache\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nDatas", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nDate: " . $this->getHttpDate('now') . "\r\n\r\nDatas" + ), + // Throws an exception during revalidation + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . $this->getHttpDate('-3 hours') . "\r\n\r\nData", + "HTTP/1.1 500 INTERNAL SERVER ERROR\r\nContent-Length: 0\r\n\r\n" + ), + // ETag mismatch + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nETag: \"123\"\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nETag: \"123456\"\r\n\r\n", + ), + ); + } + + /** + * @dataProvider cacheRevalidationDataProvider + */ + public function testRevalidatesResponsesAgainstOriginServer($can, $request, $response, $validate = null, $result = null) + { + // Send some responses to the test server for cache validation + $server = $this->getServer(); + $server->flush(); + + if ($validate) { + $server->enqueue($validate); + } + + $request = RequestFactory::getInstance()->fromMessage("GET / HTTP/1.1\r\nHost: 127.0.0.1:" . $server->getPort() . "\r\n" . $request); + $response = Response::fromMessage($response); + $request->setClient(new Client()); + + $plugin = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); + $this->assertEquals( + $can, + $plugin->canResponseSatisfyRequest($request, $response), + '-> ' . $request . "\n" . $response + ); + + if ($result) { + $result = Response::fromMessage($result); + $result->removeHeader('Date'); + $request->getResponse()->removeHeader('Date'); + $request->getResponse()->removeHeader('Connection'); + // Get rid of dates + $this->assertEquals((string) $result, (string) $request->getResponse()); + } + + if ($validate) { + $this->assertEquals(1, count($server->getReceivedRequests())); + } + } + + public function testHandles404RevalidationResponses() + { + $request = new Request('GET', 'http://foo.com'); + $request->setClient(new Client()); + $badResponse = new Response(404, array(), 'Oh no!'); + $badRequest = clone $request; + $badRequest->setResponse($badResponse, true); + $response = new Response(200, array(), 'foo'); + + // Seed the cache + $s = new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); + $s->cache($request, $response); + $this->assertNotNull($s->fetch($request)); + + $rev = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') + ->setConstructorArgs(array($s)) + ->setMethods(array('createRevalidationRequest')) + ->getMock(); + + $rev->expects($this->once()) + ->method('createRevalidationRequest') + ->will($this->returnValue($badRequest)); + + try { + $rev->revalidate($request, $response); + $this->fail('Should have thrown an exception'); + } catch (BadResponseException $e) { + $this->assertSame($badResponse, $e->getResponse()); + $this->assertNull($s->fetch($request)); + } + } + + public function testCanRevalidateWithPlugin() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . + "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Content-Length: 2\r\n\r\nhi", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber(new CachePlugin()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(3, count($this->getServer()->getReceivedRequests())); + } + + public function testCanHandleRevalidationFailures() + { + $client = new Client($this->getServer()->getUrl()); + $lm = gmdate('c', time() - 60); + $mock = new MockPlugin(array( + new Response(200, array( + 'Date' => $lm, + 'Cache-Control' => 'max-age=100, must-revalidate, stale-if-error=9999', + 'Last-Modified' => $lm, + 'Content-Length' => 2 + ), 'hi'), + new CurlException('Bleh'), + new CurlException('Bleh') + )); + $client->addSubscriber(new CachePlugin()); + $client->addSubscriber($mock); + $client->get()->send(); + $response = $client->get()->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('hi', $response->getBody(true)); + $this->assertEquals(3, count($mock->getReceivedRequests())); + $this->assertEquals(0, count($mock->getQueue())); + } + + public function testCanHandleStaleIfErrorWhenRevalidating() + { + $lm = gmdate('c', time() - 60); + $mock = new MockPlugin(array( + new Response(200, array( + 'Date' => $lm, + 'Cache-Control' => 'must-revalidate, max-age=0, stale-if-error=1200', + 'Last-Modified' => $lm, + 'Content-Length' => 2 + ), 'hi'), + new CurlException('Oh no!'), + new CurlException('Oh no!') + )); + $cache = new CachePlugin(); + $client = new Client('http://www.example.com'); + $client->addSubscriber($cache); + $client->addSubscriber($mock); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $response = $client->get()->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertCount(0, $mock); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + /** + * @group issue-437 + */ + public function testDoesNotTouchClosureListeners() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . + "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Content-Length: 2\r\n\r\nhi", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber(new CachePlugin()); + $client->getEventDispatcher()->addListener('command.after_send', function(){}); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + } + +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php new file mode 100644 index 000000000..9af80f255 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php @@ -0,0 +1,19 @@ +assertFalse($deny->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php new file mode 100644 index 000000000..4bcc04bfa --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php @@ -0,0 +1,19 @@ +assertTrue($skip->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php new file mode 100644 index 000000000..5d0f668a6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php @@ -0,0 +1,385 @@ +jar = new ArrayCookieJar(); + } + + protected function getTestCookies() + { + return array( + new Cookie(array('name' => 'foo', 'value' => 'bar', 'domain' => 'foo.com', 'path' => '/', 'discard' => true)), + new Cookie(array('name' => 'test', 'value' => '123', 'domain' => 'baz.com', 'path' => '/foo', 'expires' => 2)), + new Cookie(array('name' => 'you', 'value' => '123', 'domain' => 'bar.com', 'path' => '/boo', 'expires' => time() + 1000)) + ); + } + + /** + * Provides test data for cookie cookieJar retrieval + */ + public function getCookiesDataProvider() + { + return array( + array(array('foo', 'baz', 'test', 'muppet', 'googoo'), '', '', '', false), + array(array('foo', 'baz', 'muppet', 'googoo'), '', '', '', true), + array(array('googoo'), 'www.example.com', '', '', false), + array(array('muppet', 'googoo'), 'test.y.example.com', '', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/test/', '', false), + array(array('googoo'), 'x.y.example.com', '/test/acme/test/', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('baz'), 'example.com', '', 'baz', false), + ); + } + + public function testStoresAndRetrievesCookies() + { + $cookies = $this->getTestCookies(); + foreach ($cookies as $cookie) { + $this->assertTrue($this->jar->add($cookie)); + } + + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(3, count($this->jar->getIterator())); + $this->assertEquals($cookies, $this->jar->all(null, null, null, false, false)); + } + + public function testRemovesExpiredCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeExpired(); + $this->assertEquals(array($cookies[0], $cookies[2]), $this->jar->all()); + } + + public function testRemovesTemporaryCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeTemporary(); + $this->assertEquals(array($cookies[2]), $this->jar->all()); + } + + public function testIsSerializable() + { + $this->assertEquals('[]', $this->jar->serialize()); + $this->jar->unserialize('[]'); + $this->assertEquals(array(), $this->jar->all()); + + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove discard and expired cookies + $serialized = $this->jar->serialize(); + $data = json_decode($serialized, true); + $this->assertEquals(1, count($data)); + + $a = new ArrayCookieJar(); + $a->unserialize($serialized); + $this->assertEquals(1, count($a)); + } + + public function testRemovesSelectively() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove foo.com cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + // Try again, removing no further cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + + // Remove bar.com cookies with path of /boo + $this->jar->remove('bar.com', '/boo'); + $this->assertEquals(1, count($this->jar)); + + // Remove cookie by name + $this->jar->remove(null, null, 'test'); + $this->assertEquals(0, count($this->jar)); + } + + public function testDoesNotAddIncompleteCookies() + { + $this->assertEquals(false, $this->jar->add(new Cookie())); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => 'foo' + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => false + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => true + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com' + )))); + } + + public function testDoesAddValidCookies() + { + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => 0 + )))); + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => 0.0 + )))); + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => '0' + )))); + } + + public function testOverwritesCookiesThatAreOlderOrDiscardable() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + unset($data['discard']); + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals(false, $c[0]->getDiscard()); + + // Make sure it doesn't duplicate the cookie + $this->jar->add(new Cookie($data)); + $this->assertEquals(1, count($this->jar)); + + // Make sure the more future-ful expiration date supersede the other + $data['expires'] = time() + 2000; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + $c = $this->jar->all(); + $this->assertNotEquals($t, $c[0]->getExpires()); + } + + public function testOverwritesCookiesThatHaveChanged() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + $data['value'] = 'boo'; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + // Changing the value plus a parameter also must overwrite the existing one + $data['value'] = 'zoo'; + $data['secure'] = false; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals('zoo', $c[0]->getValue()); + } + + public function testAddsCookiesFromResponseWithNoRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => array( + "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "FPCK3=AgBNbvoQAGpGEABZLRAAbFsQAF1tEABkDhAAeO0=; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "CH=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1", + "CH=AgBNbvoQAAEcEAApuhAAMJcQADQvEAAvGxAALe0QAD6uEAATwhAAC1AQAC8t; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1" + ) + )); + + $this->jar->addCookiesFromResponse($response); + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(1, count($this->jar->all(null, null, 'fpc'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'FPCK3'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'CH'))); + } + + public function testAddsCookiesFromResponseWithRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" + )); + $request = new Request('GET', 'http://www.example.com'); + $this->jar->addCookiesFromResponse($response, $request); + $this->assertEquals(1, count($this->jar)); + } + + public function getMatchingCookiesDataProvider() + { + return array( + array('https://example.com', array(0)), + array('http://example.com', array()), + array('https://example.com:8912', array()), + array('https://foo.example.com', array(0)), + array('http://foo.example.com/test/acme/', array(4)) + ); + } + + /** + * @dataProvider getMatchingCookiesDataProvider + */ + public function testReturnsCookiesMatchingRequests($url, $cookies) + { + $bag = array( + new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(443, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'baz', + 'value' => 'foobar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'test', + 'value' => '123', + 'domain' => 'www.foobar.com', + 'path' => '/path/', + 'discard' => true + )), + new Cookie(array( + 'name' => 'muppet', + 'value' => 'cookie_monster', + 'domain' => '.y.example.com', + 'path' => '/acme/', + 'comment' => 'Comment goes here...', + 'expires' => time() + 86400 + )), + new Cookie(array( + 'name' => 'googoo', + 'value' => 'gaga', + 'domain' => '.example.com', + 'path' => '/test/acme/', + 'max_age' => 1500, + 'version' => 2 + )) + ); + + foreach ($bag as $cookie) { + $this->jar->add($cookie); + } + + $request = new Request('GET', $url); + $results = $this->jar->getMatchingCookies($request); + $this->assertEquals(count($cookies), count($results)); + foreach ($cookies as $i) { + $this->assertContains($bag[$i], $results); + } + } + + /** + * @expectedException \Guzzle\Plugin\Cookie\Exception\InvalidCookieException + * @expectedExceptionMessage The cookie name must not contain invalid characters: abc:@123 + */ + public function testThrowsExceptionWithStrictMode() + { + $a = new ArrayCookieJar(); + $a->setStrictMode(true); + $a->add(new Cookie(array( + 'name' => 'abc:@123', + 'value' => 'foo', + 'domain' => 'bar' + ))); + } + + public function testRemoveExistingCookieIfEmpty() + { + // Add a cookie that should not be affected + $a = new Cookie(array( + 'name' => 'foo', + 'value' => 'nope', + 'domain' => 'foo.com', + 'path' => '/abc' + )); + $this->jar->add($a); + + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'foo.com', + 'path' => '/' + ); + + $b = new Cookie($data); + $this->assertTrue($this->jar->add($b)); + $this->assertEquals(2, count($this->jar)); + + // Try to re-set the same cookie with no value: assert that cookie is not added + $data['value'] = null; + $this->assertFalse($this->jar->add(new Cookie($data))); + // assert that original cookie has been deleted + $cookies = $this->jar->all('foo.com'); + $this->assertTrue(in_array($a, $cookies, true)); + $this->assertFalse(in_array($b, $cookies, true)); + $this->assertEquals(1, count($this->jar)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php new file mode 100644 index 000000000..ac9471fd8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php @@ -0,0 +1,63 @@ +file = tempnam('/tmp', 'file-cookies'); + } + + public function testLoadsFromFileFile() + { + $jar = new FileCookieJar($this->file); + $this->assertEquals(array(), $jar->all()); + unlink($this->file); + } + + public function testPersistsToFileFile() + { + $jar = new FileCookieJar($this->file); + $jar->add(new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'baz', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'boo', + 'value' => 'bar', + 'domain' => 'foo.com', + ))); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the file + $contents = file_get_contents($this->file); + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new FileCookieJar($this->file); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unlink($this->file); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php new file mode 100644 index 000000000..f8c175cce --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php @@ -0,0 +1,134 @@ +getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar') + ->setMethods(array('addCookiesFromResponse')) + ->getMock(); + + $mock->expects($this->exactly(1)) + ->method('addCookiesFromResponse') + ->with($response); + + $plugin = new CookiePlugin($mock); + $plugin->onRequestSent(new Event(array( + 'response' => $response + ))); + } + + public function testAddsCookiesToRequests() + { + $cookie = new Cookie(array( + 'name' => 'foo', + 'value' => 'bar' + )); + + $mock = $this->getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar') + ->setMethods(array('getMatchingCookies')) + ->getMock(); + + $mock->expects($this->once()) + ->method('getMatchingCookies') + ->will($this->returnValue(array($cookie))); + + $plugin = new CookiePlugin($mock); + + $client = new Client(); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get('http://www.example.com'); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + + $this->assertEquals('bar', $request->getCookie('foo')); + } + + public function testCookiesAreExtractedFromRedirectResponses() + { + $plugin = new CookiePlugin(new ArrayCookieJar()); + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 302 Moved Temporarily\r\n" . + "Set-Cookie: test=583551; expires=Wednesday, 23-Mar-2050 19:49:45 GMT; path=/\r\n" . + "Location: /redirect\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $client->get()->send(); + $request = $client->get(); + $request->send(); + $this->assertEquals('test=583551', $request->getHeader('Cookie')); + + $requests = $this->getServer()->getReceivedRequests(true); + // Confirm subsequent requests have the cookie. + $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); + // Confirm the redirected request has the cookie. + $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie')); + } + + public function testCookiesAreNotAddedWhenParamIsSet() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + + $jar->add(new Cookie(array( + 'domain' => 'example.com', + 'path' => '/', + 'name' => 'test', + 'value' => 'hi', + 'expires' => time() + 3600 + ))); + + $client = new Client('http://example.com'); + $client->getEventDispatcher()->addSubscriber($plugin); + + // Ensure that it is normally added + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertEquals('hi', $request->getCookie('test')); + + // Now ensure that it is not added + $request = $client->get(); + $request->getParams()->set('cookies.disable', true); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertNull($request->getCookie('test')); + } + + public function testProvidesCookieJar() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + $this->assertSame($jar, $plugin->getCookieJar()); + } + + public function testEscapesCookieDomains() + { + $cookie = new Cookie(array('domain' => '/foo/^$[A-Z]+/')); + $this->assertFalse($cookie->matchesDomain('foo')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php new file mode 100644 index 000000000..9fb0b4310 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php @@ -0,0 +1,223 @@ +assertEquals('/', $cookie->getPath()); + $this->assertEquals(array(), $cookie->getPorts()); + } + + public function testConvertsDateTimeMaxAgeToUnixTimestamp() + { + $cookie = new Cookie(array( + 'expires' => 'November 20, 1984' + )); + $this->assertTrue(is_numeric($cookie->getExpires())); + } + + public function testAddsExpiresBasedOnMaxAge() + { + $t = time(); + $cookie = new Cookie(array( + 'max_age' => 100 + )); + $this->assertEquals($t + 100, $cookie->getExpires()); + } + + public function testHoldsValues() + { + $t = time(); + $data = array( + 'name' => 'foo', + 'value' => 'baz', + 'path' => '/bar', + 'domain' => 'baz.com', + 'expires' => $t, + 'max_age' => 100, + 'comment' => 'Hi', + 'comment_url' => 'foo.com', + 'port' => array(1, 2), + 'version' => 2, + 'secure' => true, + 'discard' => true, + 'http_only' => true, + 'data' => array( + 'foo' => 'baz', + 'bar' => 'bam' + ) + ); + + $cookie = new Cookie($data); + $this->assertEquals($data, $cookie->toArray()); + + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('baz', $cookie->getValue()); + $this->assertEquals('baz.com', $cookie->getDomain()); + $this->assertEquals('/bar', $cookie->getPath()); + $this->assertEquals($t, $cookie->getExpires()); + $this->assertEquals(100, $cookie->getMaxAge()); + $this->assertEquals('Hi', $cookie->getComment()); + $this->assertEquals('foo.com', $cookie->getCommentUrl()); + $this->assertEquals(array(1, 2), $cookie->getPorts()); + $this->assertEquals(2, $cookie->getVersion()); + $this->assertTrue($cookie->getSecure()); + $this->assertTrue($cookie->getDiscard()); + $this->assertTrue($cookie->getHttpOnly()); + $this->assertEquals('baz', $cookie->getAttribute('foo')); + $this->assertEquals('bam', $cookie->getAttribute('bar')); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'bam' + ), $cookie->getAttributes()); + + $cookie->setName('a') + ->setValue('b') + ->setPath('c') + ->setDomain('bar.com') + ->setExpires(10) + ->setMaxAge(200) + ->setComment('e') + ->setCommentUrl('f') + ->setPorts(array(80)) + ->setVersion(3) + ->setSecure(false) + ->setHttpOnly(false) + ->setDiscard(false) + ->setAttribute('snoop', 'dog'); + + $this->assertEquals('a', $cookie->getName()); + $this->assertEquals('b', $cookie->getValue()); + $this->assertEquals('c', $cookie->getPath()); + $this->assertEquals('bar.com', $cookie->getDomain()); + $this->assertEquals(10, $cookie->getExpires()); + $this->assertEquals(200, $cookie->getMaxAge()); + $this->assertEquals('e', $cookie->getComment()); + $this->assertEquals('f', $cookie->getCommentUrl()); + $this->assertEquals(array(80), $cookie->getPorts()); + $this->assertEquals(3, $cookie->getVersion()); + $this->assertFalse($cookie->getSecure()); + $this->assertFalse($cookie->getDiscard()); + $this->assertFalse($cookie->getHttpOnly()); + $this->assertEquals('dog', $cookie->getAttribute('snoop')); + } + + public function testDeterminesIfExpired() + { + $c = new Cookie(); + $c->setExpires(10); + $this->assertTrue($c->isExpired()); + $c->setExpires(time() + 10000); + $this->assertFalse($c->isExpired()); + } + + public function testMatchesPorts() + { + $cookie = new Cookie(); + // Always matches when nothing is set + $this->assertTrue($cookie->matchesPort(2)); + + $cookie->setPorts(array(1, 2)); + $this->assertTrue($cookie->matchesPort(2)); + $this->assertFalse($cookie->matchesPort(100)); + } + + public function testMatchesDomain() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('baz.com'); + $this->assertTrue($cookie->matchesDomain('baz.com')); + $this->assertFalse($cookie->matchesDomain('bar.com')); + + $cookie->setDomain('.baz.com'); + $this->assertTrue($cookie->matchesDomain('.baz.com')); + $this->assertTrue($cookie->matchesDomain('foo.baz.com')); + $this->assertFalse($cookie->matchesDomain('baz.bar.com')); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('.com.'); + $this->assertFalse($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.local'); + $this->assertTrue($cookie->matchesDomain('example.local')); + } + + public function testMatchesPath() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesPath('/foo')); + + $cookie->setPath('/foo'); + + // o The cookie-path and the request-path are identical. + $this->assertTrue($cookie->matchesPath('/foo')); + $this->assertFalse($cookie->matchesPath('/bar')); + + // o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- + // path is a %x2F ("/") character. + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/fooBar')); + + // o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + $cookie->setPath('/foo/'); + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/fooBaz')); + $this->assertFalse($cookie->matchesPath('/foo')); + + } + + public function cookieValidateProvider() + { + return array( + array('foo', 'baz', 'bar', true), + array('0', '0', '0', true), + array('', 'baz', 'bar', 'The cookie name must not be empty'), + array('foo', '', 'bar', 'The cookie value must not be empty'), + array('foo', 'baz', '', 'The cookie domain must not be empty'), + array('foo\\', 'baz', '0', 'The cookie name must not contain invalid characters: foo\\'), + ); + } + + /** + * @dataProvider cookieValidateProvider + */ + public function testValidatesCookies($name, $value, $domain, $result) + { + $cookie = new Cookie(array( + 'name' => $name, + 'value' => $value, + 'domain' => $domain + )); + $this->assertSame($result, $cookie->validate()); + } + + public function testCreatesInvalidCharacterString() + { + $m = new \ReflectionMethod('Guzzle\Plugin\Cookie\Cookie', 'getInvalidCharacters'); + $m->setAccessible(true); + $p = new \ReflectionProperty('Guzzle\Plugin\Cookie\Cookie', 'invalidCharString'); + $p->setAccessible(true); + $p->setValue(''); + // Expects a string containing 51 invalid characters + $this->assertEquals(51, strlen($m->invoke($m))); + $this->assertContains('@', $m->invoke($m)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php new file mode 100644 index 000000000..2a4b49eb6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php @@ -0,0 +1,39 @@ +getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + Version::$emitWarnings = true; + } + + public function testAddsDigestAuthentication() + { + Version::$emitWarnings = false; + $plugin = new CurlAuthPlugin('julian', 'test', CURLAUTH_DIGEST); + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('julian', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + $this->assertEquals('julian:test', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + Version::$emitWarnings = true; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php new file mode 100644 index 000000000..6f94186b5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php @@ -0,0 +1,137 @@ +flush(); + } + + public function setUp() + { + $mockError = 'Guzzle\Tests\Mock\ErrorResponseMock'; + $description = ServiceDescription::factory(array( + 'operations' => array( + 'works' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => $mockError), + array('code' => 503, 'reason' => 'foo', 'class' => $mockError), + array('code' => 200, 'reason' => 'Error!', 'class' => $mockError) + ) + ), + 'bad_class' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => 'Does\\Not\\Exist') + ) + ), + 'does_not_implement' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => __CLASS__) + ) + ), + 'no_errors' => array('httpMethod' => 'GET'), + 'no_class' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500) + ) + ), + ) + )); + $this->client = new Client($this->getServer()->getUrl()); + $this->client->setDescription($description); + } + + /** + * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException + */ + public function testSkipsWhenErrorResponsesIsNotSet() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_errors')->execute(); + } + + public function testSkipsWhenErrorResponsesIsNotSetAndAllowsSuccess() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_errors')->execute(); + } + + /** + * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException + * @expectedExceptionMessage Does\Not\Exist does not exist + */ + public function testEnsuresErrorResponseExists() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('bad_class')->execute(); + } + + /** + * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException + * @expectedExceptionMessage must implement Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface + */ + public function testEnsuresErrorResponseImplementsInterface() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('does_not_implement')->execute(); + } + + public function testThrowsSpecificErrorResponseOnMatch() + { + try { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $command = $this->client->getCommand('works'); + $command->execute(); + $this->fail('Exception not thrown'); + } catch (ErrorResponseMock $e) { + $this->assertSame($command, $e->command); + $this->assertEquals(500, $e->response->getStatusCode()); + } + } + + /** + * @expectedException \Guzzle\Tests\Mock\ErrorResponseMock + */ + public function testThrowsWhenCodeAndPhraseMatch() + { + $this->getServer()->enqueue("HTTP/1.1 200 Error!\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('works')->execute(); + } + + public function testSkipsWhenReasonDoesNotMatch() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('works')->execute(); + } + + public function testSkipsWhenNoClassIsSet() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_class')->execute(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php new file mode 100644 index 000000000..41aa673fd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php @@ -0,0 +1,140 @@ +get(); + $requests[$i]->setResponse(new Response(200), true); + $requests[$i]->send(); + $h->add($requests[$i]); + } + + return $requests; + } + + public function testDescribesSubscribedEvents() + { + $this->assertInternalType('array', HistoryPlugin::getSubscribedEvents()); + } + + public function testMaintainsLimitValue() + { + $h = new HistoryPlugin(); + $this->assertSame($h, $h->setLimit(10)); + $this->assertEquals(10, $h->getLimit()); + } + + public function testAddsRequests() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 1); + $this->assertEquals(1, count($h)); + $i = $h->getIterator(); + $this->assertEquals(1, count($i)); + $this->assertEquals($requests[0], $i[0]); + } + + /** + * @depends testAddsRequests + */ + public function testMaintainsLimit() + { + $h = new HistoryPlugin(); + $h->setLimit(2); + $requests = $this->addRequests($h, 3); + $this->assertEquals(2, count($h)); + $i = 0; + foreach ($h as $request) { + if ($i > 0) { + $this->assertSame($requests[$i], $request); + } + } + } + + public function testReturnsLastRequest() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests), $h->getLastRequest()); + } + + public function testReturnsLastResponse() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests)->getResponse(), $h->getLastResponse()); + } + + public function testClearsHistory() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertEquals(5, count($h)); + $h->clear(); + $this->assertEquals(0, count($h)); + } + + /** + * @depends testAddsRequests + */ + public function testUpdatesAddRequests() + { + $h = new HistoryPlugin(); + $client = new Client('http://127.0.0.1/'); + $client->getEventDispatcher()->addSubscriber($h); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + + $this->assertSame($request, $h->getLastRequest()); + } + + public function testCanCastToString() + { + $client = new Client('http://127.0.0.1/'); + $h = new HistoryPlugin(); + $client->getEventDispatcher()->addSubscriber($h); + + $mock = new MockPlugin(array( + new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), + new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), + new Response(200, array('Content-Length' => '2'), 'HI') + )); + + $client->getEventDispatcher()->addSubscriber($mock); + $request = $client->get(); + $request->send(); + $this->assertEquals(3, count($h)); + $this->assertEquals(3, count($mock->getReceivedRequests())); + + $h = str_replace("\r", '', $h); + $this->assertContains("> GET / HTTP/1.1\nHost: 127.0.0.1\nUser-Agent:", $h); + $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); + $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); + $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php new file mode 100644 index 000000000..ad663a53c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php @@ -0,0 +1,95 @@ +adapter = new ClosureLogAdapter(function ($message) { + echo $message; + }); + } + + public function testIgnoresCurlEventsWhenNotWiringBodies() + { + $p = new LogPlugin($this->adapter); + $this->assertNotEmpty($p->getSubscribedEvents()); + $event = new Event(array('request' => new Request('GET', 'http://foo.com'))); + $p->onCurlRead($event); + $p->onCurlWrite($event); + $p->onRequestBeforeSend($event); + } + + public function testLogsWhenComplete() + { + $output = ''; + $p = new LogPlugin(new ClosureLogAdapter(function ($message) use (&$output) { + $output = $message; + }), '{method} {resource} | {code} {res_body}'); + + $p->onRequestSent(new Event(array( + 'request' => new Request('GET', 'http://foo.com'), + 'response' => new Response(200, array(), 'Foo') + ))); + + $this->assertEquals('GET / | 200 Foo', $output); + } + + public function testWiresBodiesWhenNeeded() + { + $client = new Client($this->getServer()->getUrl()); + $plugin = new LogPlugin($this->adapter, '{req_body} | {res_body}', true); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->put(); + + // Send the response from the dummy server as the request body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nsend"); + $stream = fopen($this->getServer()->getUrl(), 'r'); + $request->setBody(EntityBody::factory($stream, 4)); + + $tmpFile = tempnam(sys_get_temp_dir(), 'non_repeatable'); + $request->setResponseBody(EntityBody::factory(fopen($tmpFile, 'w'))); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse"); + + ob_start(); + $request->send(); + $message = ob_get_clean(); + + unlink($tmpFile); + $this->assertContains("send", $message); + $this->assertContains("response", $message); + } + + public function testHasHelpfulStaticFactoryMethod() + { + $s = fopen('php://temp', 'r+'); + $client = new Client(); + $client->addSubscriber(LogPlugin::getDebugPlugin(true, $s)); + $request = $client->put('http://foo.com', array('Content-Type' => 'Foo'), 'Bar'); + $request->setresponse(new Response(200), true); + $request->send(); + rewind($s); + $contents = stream_get_contents($s); + $this->assertContains('# Request:', $contents); + $this->assertContainsIns('PUT / HTTP/1.1', $contents); + $this->assertContains('# Response:', $contents); + $this->assertContainsIns('HTTP/1.1 200 OK', $contents); + $this->assertContains('# Errors:', $contents); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php new file mode 100644 index 000000000..4bd411113 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php @@ -0,0 +1,97 @@ + array( + 'test' => array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'ContentMD5' => array(), + 'Body' => array( + 'location' => 'body' + ) + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + + return $client; + } + + public function testHasEvents() + { + $this->assertNotEmpty(CommandContentMd5Plugin::getSubscribedEvents()); + } + + public function testValidatesMd5WhenParamExists() + { + $client = $this->getClient(); + $command = $client->getCommand('test', array( + 'Body' => 'Foo', + 'ContentMD5' => true + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $this->assertEquals('E1bGfXrRY42Ba/uCLdLCXQ==', (string) $request->getHeader('Content-MD5')); + } + + public function testDoesNothingWhenNoPayloadExists() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test'); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $this->assertNull($request->getHeader('Content-MD5')); + } + + public function testAddsValidationToResponsesOfContentMd5() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test', array( + 'ValidateMD5' => true + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $listeners = $request->getEventDispatcher()->getListeners('request.complete'); + $this->assertNotEmpty($listeners); + } + + public function testIgnoresValidationWhenDisabled() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test', array( + 'ValidateMD5' => false + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $listeners = $request->getEventDispatcher()->getListeners('request.complete'); + $this->assertEmpty($listeners); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php new file mode 100644 index 000000000..482e92b28 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php @@ -0,0 +1,120 @@ +create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $body = 'abc'; + $hash = md5($body); + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Length' => 3 + ), 'abc'); + + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with no Content-MD5 + $response->removeHeader('Content-MD5'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testThrowsExceptionOnInvalidMd5() + { + $plugin = new Md5ValidatorPlugin(); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + public function testSkipsWhenContentLengthIsTooLarge() + { + $plugin = new Md5ValidatorPlugin(false, 1); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + public function testProperlyValidatesWhenUsingContentEncoding() + { + $plugin = new Md5ValidatorPlugin(true); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + // Content-MD5 is the MD5 hash of the canonical content after all + // content-encoding has been applied. Because cURL will automatically + // decompress entity bodies, we need to re-compress it to calculate. + $body = EntityBody::factory('abc'); + $body->compress(); + $hash = $body->getContentMd5(); + $body->uncompress(); + + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'gzip' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + $this->assertEquals('abc', $response->getBody(true)); + + // Try again with an unknown encoding + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'foobar' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with compress + $body->compress('bzip2.compress'); + $response = new Response(200, array( + 'Content-MD5' => $body->getContentMd5(), + 'Content-Encoding' => 'compress' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with encoding and disabled content-encoding checks + $request->getEventDispatcher()->removeSubscriber($plugin); + $plugin = new Md5ValidatorPlugin(false); + $request->getEventDispatcher()->addSubscriber($plugin); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php new file mode 100644 index 000000000..3af8fefcc --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php @@ -0,0 +1,199 @@ +assertInternalType('array', MockPlugin::getSubscribedEvents()); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', MockPlugin::getAllEvents()); + } + + public function testCanBeTemporary() + { + $plugin = new MockPlugin(); + $this->assertFalse($plugin->isTemporary()); + $plugin = new MockPlugin(null, true); + $this->assertTrue($plugin->isTemporary()); + } + + public function testIsCountable() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $this->assertEquals(1, count($plugin)); + } + + /** + * @depends testIsCountable + */ + public function testCanClearQueue() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $plugin->clearQueue(); + $this->assertEquals(0, count($plugin)); + } + + public function testCanInspectQueue() + { + $plugin = new MockPlugin(); + $this->assertInternalType('array', $plugin->getQueue()); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $queue = $plugin->getQueue(); + $this->assertInternalType('array', $queue); + $this->assertEquals(1, count($queue)); + } + + public function testRetrievesResponsesFromFiles() + { + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response); + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenResponseFileIsNotFound() + { + MockPlugin::getMockFile('missing/filename'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidResponsesThrowAnException() + { + $p = new MockPlugin(); + $p->addResponse($this); + } + + public function testAddsResponseObjectsToQueue() + { + $p = new MockPlugin(); + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $p->addResponse($response); + $this->assertEquals(array($response), $p->getQueue()); + } + + public function testAddsResponseFilesToQueue() + { + $p = new MockPlugin(); + $p->addResponse(__DIR__ . '/../../TestData/mock_response'); + $this->assertEquals(1, count($p)); + } + + /** + * @depends testAddsResponseFilesToQueue + */ + public function testAddsMockResponseToRequestFromClient() + { + $p = new MockPlugin(); + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $p->addResponse($response); + + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertSame($response, $request->getResponse()); + $this->assertEquals(0, count($p)); + } + + /** + * @depends testAddsResponseFilesToQueue + * @expectedException \OutOfBoundsException + */ + public function testUpdateThrowsExceptionWhenEmpty() + { + $p = new MockPlugin(); + $p->onRequestBeforeSend(new Event()); + } + + /** + * @depends testAddsMockResponseToRequestFromClient + */ + public function testDetachesTemporaryWhenEmpty() + { + $p = new MockPlugin(null, true); + $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response')); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertFalse($this->hasSubscriber($client, $p)); + } + + public function testLoadsResponsesFromConstructor() + { + $p = new MockPlugin(array(new Response(200))); + $this->assertEquals(1, $p->count()); + } + + public function testStoresMockedRequests() + { + $p = new MockPlugin(array(new Response(200), new Response(200))); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + + $request1 = $client->get(); + $request1->send(); + $this->assertEquals(array($request1), $p->getReceivedRequests()); + + $request2 = $client->get(); + $request2->send(); + $this->assertEquals(array($request1, $request2), $p->getReceivedRequests()); + + $p->flush(); + $this->assertEquals(array(), $p->getReceivedRequests()); + } + + public function testReadsBodiesFromMockedRequests() + { + $p = new MockPlugin(array(new Response(200))); + $p->readBodies(true); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + + $body = EntityBody::factory('foo'); + $request = $client->put(); + $request->setBody($body); + $request->send(); + $this->assertEquals(3, $body->ftell()); + } + + public function testCanMockBadRequestExceptions() + { + $client = new Client('http://127.0.0.1:123/'); + $ex = new CurlException('Foo'); + $mock = new MockPlugin(array($ex)); + $client->addSubscriber($mock); + $request = $client->get('foo'); + + try { + $request->send(); + $this->fail('Did not dequeue an exception'); + } catch (CurlException $e) { + $this->assertSame($e, $ex); + $this->assertSame($request, $ex->getRequest()); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php new file mode 100644 index 000000000..3892fb62b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php @@ -0,0 +1,345 @@ + 'foo', + 'consumer_secret' => 'bar', + 'token' => 'count', + 'token_secret' => 'dracula' + ); + + protected function getRequest() + { + return RequestFactory::getInstance()->create('POST', 'http://www.test.com/path?a=b&c=d', null, array( + 'e' => 'f' + )); + } + + public function testSubscribesToEvents() + { + $events = OauthPlugin::getSubscribedEvents(); + $this->assertArrayHasKey('request.before_send', $events); + } + + public function testAcceptsConfigurationData() + { + $p = new OauthPlugin($this->config); + + // Access the config object + $class = new \ReflectionClass($p); + $property = $class->getProperty('config'); + $property->setAccessible(true); + $config = $property->getValue($p); + + $this->assertEquals('foo', $config['consumer_key']); + $this->assertEquals('bar', $config['consumer_secret']); + $this->assertEquals('count', $config['token']); + $this->assertEquals('dracula', $config['token_secret']); + $this->assertEquals('1.0', $config['version']); + $this->assertEquals('HMAC-SHA1', $config['signature_method']); + $this->assertEquals('header', $config['request_method']); + } + + public function testCreatesStringToSignFromPostRequest() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); + + $this->assertContains('&e=f', rawurldecode($signString)); + + $expectedSignString = + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0'; + + $this->assertEquals($expectedSignString, $signString); + } + + public function testCreatesStringToSignIgnoringPostFields() + { + $config = $this->config; + $config['disable_post_params'] = true; + $p = new OauthPlugin($config); + $request = $this->getRequest(); + $sts = rawurldecode($p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + $this->assertNotContains('&e=f', $sts); + } + + public function testCreatesStringToSignFromPostRequestWithCustomContentType() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->setHeader('Content-Type', 'Foo'); + $this->assertEquals( + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D'. self::NONCE .'%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0', + $p->getStringToSign($request, self::TIMESTAMP, self::NONCE) + ); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testConvertsBooleansToStrings() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->getQuery()->set('a', true); + $request->getQuery()->set('c', false); + $this->assertContains('&a%3Dtrue%26c%3Dfalse', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + public function testCreatesStringToSignFromPostRequestWithNullValues() + { + $config = array( + 'consumer_key' => 'foo', + 'consumer_secret' => 'bar', + 'token' => null, + 'token_secret' => 'dracula' + ); + + $p = new OauthPlugin($config); + $request = $this->getRequest(); + $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); + + $this->assertContains('&e=f', rawurldecode($signString)); + + $expectedSignString = // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_version%3D1.0'; + + $this->assertEquals($expectedSignString, $signString); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testMultiDimensionalArray() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->getQuery()->set('a', array('b' => array('e' => 'f', 'c' => 'd'))); + $this->assertContains('a%255Bb%255D%255Bc%255D%3Dd%26a%255Bb%255D%255Be%255D%3Df%26c%3Dd%26e%3Df%26', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + /** + * @depends testMultiDimensionalArray + */ + public function testMultiDimensionalArrayWithNonDefaultQueryAggregator() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $aggregator = new CommaAggregator(); + $query = $request->getQuery()->setAggregator($aggregator) + ->set('g', array('h', 'i', 'j')) + ->set('k', array('l')) + ->set('m', array('n', 'o')); + $this->assertContains('a%3Db%26c%3Dd%26e%3Df%26g%3Dh%2Ci%2Cj%26k%3Dl%26m%3Dn%2Co', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testSignsStrings() + { + $p = new OauthPlugin(array_merge($this->config, array( + 'signature_callback' => function($string, $key) { + return "_{$string}|{$key}_"; + } + ))); + $request = $this->getRequest(); + $sig = $p->getSignature($request, self::TIMESTAMP, self::NONCE); + $this->assertEquals( + '_POST&http%3A%2F%2Fwww.test.com%2Fpath&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D'. self::NONCE .'%26oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0|' . + 'bar&dracula_', + base64_decode($sig) + ); + } + + /** + * Test that the Oauth is signed correctly and that extra strings haven't been added + * to the authorization header. + */ + public function testSignsOauthRequests() + { + $p = new OauthPlugin($this->config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + $params = $p->onRequestBeforeSend($event); + + $this->assertTrue($event['request']->hasHeader('Authorization')); + + $authorizationHeader = (string)$event['request']->getHeader('Authorization'); + + $this->assertStringStartsWith('OAuth ', $authorizationHeader); + + $stringsToCheck = array( + 'oauth_consumer_key="foo"', + 'oauth_nonce="'.urlencode($params['oauth_nonce']).'"', + 'oauth_signature="'.urlencode($params['oauth_signature']).'"', + 'oauth_signature_method="HMAC-SHA1"', + 'oauth_timestamp="' . self::TIMESTAMP . '"', + 'oauth_token="count"', + 'oauth_version="1.0"', + ); + + $totalLength = strlen('OAuth '); + + //Separator is not used before first parameter. + $separator = ''; + + foreach ($stringsToCheck as $stringToCheck) { + $this->assertContains($stringToCheck, $authorizationHeader); + $totalLength += strlen($separator); + $totalLength += strlen($stringToCheck); + $separator = ', '; + } + + // Technically this test is not universally valid. It would be allowable to have extra \n characters + // in the Authorization header. However Guzzle does not do this, so we just perform a simple check + // on length to validate the Authorization header is composed of only the strings above. + $this->assertEquals($totalLength, strlen($authorizationHeader), 'Authorization has extra characters i.e. contains extra elements compared to stringsToCheck.'); + } + + public function testSignsOauthQueryStringRequest() + { + $config = array_merge( + $this->config, + array('request_method' => OauthPlugin::REQUEST_METHOD_QUERY) + ); + + $p = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + $params = $p->onRequestBeforeSend($event); + + $this->assertFalse($event['request']->hasHeader('Authorization')); + + $stringsToCheck = array( + 'a=b', + 'c=d', + 'oauth_consumer_key=foo', + 'oauth_nonce='.urlencode($params['oauth_nonce']), + 'oauth_signature='.urlencode($params['oauth_signature']), + 'oauth_signature_method=HMAC-SHA1', + 'oauth_timestamp='.self::TIMESTAMP, + 'oauth_token=count', + 'oauth_version=1.0', + ); + + $queryString = (string) $event['request']->getQuery(); + + $totalLength = strlen('?'); + + //Separator is not used before first parameter. + $separator = ''; + + foreach ($stringsToCheck as $stringToCheck) { + $this->assertContains($stringToCheck, $queryString); + $totalLength += strlen($separator); + $totalLength += strlen($stringToCheck); + $separator = '&'; + } + + // Removes the last query string separator '&' + $totalLength -= 1; + + $this->assertEquals($totalLength, strlen($queryString), 'Query string has extra characters i.e. contains extra elements compared to stringsToCheck.'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidArgumentExceptionOnMethodError() + { + $config = array_merge( + $this->config, + array('request_method' => 'FakeMethod') + ); + + $p = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + + $p->onRequestBeforeSend($event); + } + + public function testDoesNotAddFalseyValuesToAuthorization() + { + unset($this->config['token']); + $p = new OauthPlugin($this->config); + $event = new Event(array('request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP)); + $p->onRequestBeforeSend($event); + $this->assertTrue($event['request']->hasHeader('Authorization')); + $this->assertNotContains('oauth_token=', (string) $event['request']->getHeader('Authorization')); + } + + public function testOptionalOauthParametersAreNotAutomaticallyAdded() + { + // The only required Oauth parameters are the consumer key and secret. That is enough credentials + // for signing oauth requests. + $config = array( + 'consumer_key' => 'foo', + 'consumer_secret' => 'bar', + ); + + $plugin = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + + $timestamp = $plugin->getTimestamp($event); + $request = $event['request']; + $nonce = $plugin->generateNonce($request); + + $paramsToSign = $plugin->getParamsToSign($request, $timestamp, $nonce); + + $optionalParams = array( + 'callback' => 'oauth_callback', + 'token' => 'oauth_token', + 'verifier' => 'oauth_verifier', + 'token_secret' => 'token_secret' + ); + + foreach ($optionalParams as $optionName => $oauthName) { + $this->assertArrayNotHasKey($oauthName, $paramsToSign, "Optional Oauth param '$oauthName' was not set via config variable '$optionName', but it is listed in getParamsToSign()."); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php new file mode 100644 index 000000000..8b42fb839 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php @@ -0,0 +1,149 @@ +loader = $this->getMockBuilder('Guzzle\Service\AbstractConfigLoader') + ->setMethods(array('build')) + ->getMockForAbstractClass(); + } + + public function tearDown() + { + foreach ($this->cleanup as $file) { + unlink($file); + } + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnlyLoadsSupportedTypes() + { + $this->loader->load(new \stdClass()); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to open fooooooo.json + */ + public function testFileMustBeReadable() + { + $this->loader->load('fooooooo.json'); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unknown file extension + */ + public function testMustBeSupportedExtension() + { + $this->loader->load(dirname(__DIR__) . '/TestData/FileBody.txt'); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Error loading JSON data from + */ + public function testJsonMustBeValue() + { + $filename = tempnam(sys_get_temp_dir(), 'json') . '.json'; + file_put_contents($filename, '{/{./{}foo'); + $this->cleanup[] = $filename; + $this->loader->load($filename); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage PHP files must return an array + */ + public function testPhpFilesMustReturnAnArray() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, 'cleanup[] = $filename; + $this->loader->load($filename); + } + + public function testLoadsPhpFileIncludes() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, ' "bar");'); + $this->cleanup[] = $filename; + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $config = $this->loader->load($filename); + $this->assertEquals(array('foo' => 'bar'), $config); + } + + public function testCanCreateFromJson() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($file); + // Ensure that the config files were merged using the includes directives + $this->assertArrayHasKey('includes', $data); + $this->assertArrayHasKey('services', $data); + $this->assertInternalType('array', $data['services']['foo']); + $this->assertInternalType('array', $data['services']['abstract']); + $this->assertInternalType('array', $data['services']['mock']); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testUsesAliases() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $this->loader->addAlias('foo', $file); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load('foo'); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to open foo.json + */ + public function testCanRemoveAliases() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $this->loader->addAlias('foo.json', $file); + $this->loader->removeAlias('foo.json'); + $this->loader->load('foo.json'); + } + + public function testCanLoadArraysWithIncludes() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $config = array('includes' => array($file)); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($config); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testDoesNotEnterInfiniteLoop() + { + $prefix = $file = dirname(__DIR__) . '/TestData/description'; + $this->loader->load("{$prefix}/baz.json"); + $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); + // Ensure that the internal list of loaded files is reset + $this->loader->load("{$prefix}/../test_service2.json"); + $this->assertCount(1, $this->readAttribute($this->loader, 'loadedFiles')); + // Ensure that previously loaded files will be reloaded when starting fresh + $this->loader->load("{$prefix}/baz.json"); + $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php new file mode 100644 index 000000000..f63070e38 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php @@ -0,0 +1,177 @@ + array( + 'abstract' => array( + 'params' => array( + 'access_key' => 'xyz', + 'secret' => 'abc', + ), + ), + 'foo' => array( + 'extends' => 'abstract', + 'params' => array( + 'baz' => 'bar', + ), + ), + 'mock' => array( + 'extends' => 'abstract', + 'params' => array( + 'username' => 'foo', + 'password' => 'baz', + 'subdomain' => 'bar', + ) + ) + ) + ); + + $builder = $arrayFactory->load($data); + + // Ensure that services were parsed + $this->assertTrue(isset($builder['mock'])); + $this->assertTrue(isset($builder['abstract'])); + $this->assertTrue(isset($builder['foo'])); + $this->assertFalse(isset($builder['jimmy'])); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage foo is trying to extend a non-existent service: abstract + */ + public function testThrowsExceptionWhenExtendingNonExistentService() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'extends' => 'abstract' + ) + ) + ); + + $builder = $arrayFactory->load($data); + } + + public function testAllowsGlobalParameterOverrides() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'params' => array( + 'foo' => 'baz', + 'bar' => 'boo' + ) + ) + ) + ); + + $builder = $arrayFactory->load($data, array( + 'bar' => 'jar', + 'far' => 'car' + )); + + $compiled = json_decode($builder->serialize(), true); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'jar', + 'far' => 'car' + ), $compiled['foo']['params']); + } + + public function tstDoesNotErrorOnCircularReferences() + { + $arrayFactory = new ServiceBuilderLoader(); + $arrayFactory->load(array( + 'services' => array( + 'too' => array('extends' => 'ball'), + 'ball' => array('extends' => 'too'), + ) + )); + } + + public function configProvider() + { + $foo = array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '456') + ); + + return array( + array( + // Does not extend the existing `foo` service but overwrites it + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz') + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz'), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ), + array( + // Extends the existing `foo` service + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'foo', + 'params' => array('b' => '123', 'c' => 'def') + ) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '123', 'c' => 'def') + ), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ) + ); + } + + /** + * @dataProvider configProvider + */ + public function testCombinesConfigs($a, $b, $c) + { + $l = new ServiceBuilderLoader(); + $m = new \ReflectionMethod($l, 'mergeData'); + $m->setAccessible(true); + $this->assertEquals($c, $m->invoke($l, $a, $b)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php new file mode 100644 index 000000000..e1b3a1d49 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php @@ -0,0 +1,317 @@ + array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'michael', + 'password' => 'testing123', + 'subdomain' => 'michael', + ), + ), + 'billy.mock' => array( + 'alias' => 'Hello!', + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'billy', + 'password' => 'passw0rd', + 'subdomain' => 'billy', + ), + ), + 'billy.testing' => array( + 'extends' => 'billy.mock', + 'params' => array( + 'subdomain' => 'test.billy', + ), + ), + 'missing_params' => array( + 'extends' => 'billy.mock' + ) + ); + + public function testAllowsSerialization() + { + $builder = ServiceBuilder::factory($this->arrayData); + $cached = unserialize(serialize($builder)); + $this->assertEquals($cached, $builder); + } + + public function testDelegatesFactoryMethodToAbstractFactory() + { + $builder = ServiceBuilder::factory($this->arrayData); + $c = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage No service is registered as foobar + */ + public function testThrowsExceptionWhenGettingInvalidClient() + { + ServiceBuilder::factory($this->arrayData)->get('foobar'); + } + + public function testStoresClientCopy() + { + $builder = ServiceBuilder::factory($this->arrayData); + $client = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); + $this->assertEquals('http://127.0.0.1:8124/v1/michael', $client->getBaseUrl()); + $this->assertEquals($client, $builder->get('michael.mock')); + + // Get another client but throw this one away + $client2 = $builder->get('billy.mock', true); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client2); + $this->assertEquals('http://127.0.0.1:8124/v1/billy', $client2->getBaseUrl()); + + // Make sure the original client is still there and set + $this->assertTrue($client === $builder->get('michael.mock')); + + // Create a new billy.mock client that is stored + $client3 = $builder->get('billy.mock'); + + // Make sure that the stored billy.mock client is equal to the other stored client + $this->assertTrue($client3 === $builder->get('billy.mock')); + + // Make sure that this client is not equal to the previous throwaway client + $this->assertFalse($client2 === $builder->get('billy.mock')); + } + + public function testBuildersPassOptionsThroughToClients() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertEquals(8080, $c->getConfig('curl.curlopt_proxyport')); + } + + public function testUsesTheDefaultBuilderWhenNoBuilderIsSpecified() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c); + } + + public function testUsedAsArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $this->assertTrue($b->offsetExists('michael.mock')); + $this->assertFalse($b->offsetExists('not_there')); + $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']); + + unset($b['michael.mock']); + $this->assertFalse($b->offsetExists('michael.mock')); + + $b['michael.mock'] = new Client('http://www.test.com/'); + $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']); + } + + public function testFactoryCanCreateFromJson() + { + $tmp = sys_get_temp_dir() . '/test.js'; + file_put_contents($tmp, json_encode($this->arrayData)); + $b = ServiceBuilder::factory($tmp); + unlink($tmp); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testFactoryCanCreateFromArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testFactoryDoesNotRequireParams() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('missing_params'); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testBuilderAllowsReferencesBetweenClients() + { + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'other_client' => '{b}', + 'username' => 'x', + 'password' => 'y', + 'subdomain' => 'z' + ) + ), + 'b' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => '1', + 'password' => '2', + 'subdomain' => '3' + ) + ) + )); + + $client = $builder['a']; + $this->assertEquals('x', $client->getConfig('username')); + $this->assertSame($builder['b'], $client->getConfig('other_client')); + $this->assertEquals('1', $builder['b']->getConfig('username')); + } + + public function testEmitsEventsWhenClientsAreCreated() + { + // Ensure that the client signals that it emits an event + $this->assertEquals(array('service_builder.create_client'), ServiceBuilder::getAllEvents()); + + // Create a test service builder + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'test', + 'password' => '123', + 'subdomain' => 'z' + ) + ) + )); + + // Add an event listener to pick up client creation events + $emits = 0; + $builder->getEventDispatcher()->addListener('service_builder.create_client', function($event) use (&$emits) { + $emits++; + }); + + // Get the 'a' client by name + $client = $builder->get('a'); + + // Ensure that the event was emitted once, and that the client was present + $this->assertEquals(1, $emits); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); + } + + public function testCanAddGlobalParametersToServicesOnLoad() + { + $builder = ServiceBuilder::factory($this->arrayData, array( + 'username' => 'fred', + 'new_value' => 'test' + )); + + $data = json_decode($builder->serialize(), true); + + foreach ($data as $service) { + $this->assertEquals('fred', $service['params']['username']); + $this->assertEquals('test', $service['params']['new_value']); + } + } + + public function testAddsGlobalPlugins() + { + $b = new ServiceBuilder($this->arrayData); + $b->addGlobalPlugin(new HistoryPlugin()); + $s = $b->get('michael.mock'); + $this->assertTrue($s->getEventDispatcher()->hasListeners('request.sent')); + } + + public function testCanGetData() + { + $b = new ServiceBuilder($this->arrayData); + $this->assertEquals($this->arrayData['michael.mock'], $b->getData('michael.mock')); + $this->assertNull($b->getData('ewofweoweofe')); + } + + public function testCanGetByAlias() + { + $b = new ServiceBuilder($this->arrayData); + $this->assertSame($b->get('billy.mock'), $b->get('Hello!')); + } + + public function testCanOverwriteParametersForThrowawayClients() + { + $b = new ServiceBuilder($this->arrayData); + + $c1 = $b->get('michael.mock'); + $this->assertEquals('michael', $c1->getConfig('username')); + + $c2 = $b->get('michael.mock', array('username' => 'jeremy')); + $this->assertEquals('jeremy', $c2->getConfig('username')); + } + + public function testGettingAThrowawayClientWithParametersDoesNotAffectGettingOtherClients() + { + $b = new ServiceBuilder($this->arrayData); + + $c1 = $b->get('michael.mock', array('username' => 'jeremy')); + $this->assertEquals('jeremy', $c1->getConfig('username')); + + $c2 = $b->get('michael.mock'); + $this->assertEquals('michael', $c2->getConfig('username')); + } + + public function testCanUseArbitraryData() + { + $b = new ServiceBuilder(); + $b['a'] = 'foo'; + $this->assertTrue(isset($b['a'])); + $this->assertEquals('foo', $b['a']); + unset($b['a']); + $this->assertFalse(isset($b['a'])); + } + + public function testCanRegisterServiceData() + { + $b = new ServiceBuilder(); + $b['a'] = array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'billy', + 'password' => 'passw0rd', + 'subdomain' => 'billy', + ) + ); + $this->assertTrue(isset($b['a'])); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $b['a']); + $client = $b['a']; + unset($b['a']); + $this->assertFalse(isset($b['a'])); + // Ensure that instantiated clients can be registered + $b['mock'] = $client; + $this->assertSame($client, $b['mock']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php new file mode 100644 index 000000000..b8245ad58 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php @@ -0,0 +1,43 @@ +getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->once()) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load('foo')); + $this->assertEquals($data, $cache->load('foo')); + } + + public function testDoesNotCacheArrays() + { + $cache = new DoctrineCacheAdapter(new ArrayCache()); + $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->exactly(2)) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load(array())); + $this->assertEquals($data, $cache->load(array())); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php new file mode 100644 index 000000000..aee29ed8d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php @@ -0,0 +1,320 @@ +serviceTest = new ServiceDescription(array( + 'test_command' => new Operation(array( + 'doc' => 'documentationForCommand', + 'method' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'args' => array( + 'bucket' => array( + 'required' => true + ), + 'key' => array( + 'required' => true + ) + ) + )) + )); + + $this->service = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json'); + } + + public function testAllowsCustomClientParameters() + { + $client = new Mock\MockClient(null, array( + Client::COMMAND_PARAMS => array(AbstractCommand::RESPONSE_PROCESSING => 'foo') + )); + $command = $client->getCommand('mock_command'); + $this->assertEquals('foo', $command->get(AbstractCommand::RESPONSE_PROCESSING)); + } + + public function testFactoryCreatesClient() + { + $client = Client::factory(array( + 'base_url' => 'http://www.test.com/', + 'test' => '123' + )); + + $this->assertEquals('http://www.test.com/', $client->getBaseUrl()); + $this->assertEquals('123', $client->getConfig('test')); + } + + public function testFactoryDoesNotRequireBaseUrl() + { + $client = Client::factory(); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', Client::getAllEvents()); + } + + public function testExecutesCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $cmd = new MockCommand(); + $client->execute($cmd); + + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResult()); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + public function testExecutesCommandsWithArray() + { + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200), + new Response(200) + ))); + + // Create a command set and a command + $set = array(new MockCommand(), new MockCommand()); + $client->execute($set); + + // Make sure it sent + $this->assertTrue($set[0]->isExecuted()); + $this->assertTrue($set[1]->isExecuted()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidCommandIsExecuted() + { + $client = new Client(); + $client->execute(new \stdClass()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenMissingCommand() + { + $client = new Client(); + + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('test')) + ->will($this->returnValue(null)); + + $client->setCommandFactory($mock); + $client->getCommand('test'); + } + + public function testCreatesCommandsUsingCommandFactory() + { + $mockCommand = new MockCommand(); + + $client = new Mock\MockClient(); + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand)); + + $client->setCommandFactory($mock); + + $command = $client->getCommand('foo', array('acl' => '123')); + $this->assertSame($mockCommand, $command); + $command = $client->getCommand('foo', array('acl' => '123')); + $this->assertSame($mockCommand, $command); + $this->assertSame($client, $command->getClient()); + } + + public function testOwnsServiceDescription() + { + $client = new Mock\MockClient(); + $this->assertNull($client->getDescription()); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $this->assertSame($client, $client->setDescription($description)); + $this->assertSame($description, $client->getDescription()); + } + + public function testOwnsResourceIteratorFactory() + { + $client = new Mock\MockClient(); + + $method = new \ReflectionMethod($client, 'getResourceIteratorFactory'); + $method->setAccessible(TRUE); + $rf1 = $method->invoke($client); + + $rf = $this->readAttribute($client, 'resourceIteratorFactory'); + $this->assertInstanceOf('Guzzle\\Service\\Resource\\ResourceIteratorClassFactory', $rf); + $this->assertSame($rf1, $rf); + + $rf = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock'); + $client->setResourceIteratorFactory($rf); + $this->assertNotSame($rf1, $rf); + } + + public function testClientResetsRequestsBeforeExecutingCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHi", + "HTTP/1.1 200 OK\r\nContent-Length: 1\r\n\r\nI" + )); + + $client = new Mock\MockClient($this->getServer()->getUrl()); + + $command = $client->getCommand('mock_command'); + $client->execute($command); + $client->execute($command); + $this->assertEquals('I', $command->getResponse()->getBody(true)); + } + + public function testClientCreatesIterators() + { + $client = new Mock\MockClient(); + + $iterator = $client->getIterator('mock_command', array( + 'foo' => 'bar' + ), array( + 'limit' => 10 + )); + + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $this->assertEquals(10, $this->readAttribute($iterator, 'limit')); + + $command = $this->readAttribute($iterator, 'originalCommand'); + $this->assertEquals('bar', $command->get('foo')); + } + + public function testClientCreatesIteratorsWithNoOptions() + { + $client = new Mock\MockClient(); + $iterator = $client->getIterator('mock_command'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testClientCreatesIteratorsWithCommands() + { + $client = new Mock\MockClient(); + $command = new MockCommand(); + $iterator = $client->getIterator($command); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $iteratorCommand = $this->readAttribute($iterator, 'originalCommand'); + $this->assertSame($command, $iteratorCommand); + } + + public function testClientHoldsInflector() + { + $client = new Mock\MockClient(); + $this->assertInstanceOf('Guzzle\Inflection\MemoizingInflector', $client->getInflector()); + + $inflector = new Inflector(); + $client->setInflector($inflector); + $this->assertSame($inflector, $client->getInflector()); + } + + public function testClientAddsGlobalCommandOptions() + { + $client = new Mock\MockClient('http://www.foo.com', array( + Client::COMMAND_PARAMS => array( + 'mesa' => 'bar' + ) + )); + $command = $client->getCommand('mock_command'); + $this->assertEquals('bar', $command->get('mesa')); + } + + public function testSupportsServiceDescriptionBaseUrls() + { + $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); + $client = new Client(); + $client->setDescription($description); + $this->assertEquals('http://foo.com', $client->getBaseUrl()); + } + + public function testMergesDefaultCommandParamsCorrectly() + { + $client = new Mock\MockClient('http://www.foo.com', array( + Client::COMMAND_PARAMS => array( + 'mesa' => 'bar', + 'jar' => 'jar' + ) + )); + $command = $client->getCommand('mock_command', array('jar' => 'test')); + $this->assertEquals('bar', $command->get('mesa')); + $this->assertEquals('test', $command->get('jar')); + } + + /** + * @expectedException \Guzzle\Http\Exception\BadResponseException + */ + public function testWrapsSingleCommandExceptions() + { + $client = new Mock\MockClient('http://foobaz.com'); + $mock = new MockPlugin(array(new Response(401))); + $client->addSubscriber($mock); + $client->execute(new MockCommand()); + } + + public function testWrapsMultipleCommandExceptions() + { + $client = new Mock\MockClient('http://foobaz.com'); + $mock = new MockPlugin(array(new Response(200), new Response(200), new Response(404), new Response(500))); + $client->addSubscriber($mock); + + $cmds = array(new MockCommand(), new MockCommand(), new MockCommand(), new MockCommand()); + try { + $client->execute($cmds); + } catch (CommandTransferException $e) { + $this->assertEquals(2, count($e->getFailedRequests())); + $this->assertEquals(2, count($e->getSuccessfulRequests())); + $this->assertEquals(2, count($e->getFailedCommands())); + $this->assertEquals(2, count($e->getSuccessfulCommands())); + + foreach ($e->getSuccessfulCommands() as $c) { + $this->assertTrue($c->getResponse()->isSuccessful()); + } + + foreach ($e->getFailedCommands() as $c) { + $this->assertFalse($c->getRequest()->getResponse()->isSuccessful()); + } + } + } + + public function testGetCommandAfterTwoSetDescriptions() + { + $service1 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json'); + $service2 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service_3.json'); + + $client = new Mock\MockClient(); + + $client->setDescription($service1); + $client->getCommand('foo_bar'); + $client->setDescription($service2); + $client->getCommand('baz_qux'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php new file mode 100644 index 000000000..1004fae66 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php @@ -0,0 +1,16 @@ +setDescription(ServiceDescription::factory(__DIR__ . '/../../TestData/test_service.json')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php new file mode 100644 index 000000000..d76224697 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php @@ -0,0 +1,54 @@ + function($command, $api) { + $command->set('testing', '123'); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + return $request; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + $this->assertEquals('123', $c->get('testing')); + $this->assertEquals('http://www.test.com/', $c->getRequest()->getUrl()); + } + + /** + * @expectedException UnexpectedValueException + * @expectedExceptionMessage Closure command did not return a RequestInterface object + */ + public function testMustReturnRequest() + { + $c = new ClosureCommand(array( + 'closure' => function($command, $api) { + return false; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php new file mode 100644 index 000000000..b7173d486 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php @@ -0,0 +1,445 @@ +assertEquals('123', $command->get('test')); + $this->assertFalse($command->isPrepared()); + $this->assertFalse($command->isExecuted()); + } + + public function testDeterminesShortName() + { + $api = new Operation(array('name' => 'foobar')); + $command = new MockCommand(array(), $api); + $this->assertEquals('foobar', $command->getName()); + + $command = new MockCommand(); + $this->assertEquals('mock_command', $command->getName()); + + $command = new Sub(); + $this->assertEquals('sub.sub', $command->getName()); + } + + /** + * @expectedException RuntimeException + */ + public function testGetRequestThrowsExceptionBeforePreparation() + { + $command = new MockCommand(); + $command->getRequest(); + } + + public function testGetResponseExecutesCommandsWhenNeeded() + { + $response = new Response(200); + $client = $this->getClient(); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + $this->assertSame($response, $command->getResponse()); + $this->assertSame($response, $command->getResponse()); + } + + public function testGetResultExecutesCommandsWhenNeeded() + { + $response = new Response(200); + $client = $this->getClient(); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + $this->assertSame($response, $command->getResult()); + $this->assertSame($response, $command->getResult()); + } + + public function testSetClient() + { + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client); + $this->assertEquals($client, $command->getClient()); + + unset($client); + unset($command); + + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client)->prepare(); + $this->assertEquals($client, $command->getClient()); + $this->assertTrue($command->isPrepared()); + } + + public function testExecute() + { + $client = $this->getClient(); + $response = new Response(200, array( + 'Content-Type' => 'application/xml' + ), '123'); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $this->assertSame($command, $command->setClient($client)); + + // Returns the result of the command + $this->assertInstanceOf('SimpleXMLElement', $command->execute()); + + $this->assertTrue($command->isPrepared()); + $this->assertTrue($command->isExecuted()); + $this->assertSame($response, $command->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $command->getRequest()); + // Make sure that the result was automatically set to a SimpleXMLElement + $this->assertInstanceOf('SimpleXMLElement', $command->getResult()); + $this->assertEquals('123', (string) $command->getResult()->data); + } + + public function testConvertsJsonResponsesToArray() + { + $client = $this->getClient(); + $this->setMockResponse($client, array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), '{ "key": "Hi!" }' + ) + )); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + $this->assertEquals(array( + 'key' => 'Hi!' + ), $command->getResult()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testConvertsInvalidJsonResponsesToArray() + { + $json = '{ "key": "Hi!" }invalid'; + // Some implementations of php-json extension are not strict enough + // and allow to parse invalid json ignoring invalid parts + // See https://github.com/remicollet/pecl-json-c/issues/5 + if (json_decode($json) && JSON_ERROR_NONE === json_last_error()) { + $this->markTestSkipped('php-pecl-json library regression issues'); + } + + $client = $this->getClient(); + $this->setMockResponse($client, array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), $json + ) + )); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + } + + public function testProcessResponseIsNotXml() + { + $client = $this->getClient(); + $this->setMockResponse($client, array( + new Response(200, array( + 'Content-Type' => 'application/octet-stream' + ), 'abc,def,ghi') + )); + $command = new MockCommand(); + $client->execute($command); + + // Make sure that the result was not converted to XML + $this->assertFalse($command->getResult() instanceof \SimpleXMLElement); + } + + /** + * @expectedException RuntimeException + */ + public function testExecuteThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->execute(); + } + + /** + * @expectedException RuntimeException + */ + public function testPrepareThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->prepare(); + } + + public function testCommandsAllowsCustomRequestHeaders() + { + $command = new MockCommand(); + $command->getRequestHeaders()->set('test', '123'); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('123', $command->getRequestHeaders()->get('test')); + + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', (string) $command->getRequest()->getHeader('test')); + } + + public function testCommandsAllowsCustomRequestHeadersAsArray() + { + $command = new MockCommand(array(AbstractCommand::HEADERS_OPTION => array('Foo' => 'Bar'))); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('Bar', $command->getRequestHeaders()->get('Foo')); + } + + private function getOperation() + { + return new Operation(array( + 'name' => 'foobar', + 'httpMethod' => 'POST', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'parameters' => array( + 'test' => array( + 'default' => '123', + 'type' => 'string' + ) + ))); + } + + public function testCommandsUsesOperation() + { + $api = $this->getOperation(); + $command = new MockCommand(array(), $api); + $this->assertSame($api, $command->getOperation()); + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', $command->get('test')); + $this->assertSame($api, $command->getOperation($api)); + } + + public function testCloneMakesNewRequest() + { + $client = $this->getClient(); + $command = new MockCommand(array(), $this->getOperation()); + $command->setClient($client); + + $command->prepare(); + $this->assertTrue($command->isPrepared()); + + $command2 = clone $command; + $this->assertFalse($command2->isPrepared()); + } + + public function testHasOnCompleteMethod() + { + $that = $this; + $called = 0; + + $testFunction = function($command) use (&$called, $that) { + $called++; + $that->assertInstanceOf('Guzzle\Service\Command\CommandInterface', $command); + }; + + $client = $this->getClient(); + $command = new MockCommand(array( + 'command.on_complete' => $testFunction + ), $this->getOperation()); + $command->setClient($client); + + $command->prepare()->setResponse(new Response(200), true); + $command->execute(); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnCompleteMustBeCallable() + { + $client = $this->getClient(); + $command = new MockCommand(); + $command->setOnComplete('foo'); + } + + public function testCanSetResultManually() + { + $client = $this->getClient(); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200) + ))); + $command = new MockCommand(); + $client->execute($command); + $command->setResult('foo!'); + $this->assertEquals('foo!', $command->getResult()); + } + + public function testCanInitConfig() + { + $command = $this->getMockBuilder('Guzzle\\Service\\Command\\AbstractCommand') + ->setConstructorArgs(array(array( + 'foo' => 'bar' + ), new Operation(array( + 'parameters' => array( + 'baz' => new Parameter(array( + 'default' => 'baaar' + )) + ) + )))) + ->getMockForAbstractClass(); + + $this->assertEquals('bar', $command['foo']); + $this->assertEquals('baaar', $command['baz']); + } + + public function testAddsCurlOptionsToRequestsWhenPreparing() + { + $command = new MockCommand(array( + 'foo' => 'bar', + 'curl.options' => array('CURLOPT_PROXYPORT' => 8080) + )); + $client = new Client(); + $command->setClient($client); + $request = $command->prepare(); + $this->assertEquals(8080, $request->getCurlOptions()->get(CURLOPT_PROXYPORT)); + } + + public function testIsInvokable() + { + $client = $this->getClient(); + $response = new Response(200); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + // Returns the result of the command + $this->assertSame($response, $command()); + } + + public function testCreatesDefaultOperation() + { + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); + $this->assertInstanceOf('Guzzle\Service\Description\Operation', $command->getOperation()); + } + + public function testAllowsValidatorToBeInjected() + { + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); + $v = new SchemaValidator(); + $command->setValidator($v); + $this->assertSame($v, $this->readAttribute($command, 'validator')); + } + + public function testCanDisableValidation() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate')) + ->getMock(); + $v->expects($this->never())->method('validate'); + $command->setValidator($v); + $command->set(AbstractCommand::DISABLE_VALIDATION, true); + $command->prepare(); + } + + public function testValidatorDoesNotUpdateNonDefaultValues() + { + $command = new MockCommand(array('test' => 123, 'foo' => 'bar')); + $command->setClient(new \Guzzle\Service\Client()); + $command->prepare(); + $this->assertEquals(123, $command->get('test')); + $this->assertEquals('bar', $command->get('foo')); + } + + public function testValidatorUpdatesDefaultValues() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $command->prepare(); + $this->assertEquals(123, $command->get('test')); + $this->assertEquals('abc', $command->get('_internal')); + } + + /** + * @expectedException \Guzzle\Service\Exception\ValidationException + * @expectedExceptionMessage [Foo] Baz + */ + public function testValidatesCommandBeforeSending() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate', 'getErrors')) + ->getMock(); + $v->expects($this->any())->method('validate')->will($this->returnValue(false)); + $v->expects($this->any())->method('getErrors')->will($this->returnValue(array('[Foo] Baz', '[Bar] Boo'))); + $command->setValidator($v); + $command->prepare(); + } + + /** + * @expectedException \Guzzle\Service\Exception\ValidationException + * @expectedExceptionMessage Validation errors: [abc] must be of type string + */ + public function testValidatesAdditionalParameters() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array( + 'parameters' => array( + 'baz' => array('type' => 'integer') + ), + 'additionalParameters' => array( + 'type' => 'string' + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('foo', array( + 'abc' => false, + 'command.headers' => array('foo' => 'bar') + )); + $command->prepare(); + } + + public function testCanAccessValidationErrorsFromCommand() + { + $validationErrors = array('[Foo] Baz', '[Bar] Boo'); + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + + $this->assertFalse($command->getValidationErrors()); + + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate', 'getErrors')) + ->getMock(); + $v->expects($this->any())->method('getErrors')->will($this->returnValue($validationErrors)); + $command->setValidator($v); + + $this->assertEquals($validationErrors, $command->getValidationErrors()); + } + + public function testCanChangeResponseBody() + { + $body = EntityBody::factory(); + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $command->set(AbstractCommand::RESPONSE_BODY, $body); + $request = $command->prepare(); + $this->assertSame($body, $this->readAttribute($request, 'responseBody')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php new file mode 100644 index 000000000..b7a4682fc --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php @@ -0,0 +1,122 @@ +serializer = DefaultRequestSerializer::getInstance(); + $this->client = new Client('http://foo.com/baz'); + $this->operation = new Operation(array('httpMethod' => 'POST')); + $this->command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setConstructorArgs(array(array(), $this->operation)) + ->getMockForAbstractClass(); + $this->command->setClient($this->client); + } + + public function testAllowsCustomVisitor() + { + $this->serializer->addVisitor('custom', new HeaderVisitor()); + $this->command['test'] = '123'; + $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'custom'))); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('123', (string) $request->getHeader('test')); + } + + public function testUsesRelativePath() + { + $this->operation->setUri('bar'); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar', (string) $request->getUrl()); + } + + public function testUsesRelativePathWithUriLocations() + { + $this->command['test'] = '123'; + $this->operation->setUri('bar/{test}'); + $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'uri'))); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar/123', (string) $request->getUrl()); + } + + public function testAllowsCustomFactory() + { + $f = new VisitorFlyweight(); + $serializer = new DefaultRequestSerializer($f); + $this->assertSame($f, $this->readAttribute($serializer, 'factory')); + } + + public function testMixedParams() + { + $this->operation->setUri('bar{?limit,fields}'); + $this->operation->addParam(new Parameter(array( + 'name' => 'limit', + 'location' => 'uri', + 'required' => false, + ))); + $this->operation->addParam(new Parameter(array( + 'name' => 'fields', + 'location' => 'uri', + 'required' => true, + ))); + + $this->command['fields'] = array('id', 'name'); + + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar?fields='.urlencode('id,name'), (string) $request->getUrl()); + } + + public function testValidatesAdditionalParameters() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'bar' => array('location' => 'header') + ), + 'additionalParameters' => array( + 'location' => 'json' + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('foo'); + $command['bar'] = 'test'; + $command['hello'] = 'abc'; + $request = $command->prepare(); + $this->assertEquals('test', (string) $request->getHeader('bar')); + $this->assertEquals('{"hello":"abc"}', (string) $request->getBody()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php new file mode 100644 index 000000000..a6a02f951 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php @@ -0,0 +1,59 @@ +setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'), true); + $this->assertInstanceOf('SimpleXMLElement', $op->execute()); + } + + public function testParsesJsonResponses() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"Baz":"Bar"}'), true); + $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testThrowsExceptionWhenParsingJsonFails() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array('Content-Type' => 'application/json'), '{"Baz":ddw}'), true); + $op->execute(); + } + + public function testAddsContentTypeWhenExpectsIsSetOnCommand() + { + $op = new OperationCommand(array(), new Operation()); + $op['command.expects'] = 'application/json'; + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, null, '{"Baz":"Bar"}'), true); + $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php new file mode 100644 index 000000000..ab1041ad3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php @@ -0,0 +1,76 @@ +client = new Client(); + + $map = new MapFactory(array( + 'test' => 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + $this->factory = new AliasFactory($this->client, array( + 'foo' => 'test', + 'bar' => 'sub', + 'sub' => 'test1', + 'krull' => 'test3', + 'krull_2' => 'krull', + 'sub_2' => 'bar', + 'bad_link' => 'jarjar' + )); + + $map2 = new MapFactory(array( + 'test3' => 'Guzzle\Tests\Service\Mock\Command\Sub\Sub' + )); + + $this->client->setCommandFactory(new CompositeFactory(array($map, $this->factory, $map2))); + } + + public function aliasProvider() + { + return array( + array('foo', 'Guzzle\Tests\Service\Mock\Command\MockCommand', false), + array('bar', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub_2', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('krull', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('krull_2', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('missing', null, true), + array('bad_link', null, true) + ); + } + + /** + * @dataProvider aliasProvider + */ + public function testAliasesCommands($key, $result, $exception) + { + try { + $command = $this->client->getCommand($key); + if (is_null($result)) { + $this->assertNull($command); + } else { + $this->assertInstanceof($result, $command); + } + } catch (\Exception $e) { + if (!$exception) { + $this->fail('Got exception when it was not expected'); + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php new file mode 100644 index 000000000..b896dcfd6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php @@ -0,0 +1,124 @@ +getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testIsIterable() + { + $factory = new CompositeFactory(array($this->getFactory(), $this->getFactory())); + $this->assertEquals(2, count($factory)); + $this->assertEquals(2, count(iterator_to_array($factory->getIterator()))); + } + + public function testFindsFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factory = new CompositeFactory(array($f1, $f2)); + $this->assertNull($factory->find('foo')); + $this->assertNull($factory->find($this->getFactory())); + $this->assertSame($f1, $factory->find('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertSame($f2, $factory->find('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + $this->assertSame($f1, $factory->find($f1)); + $this->assertSame($f2, $factory->find($f2)); + + $this->assertFalse($factory->has('foo')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + } + + public function testCreatesCommands() + { + $factory = new CompositeFactory(); + $this->assertNull($factory->factory('foo')); + + $f1 = $this->getFactory(); + $mockCommand1 = $this->getMockForAbstractClass('Guzzle\\Service\\Command\\AbstractCommand'); + + $f1->expects($this->once()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand1)); + + $factory = new CompositeFactory(array($f1)); + $this->assertSame($mockCommand1, $factory->factory('foo')); + } + + public function testAllowsRemovalOfFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factories = array($f1, $f2, $f3); + $factory = new CompositeFactory($factories); + + $factory->remove('foo'); + $this->assertEquals($factories, $factory->getIterator()->getArrayCopy()); + + $factory->remove($f1); + $this->assertEquals(array($f2, $f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\MapFactory'); + $this->assertEquals(array($f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + + $factory->remove('foo'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + } + + public function testAddsFactoriesBeforeAndAtEnd() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $f4 = $this->getFactory(); + + $factory = new CompositeFactory(); + + $factory->add($f1); + $this->assertEquals(array($f1), $factory->getIterator()->getArrayCopy()); + + $factory->add($f2); + $this->assertEquals(array($f1, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f3, $f2); + $this->assertEquals(array($f1, $f3, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f4, 'Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array($f1, $f4, $f3, $f2), $factory->getIterator()->getArrayCopy()); + } + + public function testProvidesDefaultChainForClients() + { + $client = $this->getMock('Guzzle\\Service\\Client'); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(1, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[0]); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $client->expects($this->once()) + ->method('getDescription') + ->will($this->returnValue($description)); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(2, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory', $a[0]); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[1]); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php new file mode 100644 index 000000000..766471822 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php @@ -0,0 +1,49 @@ + $prefix + )); + } + + $factory = new ConcreteClassFactory($client); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php new file mode 100644 index 000000000..ee720d1e7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php @@ -0,0 +1,37 @@ + 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php new file mode 100644 index 000000000..337263481 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php @@ -0,0 +1,68 @@ +getDescription(); + + $factory = new ServiceDescriptionFactory($d); + $this->assertSame($d, $factory->getServiceDescription()); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } + + public function testUsesUcFirstIfNoExactMatch() + { + $d = $this->getDescription(); + $factory = new ServiceDescriptionFactory($d, new Inflector()); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Test')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('test')); + } + + public function testUsesInflectionIfNoExactMatch() + { + $d = $this->getDescription(); + $factory = new ServiceDescriptionFactory($d, new Inflector()); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('JarJar')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('jar_jar')); + } + + protected function getDescription() + { + return ServiceDescription::factory(array( + 'operations' => array( + 'jar_jar' => array('class' => 'Guzzle\Tests\Service\Mock\Command\MockCommand'), + 'binks' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand'), + 'Test' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand') + ) + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php new file mode 100644 index 000000000..46b472eb5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php @@ -0,0 +1,110 @@ +command = new MockCommand(); + $this->request = new EntityEnclosingRequest('POST', 'http://www.test.com/some/path.php'); + $this->validator = new SchemaValidator(); + } + + protected function getCommand($location) + { + $command = new OperationCommand(array(), $this->getNestedCommand($location)); + $command->setClient(new MockClient()); + + return $command; + } + + protected function getNestedCommand($location) + { + return new Operation(array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => new Parameter(array( + 'type' => 'object', + 'location' => $location, + 'sentAs' => 'Foo', + 'required' => true, + 'properties' => array( + 'test' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'baz' => array( + 'type' => 'boolean', + 'default' => true + ), + 'jenga' => array( + 'type' => 'string', + 'default' => 'hello', + 'sentAs' => 'Jenga_Yall!', + 'filters' => array('strtoupper') + ) + ) + ), + 'bar' => array('default' => 123) + ), + 'additionalProperties' => array( + 'type' => 'string', + 'filters' => array('strtoupper'), + 'location' => $location + ) + )), + 'arr' => new Parameter(array( + 'type' => 'array', + 'location' => $location, + 'items' => array( + 'type' => 'string', + 'filters' => array('strtoupper') + ) + )), + ) + )); + } + + protected function getCommandWithArrayParamAndFilters() + { + $operation = new Operation(array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => new Parameter(array( + 'type' => 'string', + 'location' => 'query', + 'sentAs' => 'Foo', + 'required' => true, + 'default' => 'bar', + 'filters' => array('strtoupper') + )), + 'arr' => new Parameter(array( + 'type' => 'array', + 'location' => 'query', + 'sentAs' => 'Arr', + 'required' => true, + 'default' => array(123, 456, 789), + 'filters' => array(array('method' => 'implode', 'args' => array(',', '@value'))) + )) + ) + )); + $command = new OperationCommand(array(), $operation); + $command->setClient(new MockClient()); + + return $command; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php new file mode 100644 index 000000000..2a95c452a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php @@ -0,0 +1,63 @@ +getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getBody()); + $this->assertNull($this->request->getHeader('Expect')); + } + + public function testAddsExpectHeaderWhenSetToTrue() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $param->setData('expect_header', true); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getBody()); + } + + public function testCanDisableExpectHeader() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $param->setData('expect_header', false); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertNull($this->request->getHeader('Expect')); + } + + public function testCanSetExpectHeaderBasedOnSize() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + // The body is less than the cutoff + $param->setData('expect_header', 5); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertNull($this->request->getHeader('Expect')); + // Now check when the body is greater than the cutoff + $param->setData('expect_header', 2); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('100-Continue', (string) $this->request->getHeader('Expect')); + } + + public function testAddsContentEncodingWhenSetOnBody() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $body = EntityBody::factory('foo'); + $body->compress(); + $visitor->visit($this->command, $this->request, $param, $body); + $this->assertEquals('gzip', (string) $this->request->getHeader('Content-Encoding')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php new file mode 100644 index 000000000..7ea1ae913 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php @@ -0,0 +1,48 @@ +getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setAdditionalProperties(new Parameter(array())); + $visitor->visit($this->command, $this->request, $param, 'test'); + } + + public function testVisitsLocation() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setAdditionalProperties(false); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getHeader('test')); + } + + public function testVisitsMappedPrefixHeaders() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setSentAs('x-foo-'); + $param->setAdditionalProperties(new Parameter(array( + 'type' => 'string' + ))); + $visitor->visit($this->command, $this->request, $param, array( + 'bar' => 'test', + 'baz' => '123' + )); + $this->assertEquals('test', (string) $this->request->getHeader('x-foo-bar')); + $this->assertEquals('123', (string) $this->request->getHeader('x-foo-baz')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php new file mode 100644 index 000000000..ea6782f53 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php @@ -0,0 +1,60 @@ +after($this->command, $this->request); + + $param = $this->getNestedCommand('json')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test2'), 'abc'); + $visitor->after($this->command, $this->request); + $this->assertEquals('{"test":"123","test2":"abc"}', (string) $this->request->getBody()); + } + + public function testAddsJsonHeader() + { + $visitor = new Visitor(); + $visitor->setContentTypeHeader('application/json-foo'); + $param = $this->getNestedCommand('json')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $visitor->after($this->command, $this->request); + $this->assertEquals('application/json-foo', (string) $this->request->getHeader('Content-Type')); + } + + public function testRecursivelyBuildsJsonBodies() + { + $command = $this->getCommand('json'); + $request = $command->prepare(); + $this->assertEquals('{"Foo":{"test":{"baz":true,"Jenga_Yall!":"HELLO"},"bar":123}}', (string) $request->getBody()); + } + + public function testAppliesFiltersToAdditionalProperties() + { + $command = $this->getCommand('json'); + $command->set('foo', array('not_set' => 'abc')); + $request = $command->prepare(); + $result = json_decode($request->getBody(), true); + $this->assertEquals('ABC', $result['Foo']['not_set']); + } + + public function testAppliesFiltersToArrayItemValues() + { + $command = $this->getCommand('json'); + $command->set('arr', array('a', 'b')); + $request = $command->prepare(); + $result = json_decode($request->getBody(), true); + $this->assertEquals(array('A', 'B'), $result['arr']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php new file mode 100644 index 000000000..540b41087 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php @@ -0,0 +1,33 @@ +getNestedCommand('postField')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $this->assertEquals('123', (string) $this->request->getPostField('test')); + } + + public function testRecursivelyBuildsPostFields() + { + $command = $this->getCommand('postField'); + $request = $command->prepare(); + $visitor = new Visitor(); + $param = $command->getOperation()->getParam('foo'); + $visitor->visit($command, $request, $param, $command['foo']); + $visitor->after($command, $request); + $this->assertEquals( + 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', + rawurldecode((string) $request->getPostFields()) + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php new file mode 100644 index 000000000..21e3cec33 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php @@ -0,0 +1,54 @@ +getNestedCommand('postFile')->getParam('foo'); + + // Test using a path to a file + $visitor->visit($this->command, $this->request, $param->setSentAs('test_3'), __FILE__); + $this->assertInternalType('array', $this->request->getPostFile('test_3')); + + // Test with a PostFile + $visitor->visit($this->command, $this->request, $param->setSentAs(null), new PostFile('baz', __FILE__)); + $this->assertInternalType('array', $this->request->getPostFile('baz')); + } + + public function testVisitsLocationWithMultipleFiles() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'DoPost' => array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => array( + 'location' => 'postFile', + 'type' => array('string', 'array') + ) + ) + ) + ) + )); + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length:0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $client->setDescription($description); + $command = $client->getCommand('DoPost', array('foo' => array(__FILE__, __FILE__))); + $command->execute(); + $received = $this->getServer()->getReceivedRequests(); + $this->assertContains('name="foo[0]";', $received[0]); + $this->assertContains('name="foo[1]";', $received[0]); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php new file mode 100644 index 000000000..607af7603 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php @@ -0,0 +1,48 @@ +getNestedCommand('query')->getParam('foo')->setSentAs('test'); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', $this->request->getQuery()->get('test')); + } + + /** + * @covers Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor + * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively + */ + public function testRecursivelyBuildsQueryStrings() + { + $command = $this->getCommand('query'); + $command->getOperation()->getParam('foo')->setSentAs('Foo'); + $request = $command->prepare(); + $this->assertEquals( + 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', + rawurldecode($request->getQuery()) + ); + } + + /** + * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively + */ + public function testFiltersAreAppliedToArrayParamType() + { + $command = $this->getCommandWithArrayParamAndFilters(); + $request = $command->prepare(); + $query = $request->getQuery(); + // param type 'string' + $this->assertEquals('BAR', $query->get('Foo')); + // param type 'array' + $this->assertEquals('123,456,789', $query->get('Arr')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php new file mode 100644 index 000000000..ff8cec599 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php @@ -0,0 +1,20 @@ +getNestedCommand('response_body')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param, sys_get_temp_dir() . '/foo.txt'); + $body = $this->readAttribute($this->request, 'responseBody'); + $this->assertContains('/foo.txt', $body->getUri()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php new file mode 100644 index 000000000..beb58b00a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php @@ -0,0 +1,558 @@ + array( + 'xmlRoot' => array( + 'name' => 'test', + 'namespaces' => 'http://foo.com' + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array('location' => 'xml', 'type' => 'string') + ) + ), + array('Foo' => 'test', 'Baz' => 'bar'), + 'testbar' + ), + // Ensure that the content-type is not added + array(array('parameters' => array('Foo' => array('location' => 'xml', 'type' => 'string'))), array(), ''), + // Test with adding attributes and no namespace + array( + array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'test' + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string', 'data' => array('xmlAttribute' => true)) + ) + ), + array('Foo' => 'test', 'Baz' => 'bar'), + '' + ), + // Test adding with an array + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'numeric', + 'sentAs' => 'Bar' + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array(1, 2)), + 'test12' + ), + // Test adding an object + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), + 'testabcfoo' + ), + // Add an array that contains an object + array( + array( + 'parameters' => array( + 'Baz' => array( + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Bar', + 'properties' => array('A' => array(), 'B' => array()) + ) + ) + ) + ), + array('Baz' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + )), + '1234' + ), + // Add an object of attributes + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), + 'testfoo' + ), + // Check order doesn't matter + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bam' => 'foo', 'Bar' => 'abc')), + 'testfoo' + ), + // Add values with custom namespaces + array( + array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'data' => array( + 'xmlNamespace' => 'http://foo.com' + ) + ) + ) + ), + array('Foo' => 'test'), + 'test' + ), + // Add attributes with custom namespace prefix + array( + array( + 'parameters' => array( + 'Wrap' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Foo' => array( + 'type' => 'string', + 'sentAs' => 'xsi:baz', + 'data' => array( + 'xmlNamespace' => 'http://foo.com', + 'xmlAttribute' => true + ) + ) + ) + ), + ) + ), + array('Wrap' => array( + 'Foo' => 'test' + )), + '' + ), + // Add nodes with custom namespace prefix + array( + array( + 'parameters' => array( + 'Wrap' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Foo' => array( + 'type' => 'string', + 'sentAs' => 'xsi:Foo', + 'data' => array( + 'xmlNamespace' => 'http://foobar.com' + ) + ) + ) + ), + ) + ), + array('Wrap' => array( + 'Foo' => 'test' + )), + 'test' + ), + array( + array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'data' => array( + 'xmlNamespace' => 'http://foo.com' + ) + ) + ) + ), + array('Foo' => '

    This is a title

    '), + 'This is a title]]>' + ), + // Flat array at top level + array( + array( + 'parameters' => array( + 'Bars' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Bar', + 'properties' => array( + 'A' => array(), + 'B' => array() + ) + ) + ), + 'Boos' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'location' => 'xml', + 'items' => array( + 'sentAs' => 'Boo', + 'type' => 'string' + ) + ) + ) + ), + array( + 'Bars' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + ), + 'Boos' => array('test', '123') + ), + '1234test123' + ), + // Nested flat arrays + array( + array( + 'parameters' => array( + 'Delete' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Items' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Item', + 'properties' => array( + 'A' => array(), + 'B' => array() + ) + ) + ) + ) + ) + ) + ), + array( + 'Delete' => array( + 'Items' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + ) + ) + ), + '1234' + ) + ); + } + + /** + * @dataProvider xmlProvider + */ + public function testSerializesXml(array $operation, array $input, $xml) + { + $operation = new Operation($operation); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array($input, $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client('http://www.test.com/some/path.php')); + $request = $command->prepare(); + if (!empty($input)) { + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + } else { + $this->assertNull($request->getHeader('Content-Type')); + } + $body = str_replace(array("\n", ""), '', (string) $request->getBody()); + $this->assertEquals($xml, $body); + } + + public function testAddsContentTypeAndTopLevelValues() + { + $operation = new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'test', + 'namespaces' => array( + 'xsi' => 'http://foo.com' + ) + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array('location' => 'xml', 'type' => 'string') + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test', + 'Baz' => 'bar' + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + $this->assertEquals( + '' . "\n" + . 'testbar' . "\n", + (string) $request->getBody() + ); + } + + public function testCanChangeContentType() + { + $visitor = new XmlVisitor(); + $visitor->setContentTypeHeader('application/foo'); + $this->assertEquals('application/foo', $this->readAttribute($visitor, 'contentType')); + } + + public function testCanAddArrayOfSimpleTypes() + { + $request = new EntityEnclosingRequest('POST', 'http://foo.com'); + $visitor = new XmlVisitor(); + $param = new Parameter(array( + 'type' => 'object', + 'location' => 'xml', + 'name' => 'Out', + 'properties' => array( + 'Nodes' => array( + 'required' => true, + 'type' => 'array', + 'min' => 1, + 'items' => array('type' => 'string', 'sentAs' => 'Node') + ) + ) + )); + + $param->setParent(new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'Test', + 'namespaces' => array( + 'https://foo/' + ) + ) + ) + ))); + + $value = array('Nodes' => array('foo', 'baz')); + $this->assertTrue($this->validator->validate($param, $value)); + $visitor->visit($this->command, $request, $param, $value); + $visitor->after($this->command, $request); + + $this->assertEquals( + "\n" + . "foobaz\n", + (string) $request->getBody() + ); + } + + public function testCanAddMultipleNamespacesToRoot() + { + $operation = new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'Hi', + 'namespaces' => array( + 'xsi' => 'http://foo.com', + 'foo' => 'http://foobar.com' + ) + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string') + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test' + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testValuesAreFiltered() + { + $operation = new Operation(array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'filters' => array('strtoupper') + ), + 'Bar' => array( + 'location' => 'xml', + 'type' => 'object', + 'properties' => array( + 'Baz' => array( + 'filters' => array('strtoupper') + ) + ) + ) + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test', + 'Bar' => array( + 'Baz' => 'abc' + ) + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'TESTABC' . "\n", + (string) $request->getBody() + ); + } + + public function testSkipsNullValues() + { + $operation = new Operation(array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string' + ), + 'Bar' => array( + 'location' => 'xml', + 'type' => 'object', + 'properties' => array( + 'Baz' => array(), + 'Bam' => array(), + ) + ), + 'Arr' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string' + ) + ) + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => null, + 'Bar' => array( + 'Bar' => null, + 'Bam' => 'test' + ), + 'Arr' => array(null) + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testAllowsXmlEncoding() + { + $operation = new Operation(array( + 'data' => array( + 'xmlEncoding' => 'UTF-8' + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml') + ) + )); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array('Foo' => 'test'), $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testAllowsSendingXmlPayloadIfNoXmlParamsWereSet() + { + $operation = new Operation(array( + 'httpMethod' => 'POST', + 'data' => array('xmlAllowEmpty' => true), + 'parameters' => array('Foo' => array('location' => 'xml')) + )); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array(), $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client('http://foo.com')); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . '' . "\n", + (string) $request->getBody() + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php new file mode 100644 index 000000000..7b8600342 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php @@ -0,0 +1,29 @@ +value = array(); + $this->command = new MockCommand(); + $this->response = new Response(200, array( + 'X-Foo' => 'bar', + 'Content-Length' => 3, + 'Content-Type' => 'text/plain' + ), 'Foo'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php new file mode 100644 index 000000000..932e39bff --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php @@ -0,0 +1,21 @@ + 'body', 'name' => 'foo')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('Foo', (string) $this->value['foo']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php new file mode 100644 index 000000000..db54b1abb --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php @@ -0,0 +1,98 @@ + 'header', + 'name' => 'ContentType', + 'sentAs' => 'Content-Type' + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['ContentType']); + } + + public function testVisitsLocationWithFilters() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Content-Type', + 'filters' => array('strtoupper') + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('TEXT/PLAIN', $this->value['Content-Type']); + } + + public function testVisitsMappedPrefixHeaders() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Metadata', + 'sentAs' => 'X-Baz-', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'string' + ) + )); + $response = new Response(200, array( + 'X-Baz-Test' => 'ABC', + 'X-Baz-Bar' => array('123', '456'), + 'Content-Length' => 3 + ), 'Foo'); + $visitor->visit($this->command, $response, $param, $this->value); + $this->assertEquals(array( + 'Metadata' => array( + 'Test' => 'ABC', + 'Bar' => array('123', '456') + ) + ), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownHeaders() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Content-Type', + 'additionalParameters' => false + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['Content-Type']); + $this->assertArrayNotHasKey('X-Foo', $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'ContentType', + 'sentAs' => 'Content-Type', + 'additionalParameters' => false + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['ContentType']); + $this->assertArrayNotHasKey('X-Foo', $this->value); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php new file mode 100644 index 000000000..4f8d30b1e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php @@ -0,0 +1,157 @@ +getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, '{"foo":"bar"}'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('foo' => 'bar'), $result); + } + + public function testVisitsLocation() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'array', + 'items' => array( + 'filters' => 'strtoupper', + 'type' => 'string' + ) + )); + $this->value = array('foo' => array('a', 'b', 'c')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('A', 'B', 'C'), $this->value['foo']); + } + + public function testRenamesTopLevelValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'sentAs' => 'Baz', + 'type' => 'string', + )); + $this->value = array('Baz' => 'test'); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => 'test'), $this->value); + } + + public function testRenamesDoesNotFailForNonExistentKey() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('unknown' => 'Unknown')), $this->value); + } + + public function testTraversesObjectsAndAppliesFilters() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'foo' => array('filters' => 'strtoupper'), + 'bar' => array('filters' => 'strtolower') + ) + )); + $this->value = array('foo' => array('foo' => 'hello', 'bar' => 'THERE')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => 'HELLO', 'bar' => 'there'), $this->value['foo']); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'name' => 'bar', + ), + ), + )); + $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + public function testWalksAdditionalProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'object', + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'filters' => array('base64_decode') + ) + ), + ), + )); + $this->value = array('foo' => array('baz' => array('bar' => 'Zm9v'))); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('foo', $this->value['foo']['baz']['bar']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php new file mode 100644 index 000000000..23cd40fed --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php @@ -0,0 +1,21 @@ + 'reasonPhrase', 'name' => 'phrase')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('OK', $this->value['phrase']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php new file mode 100644 index 000000000..7211a5801 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php @@ -0,0 +1,21 @@ + 'statusCode', 'name' => 'code')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(200, $this->value['code']); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php new file mode 100644 index 000000000..f87cec7cd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php @@ -0,0 +1,431 @@ +getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Bar' => 'test'), $result); + } + + public function testBeforeMethodParsesXmlWithNamespace() + { + $this->markTestSkipped("Response/XmlVisitor cannot accept 'xmlns' in response, see #368 (http://git.io/USa1mA)."); + + $visitor = new Visitor(); + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Bar' => 'test'), $result); + } + + public function testBeforeMethodParsesNestedXml() + { + $visitor = new Visitor(); + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Items' => array('Bar' => 'test')), $result); + } + + public function testCanExtractAndRenameTopLevelXmlValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'sentAs' => 'Bar' + )); + $value = array('Bar' => 'test'); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertArrayHasKey('foo', $value); + $this->assertEquals('test', $value['foo']); + } + + public function testEnsuresRepeatedArraysAreInCorrectLocations() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'sentAs' => 'Foo', + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Baz' => array('type' => 'string'), + 'Bam' => array('type' => 'string') + ) + ) + )); + + $xml = new \SimpleXMLElement('12'); + $value = json_decode(json_encode($xml), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array( + 'foo' => array( + array ( + 'Bar' => '1', + 'Baz' => '2' + ) + ) + ), $value); + } + + public function testEnsuresFlatArraysAreFlat() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'type' => 'array', + 'items' => array('type' => 'string') + )); + + $value = array('foo' => array('bar', 'baz')); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar', 'baz')), $value); + + $value = array('foo' => 'bar'); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar')), $value); + } + + public function xmlDataProvider() + { + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'Items', + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'name' => 'Item', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Baz' => array('type' => 'string') + ) + ) + )); + + return array( + array($param, '12', array( + 'Items' => array( + array('Bar' => 1), + array('Bar' => 2) + ) + )), + array($param, '1', array( + 'Items' => array( + array('Bar' => 1) + ) + )), + array($param, '', array( + 'Items' => array() + )) + ); + } + + /** + * @dataProvider xmlDataProvider + */ + public function testEnsuresWrappedArraysAreInCorrectLocations($param, $xml, $result) + { + $visitor = new Visitor(); + $xml = new \SimpleXMLElement($xml); + $value = json_decode(json_encode($xml), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals($result, $value); + } + + public function testCanRenameValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'TerminatingInstances', + 'type' => 'array', + 'location' => 'xml', + 'sentAs' => 'instancesSet', + 'items' => array( + 'name' => 'item', + 'type' => 'object', + 'sentAs' => 'item', + 'properties' => array( + 'InstanceId' => array( + 'type' => 'string', + 'sentAs' => 'instanceId', + ), + 'CurrentState' => array( + 'type' => 'object', + 'sentAs' => 'currentState', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + ), + 'Name' => array( + 'type' => 'string', + 'sentAs' => 'name', + ), + ), + ), + 'PreviousState' => array( + 'type' => 'object', + 'sentAs' => 'previousState', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + ), + 'Name' => array( + 'type' => 'string', + 'sentAs' => 'name', + ), + ), + ), + ), + ) + )); + + $value = array( + 'instancesSet' => array ( + 'item' => array ( + 'instanceId' => 'i-3ea74257', + 'currentState' => array( + 'code' => '32', + 'name' => 'shutting-down', + ), + 'previousState' => array( + 'code' => '16', + 'name' => 'running', + ), + ), + ) + ); + + $visitor->visit($this->command, $this->response, $param, $value); + + $this->assertEquals(array( + 'TerminatingInstances' => array( + array( + 'InstanceId' => 'i-3ea74257', + 'CurrentState' => array( + 'Code' => '32', + 'Name' => 'shutting-down', + ), + 'PreviousState' => array( + 'Code' => '16', + 'Name' => 'running', + ) + ) + ) + ), $value); + } + + public function testCanRenameAttributes() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'RunningQueues', + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'item', + 'properties' => array( + 'QueueId' => array( + 'type' => 'string', + 'sentAs' => 'queue_id', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'CurrentState' => array( + 'type' => 'object', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'Name' => array( + 'sentAs' => 'name', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + ), + ), + 'PreviousState' => array( + 'type' => 'object', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'Name' => array( + 'sentAs' => 'name', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + ), + ), + ), + ) + )); + + $xml = ''; + $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); + $visitor->visit($this->command, $this->response, $param, $value); + + $this->assertEquals(array( + 'RunningQueues' => array( + array( + 'QueueId' => 'q-3ea74257', + 'CurrentState' => array( + 'Code' => '32', + 'Name' => 'processing', + ), + 'PreviousState' => array( + 'Code' => '16', + 'Name' => 'wait', + ), + ), + ) + ), $value); + } + + public function testAddsEmptyArraysWhenValueIsMissing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'Foo', + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'array'), + 'Bar' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'array'), + ) + ) + ) + ) + )); + + $value = array(); + $visitor->visit($this->command, $this->response, $param, $value); + + $value = array( + 'Foo' => array( + 'Bar' => array() + ) + ); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array( + 'Foo' => array( + array( + 'Bar' => array() + ) + ) + ), $value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'name' => 'bar', + ), + ), + )); + $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + public function testProperlyHandlesEmptyStringValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array('type' => 'string') + ), + )); + $xml = ''; + $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar' => '')), $value); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php new file mode 100644 index 000000000..a252ffe60 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php @@ -0,0 +1,53 @@ +assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', $f->getRequestVisitor('json')); + $this->assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', $f->getResponseVisitor('json')); + } + + public function testCanUseCustomMappings() + { + $f = new VisitorFlyweight(array()); + $this->assertEquals(array(), $this->readAttribute($f, 'mappings')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage No request visitor has been mapped for foo + */ + public function testThrowsExceptionWhenRetrievingUnknownVisitor() + { + VisitorFlyweight::getInstance()->getRequestVisitor('foo'); + } + + public function testCachesVisitors() + { + $f = new VisitorFlyweight(); + $v1 = $f->getRequestVisitor('json'); + $this->assertSame($v1, $f->getRequestVisitor('json')); + } + + public function testAllowsAddingVisitors() + { + $f = new VisitorFlyweight(); + $j1 = new JsonRequestVisitor(); + $j2 = new JsonResponseVisitor(); + $f->addRequestVisitor('json', $j1); + $f->addResponseVisitor('json', $j2); + $this->assertSame($j1, $f->getRequestVisitor('json')); + $this->assertSame($j2, $f->getResponseVisitor('json')); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php new file mode 100644 index 000000000..95fb533af --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php @@ -0,0 +1,102 @@ +getRequestSerializer(); + $b = new DefaultRequestSerializer(VisitorFlyweight::getInstance()); + $operation->setRequestSerializer($b); + $this->assertNotSame($a, $operation->getRequestSerializer()); + } + + public function testPreparesRequestUsingSerializer() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $s = $this->getMockBuilder('Guzzle\Service\Command\RequestSerializerInterface') + ->setMethods(array('prepare')) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('prepare') + ->will($this->returnValue(new EntityEnclosingRequest('POST', 'http://foo.com'))); + $op->setRequestSerializer($s); + $op->prepare(); + } + + public function testParsesResponsesWithResponseParser() + { + $op = new OperationCommand(array(), new Operation()); + $p = $this->getMockBuilder('Guzzle\Service\Command\ResponseParserInterface') + ->setMethods(array('parse')) + ->getMockForAbstractClass(); + $p->expects($this->once()) + ->method('parse') + ->will($this->returnValue(array('foo' => 'bar'))); + $op->setResponseParser($p); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200), true); + $this->assertEquals(array('foo' => 'bar'), $op->execute()); + } + + public function testParsesResponsesUsingModelParserWhenMatchingModelIsFound() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array('responseClass' => 'bar', 'responseType' => 'model') + ), + 'models' => array( + 'bar' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'string', 'location' => 'xml') + ) + ) + ) + )); + + $op = new OperationCommand(array(), $description->getOperation('foo')); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'), true); + $result = $op->execute(); + $this->assertEquals(new Model(array('Baz' => 'Bar')), $result); + } + + public function testAllowsRawResponses() + { + $description = new ServiceDescription(array( + 'operations' => array('foo' => array('responseClass' => 'bar', 'responseType' => 'model')), + 'models' => array('bar' => array()) + )); + $op = new OperationCommand(array( + OperationCommand::RESPONSE_PROCESSING => OperationCommand::TYPE_RAW + ), $description->getOperation('foo')); + $op->setClient(new Client()); + $request = $op->prepare(); + $response = new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'); + $request->setResponse($response, true); + $this->assertSame($response, $op->execute()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php new file mode 100644 index 000000000..69ba1fcea --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php @@ -0,0 +1,335 @@ +addVisitor('foo', $visitor); + $this->assertSame($visitor, $this->readAttribute($p, 'factory')->getResponseVisitor('foo')); + } + + public function testUsesParentParser() + { + $p = new OperationResponseParser(new VisitorFlyweight()); + $operation = new Operation(); + $operation->setServiceDescription(new ServiceDescription()); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($p)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/xml'), 'C'), true); + $this->assertInstanceOf('SimpleXMLElement', $op->execute()); + } + + public function testVisitsLocations() + { + $parser = new OperationResponseParser(new VisitorFlyweight(array())); + $parser->addVisitor('statusCode', new StatusCodeVisitor()); + $parser->addVisitor('reasonPhrase', new ReasonPhraseVisitor()); + $parser->addVisitor('json', new JsonVisitor()); + $op = new OperationCommand(array(), $this->getDescription()->getOperation('test')); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(201), true); + $result = $op->execute(); + $this->assertEquals(201, $result['code']); + $this->assertEquals('Created', $result['phrase']); + } + + public function testVisitsLocationsForJsonResponse() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertEquals(array( + 'baz' => 'bar', + 'enigma' => '123', + 'code' => 200, + 'phrase' => 'OK' + ), $result->toArray()); + } + + public function testSkipsUnkownModels() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $operation->setResponseClass('Baz')->setResponseType('model'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(201), true); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $op->execute()); + } + + public function testAllowsModelProcessingToBeDisabled() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $op = new OperationCommand(array('command.response_processing' => 'native'), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertInstanceOf('Guzzle\Service\Resource\Model', $result); + $this->assertEquals(array( + 'baz' => 'bar', + 'enigma' => '123' + ), $result->toArray()); + } + + public function testCanInjectModelSchemaIntoModels() + { + $parser = new OperationResponseParser(VisitorFlyweight::getInstance(), true); + $desc = $this->getDescription(); + $operation = $desc->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertSame($result->getStructure(), $desc->getModel('Foo')); + } + + public function testDoesNotParseXmlWhenNotUsingXmlVisitor() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array('baz' => array('location' => 'body')) + ) + ) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $brokenXml = '<><><>>>>'; + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), $brokenXml), true); + $result = $op->execute(); + $this->assertEquals(array('baz'), $result->getKeys()); + $this->assertEquals($brokenXml, (string) $result['baz']); + } + + public function testVisitsAdditionalProperties() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array( + 'code' => array('location' => 'statusCode') + ), + 'additionalProperties' => array( + 'location' => 'json', + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'string', + 'filters' => 'strtoupper' + ) + ) + ) + ) + ) + )); + + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '[{"a":"test"},{"a":"baz"}]'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array( + 'code' => 200, + array('a' => 'TEST'), + array('a' => 'BAZ') + ), $result); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testAdditionalPropertiesDisabledDiscardsData() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'name' => array( + 'location' => 'json', + 'type' => 'string', + ), + 'nested' => array( + 'location' => 'json', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'width' => array( + 'type' => 'integer' + ) + ), + ), + 'code' => array('location' => 'statusCode') + ), + + ) + ) + )); + + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '{"name":"test", "volume":2.0, "nested":{"width":10,"bogus":1}}'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array( + 'name' => 'test', + 'nested' => array( + 'width' => 10, + ), + 'code' => 200 + ), $result); + } + + public function testCreatesCustomResponseClassInterface() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Guzzle\Tests\Mock\CustomResponseModel')) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $result = $op->execute(); + $this->assertInstanceOf('Guzzle\Tests\Mock\CustomResponseModel', $result); + $this->assertSame($op, $result->command); + } + + /** + * @expectedException \Guzzle\Service\Exception\ResponseClassException + * @expectedExceptionMessage must exist + */ + public function testEnsuresResponseClassExists() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo\Baz\Bar')) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $op->execute(); + } + + /** + * @expectedException \Guzzle\Service\Exception\ResponseClassException + * @expectedExceptionMessage and implement + */ + public function testEnsuresResponseClassImplementsResponseClassInterface() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => __CLASS__)) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $op->execute(); + } + + protected function getDescription() + { + return ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array( + 'baz' => array('type' => 'string', 'location' => 'json'), + 'code' => array('location' => 'statusCode'), + 'phrase' => array('location' => 'reasonPhrase'), + ) + ) + ) + )); + } + + public function testCanAddListenerToParseDomainObjects() + { + $client = new Client(); + $client->setDescription(ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'FooBazBar')) + ))); + $foo = new \stdClass(); + $client->getEventDispatcher()->addListener('command.parse_response', function ($e) use ($foo) { + $e['result'] = $foo; + }); + $command = $client->getCommand('test'); + $command->prepare()->setResponse(new Response(200), true); + $result = $command->execute(); + $this->assertSame($result, $foo); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/501 + */ + public function testAdditionalPropertiesWithRefAreResolved() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Baz' => array('type' => 'string'), + 'Foo' => array( + 'type' => 'object', + 'additionalProperties' => array('$ref' => 'Baz', 'location' => 'json') + ) + ) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '{"a":"a","b":"b","c":"c"}'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array('a' => 'a', 'b' => 'b', 'c' => 'c'), $result); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php new file mode 100644 index 000000000..ae33b6925 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php @@ -0,0 +1,308 @@ + 'test', + 'summary' => 'doc', + 'notes' => 'notes', + 'documentationUrl' => 'http://www.example.com', + 'httpMethod' => 'POST', + 'uri' => '/api/v1', + 'responseClass' => 'array', + 'responseNotes' => 'returns the json_decoded response', + 'deprecated' => true, + 'parameters' => array( + 'key' => array( + 'required' => true, + 'type' => 'string', + 'maxLength' => 10 + ), + 'key_2' => array( + 'required' => true, + 'type' => 'integer', + 'default' => 10 + ) + ) + )); + + $this->assertEquals('test', $c->getName()); + $this->assertEquals('doc', $c->getSummary()); + $this->assertEquals('http://www.example.com', $c->getDocumentationUrl()); + $this->assertEquals('POST', $c->getHttpMethod()); + $this->assertEquals('/api/v1', $c->getUri()); + $this->assertEquals('array', $c->getResponseClass()); + $this->assertEquals('returns the json_decoded response', $c->getResponseNotes()); + $this->assertTrue($c->getDeprecated()); + $this->assertEquals('Guzzle\\Service\\Command\\OperationCommand', $c->getClass()); + $this->assertEquals(array( + 'key' => new Parameter(array( + 'name' => 'key', + 'required' => true, + 'type' => 'string', + 'maxLength' => 10, + 'parent' => $c + )), + 'key_2' => new Parameter(array( + 'name' => 'key_2', + 'required' => true, + 'type' => 'integer', + 'default' => 10, + 'parent' => $c + )) + ), $c->getParams()); + + $this->assertEquals(new Parameter(array( + 'name' => 'key_2', + 'required' => true, + 'type' => 'integer', + 'default' => 10, + 'parent' => $c + )), $c->getParam('key_2')); + + $this->assertNull($c->getParam('afefwef')); + $this->assertArrayNotHasKey('parent', $c->getParam('key_2')->toArray()); + } + + public function testAllowsConcreteCommands() + { + $c = new Operation(array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'parameters' => array( + 'p' => new Parameter(array( + 'name' => 'foo' + )) + ) + )); + $this->assertEquals('Guzzle\\Service\\Command\ClosureCommand', $c->getClass()); + } + + public function testConvertsToArray() + { + $data = array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'summary' => 'test', + 'documentationUrl' => 'http://www.example.com', + 'httpMethod' => 'PUT', + 'uri' => '/', + 'parameters' => array('p' => array('name' => 'foo')) + ); + $c = new Operation($data); + $toArray = $c->toArray(); + unset($data['name']); + $this->assertArrayHasKey('parameters', $toArray); + $this->assertInternalType('array', $toArray['parameters']); + + // Normalize the array + unset($data['parameters']); + unset($toArray['parameters']); + + $data['responseType'] = 'primitive'; + $data['responseClass'] = 'array'; + $this->assertEquals($data, $toArray); + } + + public function testDeterminesIfHasParam() + { + $command = $this->getTestCommand(); + $this->assertTrue($command->hasParam('data')); + $this->assertFalse($command->hasParam('baz')); + } + + public function testReturnsParamNames() + { + $command = $this->getTestCommand(); + $this->assertEquals(array('data'), $command->getParamNames()); + } + + protected function getTestCommand() + { + return new Operation(array( + 'parameters' => array( + 'data' => new Parameter(array( + 'type' => 'string' + )) + ) + )); + } + + public function testCanBuildUpCommands() + { + $c = new Operation(array()); + $c->setName('foo') + ->setClass('Baz') + ->setDeprecated(false) + ->setSummary('summary') + ->setDocumentationUrl('http://www.foo.com') + ->setHttpMethod('PUT') + ->setResponseNotes('oh') + ->setResponseClass('string') + ->setUri('/foo/bar') + ->addParam(new Parameter(array( + 'name' => 'test' + ))); + + $this->assertEquals('foo', $c->getName()); + $this->assertEquals('Baz', $c->getClass()); + $this->assertEquals(false, $c->getDeprecated()); + $this->assertEquals('summary', $c->getSummary()); + $this->assertEquals('http://www.foo.com', $c->getDocumentationUrl()); + $this->assertEquals('PUT', $c->getHttpMethod()); + $this->assertEquals('oh', $c->getResponseNotes()); + $this->assertEquals('string', $c->getResponseClass()); + $this->assertEquals('/foo/bar', $c->getUri()); + $this->assertEquals(array('test'), $c->getParamNames()); + } + + public function testCanRemoveParams() + { + $c = new Operation(array()); + $c->addParam(new Parameter(array('name' => 'foo'))); + $this->assertTrue($c->hasParam('foo')); + $c->removeParam('foo'); + $this->assertFalse($c->hasParam('foo')); + } + + public function testAddsNameToParametersIfNeeded() + { + $command = new Operation(array('parameters' => array('foo' => new Parameter(array())))); + $this->assertEquals('foo', $command->getParam('foo')->getName()); + } + + public function testContainsApiErrorInformation() + { + $command = $this->getOperation(); + $this->assertEquals(1, count($command->getErrorResponses())); + $arr = $command->toArray(); + $this->assertEquals(1, count($arr['errorResponses'])); + $command->addErrorResponse(400, 'Foo', 'Baz\\Bar'); + $this->assertEquals(2, count($command->getErrorResponses())); + $command->setErrorResponses(array()); + $this->assertEquals(0, count($command->getErrorResponses())); + } + + public function testHasNotes() + { + $o = new Operation(array('notes' => 'foo')); + $this->assertEquals('foo', $o->getNotes()); + $o->setNotes('bar'); + $this->assertEquals('bar', $o->getNotes()); + } + + public function testHasData() + { + $o = new Operation(array('data' => array('foo' => 'baz', 'bar' => 123))); + $o->setData('test', false); + $this->assertEquals('baz', $o->getData('foo')); + $this->assertEquals(123, $o->getData('bar')); + $this->assertNull($o->getData('wfefwe')); + $this->assertEquals(array( + 'parameters' => array(), + 'class' => 'Guzzle\Service\Command\OperationCommand', + 'data' => array('foo' => 'baz', 'bar' => 123, 'test' => false), + 'responseClass' => 'array', + 'responseType' => 'primitive' + ), $o->toArray()); + } + + public function testHasServiceDescription() + { + $s = new ServiceDescription(); + $o = new Operation(array(), $s); + $this->assertSame($s, $o->getServiceDescription()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesResponseType() + { + $o = new Operation(array('responseClass' => 'array', 'responseType' => 'foo')); + } + + public function testInfersResponseType() + { + $o = $this->getOperation(); + $o->setServiceDescription(new ServiceDescription(array('models' => array('Foo' => array())))); + $this->assertEquals('primitive', $o->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('boolean')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('array')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('integer')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('string')->getResponseType()); + $this->assertEquals('class', $o->setResponseClass('foo')->getResponseType()); + $this->assertEquals('class', $o->setResponseClass(__CLASS__)->getResponseType()); + $this->assertEquals('model', $o->setResponseClass('Foo')->getResponseType()); + } + + public function testHasResponseType() + { + // infers in the constructor + $o = new Operation(array('responseClass' => 'array')); + $this->assertEquals('primitive', $o->getResponseType()); + // Infers when set + $o = new Operation(); + $this->assertEquals('primitive', $o->getResponseType()); + $this->assertEquals('model', $o->setResponseType('model')->getResponseType()); + } + + public function testHasAdditionalParameters() + { + $o = new Operation(array( + 'additionalParameters' => array( + 'type' => 'string', 'name' => 'binks' + ), + 'parameters' => array( + 'foo' => array('type' => 'integer') + ) + )); + $this->assertEquals('string', $o->getAdditionalParameters()->getType()); + $arr = $o->toArray(); + $this->assertEquals(array( + 'type' => 'string' + ), $arr['additionalParameters']); + } + + /** + * @return Operation + */ + protected function getOperation() + { + return new Operation(array( + 'name' => 'OperationTest', + 'class' => get_class($this), + 'parameters' => array( + 'test' => array('type' => 'object'), + 'bool_1' => array('default' => true, 'type' => 'boolean'), + 'bool_2' => array('default' => false), + 'float' => array('type' => 'numeric'), + 'int' => array('type' => 'integer'), + 'date' => array('type' => 'string'), + 'timestamp' => array('type' => 'string'), + 'string' => array('type' => 'string'), + 'username' => array('type' => 'string', 'required' => true, 'filters' => 'strtolower'), + 'test_function' => array('type' => 'string', 'filters' => __CLASS__ . '::strtoupper') + ), + 'errorResponses' => array( + array('code' => 503, 'reason' => 'InsufficientCapacity', 'class' => 'Guzzle\\Exception\\RuntimeException') + ) + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php new file mode 100644 index 000000000..b9c162aae --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php @@ -0,0 +1,411 @@ + 'foo', + 'type' => 'bar', + 'required' => true, + 'default' => '123', + 'description' => '456', + 'minLength' => 2, + 'maxLength' => 5, + 'location' => 'body', + 'static' => 'static!', + 'filters' => array('trim', 'json_encode') + ); + + public function testCreatesParamFromArray() + { + $p = new Parameter($this->data); + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('bar', $p->getType()); + $this->assertEquals(true, $p->getRequired()); + $this->assertEquals('123', $p->getDefault()); + $this->assertEquals('456', $p->getDescription()); + $this->assertEquals(2, $p->getMinLength()); + $this->assertEquals(5, $p->getMaxLength()); + $this->assertEquals('body', $p->getLocation()); + $this->assertEquals('static!', $p->getStatic()); + $this->assertEquals(array('trim', 'json_encode'), $p->getFilters()); + } + + public function testCanConvertToArray() + { + $p = new Parameter($this->data); + unset($this->data['name']); + $this->assertEquals($this->data, $p->toArray()); + } + + public function testUsesStatic() + { + $d = $this->data; + $d['default'] = 'booboo'; + $d['static'] = true; + $p = new Parameter($d); + $this->assertEquals('booboo', $p->getValue('bar')); + } + + public function testUsesDefault() + { + $d = $this->data; + $d['default'] = 'foo'; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('foo', $p->getValue(null)); + } + + public function testReturnsYourValue() + { + $d = $this->data; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('foo', $p->getValue('foo')); + } + + public function testZeroValueDoesNotCauseDefaultToBeReturned() + { + $d = $this->data; + $d['default'] = '1'; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('0', $p->getValue('0')); + } + + public function testFiltersValues() + { + $d = $this->data; + $d['static'] = null; + $d['filters'] = 'strtoupper'; + $p = new Parameter($d); + $this->assertEquals('FOO', $p->filter('foo')); + } + + public function testConvertsBooleans() + { + $p = new Parameter(array('type' => 'boolean')); + $this->assertEquals(true, $p->filter('true')); + $this->assertEquals(false, $p->filter('false')); + } + + public function testUsesArrayByDefaultForFilters() + { + $d = $this->data; + $d['filters'] = null; + $p = new Parameter($d); + $this->assertEquals(array(), $p->getFilters()); + } + + public function testAllowsSimpleLocationValue() + { + $p = new Parameter(array('name' => 'myname', 'location' => 'foo', 'sentAs' => 'Hello')); + $this->assertEquals('foo', $p->getLocation()); + $this->assertEquals('Hello', $p->getSentAs()); + } + + public function testParsesTypeValues() + { + $p = new Parameter(array('type' => 'foo')); + $this->assertEquals('foo', $p->getType()); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage A [method] value must be specified for each complex filter + */ + public function testValidatesComplexFilters() + { + $p = new Parameter(array('filters' => array(array('args' => 'foo')))); + } + + public function testCanBuildUpParams() + { + $p = new Parameter(array()); + $p->setName('foo') + ->setDescription('c') + ->setFilters(array('d')) + ->setLocation('e') + ->setSentAs('f') + ->setMaxLength(1) + ->setMinLength(1) + ->setMinimum(2) + ->setMaximum(2) + ->setMinItems(3) + ->setMaxItems(3) + ->setRequired(true) + ->setStatic(true) + ->setDefault('h') + ->setType('i'); + + $p->addFilter('foo'); + + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('h', $p->getDefault()); + $this->assertEquals('c', $p->getDescription()); + $this->assertEquals(array('d', 'foo'), $p->getFilters()); + $this->assertEquals('e', $p->getLocation()); + $this->assertEquals('f', $p->getSentAs()); + $this->assertEquals(1, $p->getMaxLength()); + $this->assertEquals(1, $p->getMinLength()); + $this->assertEquals(2, $p->getMaximum()); + $this->assertEquals(2, $p->getMinimum()); + $this->assertEquals(3, $p->getMaxItems()); + $this->assertEquals(3, $p->getMinItems()); + $this->assertEquals(true, $p->getRequired()); + $this->assertEquals(true, $p->getStatic()); + $this->assertEquals('i', $p->getType()); + } + + public function testAllowsNestedShape() + { + $command = $this->getServiceBuilder()->get('mock')->getCommand('mock_command')->getOperation(); + $param = new Parameter(array( + 'parent' => $command, + 'name' => 'foo', + 'type' => 'object', + 'location' => 'query', + 'properties' => array( + 'foo' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'baz' => array( + 'name' => 'baz', + 'type' => 'bool', + ) + ) + ), + 'bar' => array( + 'name' => 'bar', + 'default' => '123' + ) + ) + )); + + $this->assertSame($command, $param->getParent()); + $this->assertNotEmpty($param->getProperties()); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('foo')); + $this->assertSame($param, $param->getProperty('foo')->getParent()); + $this->assertSame($param->getProperty('foo'), $param->getProperty('foo')->getProperty('baz')->getParent()); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('bar')); + $this->assertSame($param, $param->getProperty('bar')->getParent()); + + $array = $param->toArray(); + $this->assertInternalType('array', $array['properties']); + $this->assertArrayHasKey('foo', $array['properties']); + $this->assertArrayHasKey('bar', $array['properties']); + } + + public function testAllowsComplexFilters() + { + $that = $this; + $param = new Parameter(array()); + $param->setFilters(array(array('method' => function ($a, $b, $c, $d) use ($that, $param) { + $that->assertEquals('test', $a); + $that->assertEquals('my_value!', $b); + $that->assertEquals('bar', $c); + $that->assertSame($param, $d); + return 'abc' . $b; + }, 'args' => array('test', '@value', 'bar', '@api')))); + $this->assertEquals('abcmy_value!', $param->filter('my_value!')); + } + + public function testCanChangeParentOfNestedParameter() + { + $param1 = new Parameter(array('name' => 'parent')); + $param2 = new Parameter(array('name' => 'child')); + $param2->setParent($param1); + $this->assertSame($param1, $param2->getParent()); + } + + public function testCanRemoveFromNestedStructure() + { + $param1 = new Parameter(array('name' => 'parent')); + $param2 = new Parameter(array('name' => 'child')); + $param1->addProperty($param2); + $this->assertSame($param1, $param2->getParent()); + $this->assertSame($param2, $param1->getProperty('child')); + + // Remove a single child from the structure + $param1->removeProperty('child'); + $this->assertNull($param1->getProperty('child')); + // Remove the entire structure + $param1->addProperty($param2); + $param1->removeProperty('child'); + $this->assertNull($param1->getProperty('child')); + } + + public function testAddsAdditionalProperties() + { + $p = new Parameter(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + )); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getAdditionalProperties()); + $this->assertNull($p->getAdditionalProperties()->getAdditionalProperties()); + $p = new Parameter(array('type' => 'object')); + $this->assertTrue($p->getAdditionalProperties()); + } + + public function testAddsItems() + { + $p = new Parameter(array( + 'type' => 'array', + 'items' => array('type' => 'string') + )); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getItems()); + $out = $p->toArray(); + $this->assertEquals('array', $out['type']); + $this->assertInternalType('array', $out['items']); + } + + public function testHasExtraProperties() + { + $p = new Parameter(); + $this->assertEquals(array(), $p->getData()); + $p->setData(array('foo' => 'bar')); + $this->assertEquals('bar', $p->getData('foo')); + $p->setData('baz', 'boo'); + $this->assertEquals(array('foo' => 'bar', 'baz' => 'boo'), $p->getData()); + } + + public function testCanRetrieveKnownPropertiesUsingDataMethod() + { + $p = new Parameter(); + $this->assertEquals(null, $p->getData('foo')); + $p->setName('test'); + $this->assertEquals('test', $p->getData('name')); + } + + public function testHasInstanceOf() + { + $p = new Parameter(); + $this->assertNull($p->getInstanceOf()); + $p->setInstanceOf('Foo'); + $this->assertEquals('Foo', $p->getInstanceOf()); + } + + public function testHasPattern() + { + $p = new Parameter(); + $this->assertNull($p->getPattern()); + $p->setPattern('/[0-9]+/'); + $this->assertEquals('/[0-9]+/', $p->getPattern()); + } + + public function testHasEnum() + { + $p = new Parameter(); + $this->assertNull($p->getEnum()); + $p->setEnum(array('foo', 'bar')); + $this->assertEquals(array('foo', 'bar'), $p->getEnum()); + } + + public function testSerializesItems() + { + $p = new Parameter(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + )); + $this->assertEquals(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + ), $p->toArray()); + } + + public function testResolvesRefKeysRecursively() + { + $description = new ServiceDescription(array( + 'models' => array( + 'JarJar' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), + 'Anakin' => array('type' => 'array', 'items' => array('$ref' => 'JarJar')) + ) + )); + $p = new Parameter(array('$ref' => 'Anakin', 'description' => 'added'), $description); + $this->assertEquals(array( + 'type' => 'array', + 'items' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), + 'description' => 'added' + ), $p->toArray()); + } + + public function testResolvesExtendsRecursively() + { + $jarJar = array('type' => 'string', 'default' => 'Mesa address tha senate!', 'description' => 'a'); + $anakin = array('type' => 'array', 'items' => array('extends' => 'JarJar', 'description' => 'b')); + $description = new ServiceDescription(array( + 'models' => array('JarJar' => $jarJar, 'Anakin' => $anakin) + )); + // Description attribute will be updated, and format added + $p = new Parameter(array('extends' => 'Anakin', 'format' => 'date'), $description); + $this->assertEquals(array( + 'type' => 'array', + 'format' => 'date', + 'items' => array( + 'type' => 'string', + 'default' => 'Mesa address tha senate!', + 'description' => 'b' + ) + ), $p->toArray()); + } + + public function testHasKeyMethod() + { + $p = new Parameter(array('name' => 'foo', 'sentAs' => 'bar')); + $this->assertEquals('bar', $p->getWireName()); + $p->setSentAs(null); + $this->assertEquals('foo', $p->getWireName()); + } + + public function testIncludesNameInToArrayWhenItemsAttributeHasName() + { + $p = new Parameter(array( + 'type' => 'array', + 'name' => 'Abc', + 'items' => array( + 'name' => 'Foo', + 'type' => 'object' + ) + )); + $result = $p->toArray(); + $this->assertEquals(array( + 'type' => 'array', + 'items' => array( + 'name' => 'Foo', + 'type' => 'object', + 'additionalProperties' => true + ) + ), $result); + } + + public function dateTimeProvider() + { + $d = 'October 13, 2012 16:15:46 UTC'; + + return array( + array($d, 'date-time', '2012-10-13T16:15:46Z'), + array($d, 'date', '2012-10-13'), + array($d, 'timestamp', strtotime($d)), + array(new \DateTime($d), 'timestamp', strtotime($d)) + ); + } + + /** + * @dataProvider dateTimeProvider + */ + public function testAppliesFormat($d, $format, $result) + { + $p = new Parameter(); + $p->setFormat($format); + $this->assertEquals($format, $p->getFormat()); + $this->assertEquals($result, $p->filter($d)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php new file mode 100644 index 000000000..eb3619bea --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php @@ -0,0 +1,61 @@ +assertEquals($result, SchemaFormatter::format($format, $value)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesDateTimeInput() + { + SchemaFormatter::format('date-time', false); + } + + public function testEnsuresTimestampsAreIntegers() + { + $t = time(); + $result = SchemaFormatter::format('timestamp', $t); + $this->assertSame($t, $result); + $this->assertInternalType('int', $result); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php new file mode 100644 index 000000000..4d6cc8728 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php @@ -0,0 +1,326 @@ +validator = new SchemaValidator(); + } + + public function testValidatesArrayListsAreNumericallyIndexed() + { + $value = array(array(1)); + $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals( + array('[Foo][0] must be an array of properties. Got a numerically indexed array.'), + $this->validator->getErrors() + ); + } + + public function testValidatesArrayListsContainProperItems() + { + $value = array(true); + $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals( + array('[Foo][0] must be of type object'), + $this->validator->getErrors() + ); + } + + public function testAddsDefaultValuesInLists() + { + $value = array(array()); + $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals(array(array('Bar' => true)), $value); + } + + public function testMergesDefaultValuesInLists() + { + $value = array( + array('Baz' => 'hello!'), + array('Bar' => false) + ); + $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals(array( + array( + 'Baz' => 'hello!', + 'Bar' => true + ), + array('Bar' => false) + ), $value); + } + + public function testCorrectlyConvertsParametersToArrayWhenArraysArePresent() + { + $param = $this->getComplexParam(); + $result = $param->toArray(); + $this->assertInternalType('array', $result['items']); + $this->assertEquals('array', $result['type']); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getItems()); + } + + public function testAllowsInstanceOf() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'instanceOf' => get_class($this) + )); + $this->assertTrue($this->validator->validate($p, $this)); + $this->assertFalse($this->validator->validate($p, $p)); + $this->assertEquals(array('[foo] must be an instance of ' . __CLASS__), $this->validator->getErrors()); + } + + public function testEnforcesInstanceOfOnlyWhenObject() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => array('object', 'string'), + 'instanceOf' => get_class($this) + )); + $this->assertTrue($this->validator->validate($p, $this)); + $s = 'test'; + $this->assertTrue($this->validator->validate($p, $s)); + } + + public function testConvertsObjectsToArraysWhenToArrayInterface() + { + $o = $this->getMockBuilder('Guzzle\Common\ToArrayInterface') + ->setMethods(array('toArray')) + ->getMockForAbstractClass(); + $o->expects($this->once()) + ->method('toArray') + ->will($this->returnValue(array( + 'foo' => 'bar' + ))); + $p = new Parameter(array( + 'name' => 'test', + 'type' => 'object', + 'properties' => array( + 'foo' => array('required' => 'true') + ) + )); + $this->assertTrue($this->validator->validate($p, $o)); + } + + public function testMergesValidationErrorsInPropertiesWithParent() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array('type' => 'string', 'required' => true, 'description' => 'This is what it does'), + 'test' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 5), + 'test2' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 2), + 'test3' => array('type' => 'integer', 'minimum' => 100), + 'test4' => array('type' => 'integer', 'maximum' => 10), + 'test5' => array('type' => 'array', 'maxItems' => 2), + 'test6' => array('type' => 'string', 'enum' => array('a', 'bc')), + 'test7' => array('type' => 'string', 'pattern' => '/[0-9]+/'), + 'test8' => array('type' => 'number'), + 'baz' => array( + 'type' => 'array', + 'minItems' => 2, + 'required' => true, + "items" => array("type" => "string") + ) + ) + )); + + $value = array( + 'test' => 'a', + 'test2' => 'abc', + 'baz' => array(false), + 'test3' => 10, + 'test4' => 100, + 'test5' => array(1, 3, 4), + 'test6' => 'Foo', + 'test7' => 'abc', + 'test8' => 'abc' + ); + + $this->assertFalse($this->validator->validate($p, $value)); + $this->assertEquals(array ( + '[foo][bar] is a required string: This is what it does', + '[foo][baz] must contain 2 or more elements', + '[foo][baz][0] must be of type string', + '[foo][test2] length must be less than or equal to 2', + '[foo][test3] must be greater than or equal to 100', + '[foo][test4] must be less than or equal to 10', + '[foo][test5] must contain 2 or fewer elements', + '[foo][test6] must be one of "a" or "bc"', + '[foo][test7] must match the following regular expression: /[0-9]+/', + '[foo][test8] must be of type number', + '[foo][test] length must be greater than or equal to 2', + ), $this->validator->getErrors()); + } + + public function testHandlesNullValuesInArraysWithDefaults() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'bar' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'foo' => array('default' => 'hi') + ) + ) + ) + )); + $value = array(); + $this->assertTrue($this->validator->validate($p, $value)); + $this->assertEquals(array('bar' => array('foo' => 'hi')), $value); + } + + public function testFailsWhenNullValuesInArraysWithNoDefaults() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'bar' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array('foo' => array('type' => 'string')) + ) + ) + )); + $value = array(); + $this->assertFalse($this->validator->validate($p, $value)); + $this->assertEquals(array('[foo][bar] is a required object'), $this->validator->getErrors()); + } + + public function testChecksTypes() + { + $p = new SchemaValidator(); + $r = new \ReflectionMethod($p, 'determineType'); + $r->setAccessible(true); + $this->assertEquals('any', $r->invoke($p, 'any', 'hello')); + $this->assertEquals(false, $r->invoke($p, 'foo', 'foo')); + $this->assertEquals('string', $r->invoke($p, 'string', 'hello')); + $this->assertEquals(false, $r->invoke($p, 'string', false)); + $this->assertEquals('integer', $r->invoke($p, 'integer', 1)); + $this->assertEquals(false, $r->invoke($p, 'integer', 'abc')); + $this->assertEquals('numeric', $r->invoke($p, 'numeric', 1)); + $this->assertEquals('numeric', $r->invoke($p, 'numeric', '1')); + $this->assertEquals('number', $r->invoke($p, 'number', 1)); + $this->assertEquals('number', $r->invoke($p, 'number', '1')); + $this->assertEquals(false, $r->invoke($p, 'numeric', 'a')); + $this->assertEquals('boolean', $r->invoke($p, 'boolean', true)); + $this->assertEquals('boolean', $r->invoke($p, 'boolean', false)); + $this->assertEquals(false, $r->invoke($p, 'boolean', 'false')); + $this->assertEquals('null', $r->invoke($p, 'null', null)); + $this->assertEquals(false, $r->invoke($p, 'null', 'abc')); + $this->assertEquals('array', $r->invoke($p, 'array', array())); + $this->assertEquals(false, $r->invoke($p, 'array', 'foo')); + } + + public function testValidatesFalseAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')), + 'additionalProperties' => false + )); + $value = array('test' => '123'); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test] is not an allowed property'), $this->validator->getErrors()); + $value = array('bar' => '123'); + $this->assertTrue($this->validator->validate($param, $value)); + } + + public function testAllowsUndefinedAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')) + )); + $value = array('test' => '123'); + $this->assertTrue($this->validator->validate($param, $value)); + } + + public function testValidatesAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')), + 'additionalProperties' => array('type' => 'integer') + )); + $value = array('test' => 'foo'); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test] must be of type integer'), $this->validator->getErrors()); + } + + public function testValidatesAdditionalPropertiesThatArrayArrays() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'array', + 'items' => array('type' => 'string') + ) + )); + $value = array('test' => array(true)); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test][0] must be of type string'), $this->validator->getErrors()); + } + + public function testIntegersCastToStringWhenTypeMismatch() + { + $param = new Parameter(array('name' => 'test', 'type' => 'string')); + $value = 12; + $this->assertTrue($this->validator->validate($param, $value)); + $this->assertEquals('12', $value); + } + + public function testRequiredMessageIncludesType() + { + $param = new Parameter(array('name' => 'test', 'type' => array('string', 'boolean'), 'required' => true)); + $value = null; + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[test] is a required string or boolean'), $this->validator->getErrors()); + } + + protected function getComplexParam() + { + return new Parameter(array( + 'name' => 'Foo', + 'type' => 'array', + 'required' => true, + 'min' => 1, + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array( + 'type' => 'string', + ), + 'Bar' => array( + 'required' => true, + 'type' => 'boolean', + 'default' => true + ) + ) + ) + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php new file mode 100644 index 000000000..bbfd1d6df --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php @@ -0,0 +1,177 @@ + true, + 'baz' => array('bar'), + 'apiVersion' => '123', + 'operations' => array() + )); + + $this->assertEquals(true, $d->getData('foo')); + $this->assertEquals(array('bar'), $d->getData('baz')); + $this->assertEquals('123', $d->getApiVersion()); + } + + public function testAllowsDeepNestedInheritance() + { + $d = ServiceDescription::factory(array( + 'operations' => array( + 'abstract' => array( + 'httpMethod' => 'HEAD', + 'parameters' => array( + 'test' => array('type' => 'string', 'required' => true) + ) + ), + 'abstract2' => array('uri' => '/test', 'extends' => 'abstract'), + 'concrete' => array('extends' => 'abstract2'), + 'override' => array('extends' => 'abstract', 'httpMethod' => 'PUT'), + 'override2' => array('extends' => 'override', 'httpMethod' => 'POST', 'uri' => '/') + ) + )); + + $c = $d->getOperation('concrete'); + $this->assertEquals('/test', $c->getUri()); + $this->assertEquals('HEAD', $c->getHttpMethod()); + $params = $c->getParams(); + $param = $params['test']; + $this->assertEquals('string', $param->getType()); + $this->assertTrue($param->getRequired()); + + // Ensure that merging HTTP method does not make an array + $this->assertEquals('PUT', $d->getOperation('override')->getHttpMethod()); + $this->assertEquals('POST', $d->getOperation('override2')->getHttpMethod()); + $this->assertEquals('/', $d->getOperation('override2')->getUri()); + } + + /** + * @expectedException RuntimeException + */ + public function testThrowsExceptionWhenExtendingMissingCommand() + { + ServiceDescription::factory(array( + 'operations' => array( + 'concrete' => array( + 'extends' => 'missing' + ) + ) + )); + } + + public function testAllowsMultipleInheritance() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'a' => array( + 'httpMethod' => 'GET', + 'parameters' => array( + 'a1' => array( + 'default' => 'foo', + 'required' => true, + 'prepend' => 'hi' + ) + ) + ), + 'b' => array( + 'extends' => 'a', + 'parameters' => array( + 'b2' => array() + ) + ), + 'c' => array( + 'parameters' => array( + 'a1' => array( + 'default' => 'bar', + 'required' => true, + 'description' => 'test' + ), + 'c3' => array() + ) + ), + 'd' => array( + 'httpMethod' => 'DELETE', + 'extends' => array('b', 'c'), + 'parameters' => array( + 'test' => array() + ) + ) + ) + )); + + $command = $description->getOperation('d'); + $this->assertEquals('DELETE', $command->getHttpMethod()); + $this->assertContains('a1', $command->getParamNames()); + $this->assertContains('b2', $command->getParamNames()); + $this->assertContains('c3', $command->getParamNames()); + $this->assertContains('test', $command->getParamNames()); + + $this->assertTrue($command->getParam('a1')->getRequired()); + $this->assertEquals('bar', $command->getParam('a1')->getDefault()); + $this->assertEquals('test', $command->getParam('a1')->getDescription()); + } + + public function testAddsOtherFields() + { + $description = ServiceDescription::factory(array( + 'operations' => array(), + 'description' => 'Foo', + 'apiVersion' => 'bar' + )); + $this->assertEquals('Foo', $description->getDescription()); + $this->assertEquals('bar', $description->getApiVersion()); + } + + public function testCanLoadNestedExtends() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'root' => array( + 'class' => 'foo' + ), + 'foo' => array( + 'extends' => 'root', + 'parameters' => array( + 'baz' => array('type' => 'string') + ) + ), + 'foo_2' => array( + 'extends' => 'foo', + 'parameters' => array( + 'bar' => array('type' => 'string') + ) + ), + 'foo_3' => array( + 'class' => 'bar', + 'parameters' => array( + 'bar2' => array('type' => 'string') + ) + ), + 'foo_4' => array( + 'extends' => array('foo_2', 'foo_3'), + 'parameters' => array( + 'bar3' => array('type' => 'string') + ) + ) + ) + )); + + $this->assertTrue($description->hasOperation('foo_4')); + $foo4 = $description->getOperation('foo_4'); + $this->assertTrue($foo4->hasParam('baz')); + $this->assertTrue($foo4->hasParam('bar')); + $this->assertTrue($foo4->hasParam('bar2')); + $this->assertTrue($foo4->hasParam('bar3')); + $this->assertEquals('bar', $foo4->getClass()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php new file mode 100644 index 000000000..ff2545235 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php @@ -0,0 +1,240 @@ +serviceData = array( + 'test_command' => new Operation(array( + 'name' => 'test_command', + 'description' => 'documentationForCommand', + 'httpMethod' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'parameters' => array( + 'bucket' => array('required' => true), + 'key' => array('required' => true) + ) + )) + ); + } + + /** + * @covers Guzzle\Service\Description\ServiceDescription::factory + * @covers Guzzle\Service\Description\ServiceDescriptionLoader::build + */ + public function testFactoryDelegatesToConcreteFactories() + { + $jsonFile = __DIR__ . '/../../TestData/test_service.json'; + $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($jsonFile)); + } + + public function testConstructor() + { + $service = new ServiceDescription(array('operations' => $this->serviceData)); + $this->assertEquals(1, count($service->getOperations())); + $this->assertFalse($service->hasOperation('foobar')); + $this->assertTrue($service->hasOperation('test_command')); + } + + public function testIsSerializable() + { + $service = new ServiceDescription(array('operations' => $this->serviceData)); + $data = serialize($service); + $d2 = unserialize($data); + $this->assertEquals(serialize($service), serialize($d2)); + } + + public function testSerializesParameters() + { + $service = new ServiceDescription(array( + 'operations' => array( + 'foo' => new Operation(array('parameters' => array('foo' => array('type' => 'string')))) + ) + )); + $serialized = serialize($service); + $this->assertContains('"parameters":{"foo":', $serialized); + $service = unserialize($serialized); + $this->assertTrue($service->getOperation('foo')->hasParam('foo')); + } + + public function testAllowsForJsonBasedArrayParamsFunctionalTest() + { + $description = new ServiceDescription(array( + 'operations' => array( + 'test' => new Operation(array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'data' => array( + 'required' => true, + 'filters' => 'json_encode', + 'location' => 'body' + ) + ) + )) + ) + )); + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('test', array( + 'data' => array( + 'foo' => 'bar' + ) + )); + + $request = $command->prepare(); + $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); + } + + public function testContainsModels() + { + $d = new ServiceDescription(array( + 'operations' => array('foo' => array()), + 'models' => array( + 'Tag' => array('type' => 'object'), + 'Person' => array('type' => 'object') + ) + )); + $this->assertTrue($d->hasModel('Tag')); + $this->assertTrue($d->hasModel('Person')); + $this->assertFalse($d->hasModel('Foo')); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $d->getModel('Tag')); + $this->assertNull($d->getModel('Foo')); + $this->assertContains('"models":{', serialize($d)); + $this->assertEquals(array('Tag', 'Person'), array_keys($d->getModels())); + } + + public function testCanAddModels() + { + $d = new ServiceDescription(array()); + $this->assertFalse($d->hasModel('Foo')); + $d->addModel(new Parameter(array('name' => 'Foo'))); + $this->assertTrue($d->hasModel('Foo')); + } + + public function testHasAttributes() + { + $d = new ServiceDescription(array( + 'operations' => array(), + 'name' => 'Name', + 'description' => 'Description', + 'apiVersion' => '1.24' + )); + + $this->assertEquals('Name', $d->getName()); + $this->assertEquals('Description', $d->getDescription()); + $this->assertEquals('1.24', $d->getApiVersion()); + + $s = serialize($d); + $this->assertContains('"name":"Name"', $s); + $this->assertContains('"description":"Description"', $s); + $this->assertContains('"apiVersion":"1.24"', $s); + + $d = unserialize($s); + $this->assertEquals('Name', $d->getName()); + $this->assertEquals('Description', $d->getDescription()); + $this->assertEquals('1.24', $d->getApiVersion()); + } + + public function testPersistsCustomAttributes() + { + $data = array( + 'operations' => array('foo' => array('class' => 'foo', 'parameters' => array())), + 'name' => 'Name', + 'description' => 'Test', + 'apiVersion' => '1.24', + 'auth' => 'foo', + 'keyParam' => 'bar' + ); + $d = new ServiceDescription($data); + $d->setData('hello', 'baz'); + $this->assertEquals('foo', $d->getData('auth')); + $this->assertEquals('baz', $d->getData('hello')); + $this->assertEquals('bar', $d->getData('keyParam')); + // responseClass and responseType are added by default + $data['operations']['foo']['responseClass'] = 'array'; + $data['operations']['foo']['responseType'] = 'primitive'; + $this->assertEquals($data + array('hello' => 'baz'), json_decode($d->serialize(), true)); + } + + public function testHasToArray() + { + $data = array( + 'operations' => array(), + 'name' => 'Name', + 'description' => 'Test' + ); + $d = new ServiceDescription($data); + $arr = $d->toArray(); + $this->assertEquals('Name', $arr['name']); + $this->assertEquals('Test', $arr['description']); + } + + public function testReturnsNullWhenRetrievingMissingOperation() + { + $s = new ServiceDescription(array()); + $this->assertNull($s->getOperation('foo')); + } + + public function testCanAddOperations() + { + $s = new ServiceDescription(array()); + $this->assertFalse($s->hasOperation('Foo')); + $s->addOperation(new Operation(array('name' => 'Foo'))); + $this->assertTrue($s->hasOperation('Foo')); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesOperationTypes() + { + $s = new ServiceDescription(array( + 'operations' => array('foo' => new \stdClass()) + )); + } + + public function testHasBaseUrl() + { + $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); + $this->assertEquals('http://foo.com', $description->getBaseUrl()); + $description->setBaseUrl('http://foobar.com'); + $this->assertEquals('http://foobar.com', $description->getBaseUrl()); + } + + public function testCanUseBasePath() + { + $description = new ServiceDescription(array('basePath' => 'http://foo.com')); + $this->assertEquals('http://foo.com', $description->getBaseUrl()); + } + + public function testModelsHaveNames() + { + $desc = array( + 'models' => array( + 'date' => array('type' => 'string'), + 'user'=> array( + 'type' => 'object', + 'properties' => array( + 'dob' => array('$ref' => 'date') + ) + ) + ) + ); + + $s = ServiceDescription::factory($desc); + $this->assertEquals('date', $s->getModel('date')->getName()); + $this->assertEquals('dob', $s->getModel('user')->getProperty('dob')->getName()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php new file mode 100644 index 000000000..be0d4ac8b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php @@ -0,0 +1,66 @@ +addSuccessfulCommand($c1)->addFailedCommand($c2); + $this->assertSame(array($c1), $e->getSuccessfulCommands()); + $this->assertSame(array($c2), $e->getFailedCommands()); + $this->assertSame(array($c1, $c2), $e->getAllCommands()); + } + + public function testConvertsMultiExceptionIntoCommandTransfer() + { + $r1 = new Request('GET', 'http://foo.com'); + $r2 = new Request('GET', 'http://foobaz.com'); + $e = new MultiTransferException('Test', 123); + $e->addSuccessfulRequest($r1)->addFailedRequest($r2); + $ce = CommandTransferException::fromMultiTransferException($e); + + $this->assertInstanceOf('Guzzle\Service\Exception\CommandTransferException', $ce); + $this->assertEquals('Test', $ce->getMessage()); + $this->assertEquals(123, $ce->getCode()); + $this->assertSame(array($r1), $ce->getSuccessfulRequests()); + $this->assertSame(array($r2), $ce->getFailedRequests()); + } + + public function testCanRetrieveExceptionForCommand() + { + $r1 = new Request('GET', 'http://foo.com'); + $e1 = new \Exception('foo'); + $c1 = $this->getMockBuilder('Guzzle\Tests\Service\Mock\Command\MockCommand') + ->setMethods(array('getRequest')) + ->getMock(); + $c1->expects($this->once())->method('getRequest')->will($this->returnValue($r1)); + + $e = new MultiTransferException('Test', 123); + $e->addFailedRequestWithException($r1, $e1); + $ce = CommandTransferException::fromMultiTransferException($e); + $ce->addFailedCommand($c1); + + $this->assertSame($e1, $ce->getExceptionForFailedCommand($c1)); + } + + public function testAddsNonRequestExceptions() + { + $e = new MultiTransferException(); + $e->add(new \Exception('bar')); + $e->addFailedRequestWithException(new Request('GET', 'http://www.foo.com'), new \Exception('foo')); + $ce = CommandTransferException::fromMultiTransferException($e); + $this->assertEquals(2, count($ce)); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php new file mode 100644 index 000000000..6455295ad --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php @@ -0,0 +1,15 @@ +assertEquals($items, $e->getCommands()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php new file mode 100644 index 000000000..ef789d8a9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php @@ -0,0 +1,17 @@ +setErrors($errors); + $this->assertEquals($errors, $e->getErrors()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php new file mode 100644 index 000000000..4ab423e85 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php @@ -0,0 +1,31 @@ + 'iterable_command', + 'parameters' => array( + 'page_size' => array('type' => 'integer'), + 'next_token' => array('type' => 'string') + ) + )); + } + + protected function build() + { + $this->request = $this->client->createRequest('GET'); + + // Add the next token and page size query string values + $this->request->getQuery()->set('next_token', $this->get('next_token')); + + if ($this->get('page_size')) { + $this->request->getQuery()->set('page_size', $this->get('page_size')); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php new file mode 100644 index 000000000..831a7e795 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php @@ -0,0 +1,32 @@ + get_called_class() == __CLASS__ ? 'mock_command' : 'sub.sub', + 'httpMethod' => 'POST', + 'parameters' => array( + 'test' => array( + 'default' => 123, + 'required' => true, + 'doc' => 'Test argument' + ), + '_internal' => array( + 'default' => 'abc' + ), + 'foo' => array('filters' => array('strtoupper')) + ) + )); + } + + protected function build() + { + $this->request = $this->client->createRequest(); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php new file mode 100644 index 000000000..72ae1f6a7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php @@ -0,0 +1,30 @@ + 'other_command', + 'parameters' => array( + 'test' => array( + 'default' => '123', + 'required' => true, + 'doc' => 'Test argument' + ), + 'other' => array(), + 'arg' => array('type' => 'string'), + 'static' => array('static' => true, 'default' => 'this is static') + ) + )); + } + + protected function build() + { + $this->request = $this->client->getRequest('HEAD'); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php new file mode 100644 index 000000000..d348480cf --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php @@ -0,0 +1,7 @@ + '{scheme}://127.0.0.1:8124/{api_version}/{subdomain}', + 'scheme' => 'http', + 'api_version' => 'v1' + ), array('username', 'password', 'subdomain')); + + return new self($config->get('base_url'), $config); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php new file mode 100644 index 000000000..8faf4123a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php @@ -0,0 +1,42 @@ +nextToken) { + $this->command->set('next_token', $this->nextToken); + } + + $this->command->set('page_size', (int) $this->calculatePageSize()); + $this->command->execute(); + + $data = json_decode($this->command->getResponse()->getBody(true), true); + + $this->nextToken = $data['next_token']; + + return $data['resources']; + } + + public function next() + { + $this->calledNext++; + parent::next(); + } + + public function getResources() + { + return $this->resources; + } + + public function getIteratedCount() + { + return $this->iteratedCount; + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php new file mode 100644 index 000000000..41c207372 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php @@ -0,0 +1,37 @@ +assertFalse($factory->canBuild($cmd)); + $factory->build($cmd); + } + + public function testBuildsResourceIterators() + { + $f1 = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $factory = new CompositeResourceIteratorFactory(array()); + $factory->addFactory($f1); + $command = new MockCommand(); + $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock')); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php new file mode 100644 index 000000000..d166e9261 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php @@ -0,0 +1,40 @@ +build(new MockCommand()); + } + + public function testBuildsResourceIterators() + { + $factory = new MapResourceIteratorFactory(array( + 'mock_command' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' + )); + $iterator = $factory->build(new MockCommand()); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testUsesWildcardMappings() + { + $factory = new MapResourceIteratorFactory(array( + '*' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' + )); + $iterator = $factory->build(new MockCommand()); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php new file mode 100644 index 000000000..7214133e5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php @@ -0,0 +1,65 @@ + 'object')); + $model = new Model(array('foo' => 'bar'), $param); + $this->assertSame($param, $model->getStructure()); + $this->assertEquals('bar', $model->get('foo')); + $this->assertEquals('bar', $model['foo']); + } + + public function testCanBeUsedWithoutStructure() + { + $model = new Model(array( + 'Foo' => 'baz', + 'Bar' => array( + 'Boo' => 'Bam' + ) + )); + $transform = function ($key, $value) { + return ($value && is_array($value)) ? new Collection($value) : $value; + }; + $model = $model->map($transform); + $this->assertInstanceOf('Guzzle\Common\Collection', $model->getPath('Bar')); + } + + public function testAllowsFiltering() + { + $model = new Model(array( + 'Foo' => 'baz', + 'Bar' => 'a' + )); + $model = $model->filter(function ($i, $v) { + return $v[0] == 'a'; + }); + $this->assertEquals(array('Bar' => 'a'), $model->toArray()); + } + + public function testDoesNotIncludeEmptyStructureInString() + { + $model = new Model(array('Foo' => 'baz')); + $str = (string) $model; + $this->assertContains('Debug output of model', $str); + $this->assertNotContains('Model structure', $str); + } + + public function testDoesIncludeModelStructureInString() + { + $model = new Model(array('Foo' => 'baz'), new Parameter(array('name' => 'Foo'))); + $str = (string) $model; + $this->assertContains('Debug output of Foo model', $str); + $this->assertContains('Model structure', $str); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php new file mode 100644 index 000000000..7b061b537 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php @@ -0,0 +1,41 @@ +registerNamespace('Baz'); + $command = new MockCommand(); + $factory->build($command); + } + + public function testBuildsResourceIterators() + { + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $command = new MockCommand(); + $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock')); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testChecksIfCanBuild() + { + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service'); + $this->assertFalse($factory->canBuild(new MockCommand())); + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $this->assertTrue($factory->canBuild(new MockCommand())); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php new file mode 100644 index 000000000..573fb6d6e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php @@ -0,0 +1,184 @@ +assertInternalType('array', ResourceIterator::getAllEvents()); + } + + public function testConstructorConfiguresDefaults() + { + $ri = $this->getMockForAbstractClass('Guzzle\\Service\\Resource\\ResourceIterator', array( + $this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), + array( + 'limit' => 10, + 'page_size' => 3 + ) + ), 'MockIterator'); + + $this->assertEquals(false, $ri->getNextToken()); + $this->assertEquals(false, $ri->current()); + } + + public function testSendsRequestsForNextSetOfResources() + { + // Queue up an array of responses for iterating + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }" + )); + + // Create a new resource iterator using the IterableCommand mock + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3 + )); + + // Ensure that no requests have been sent yet + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + + //$this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $ri->toArray(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[2]->getQuery()->get('page_size')); + + // Reset and resend + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", + )); + + $d = array(); + foreach ($ri as $data) { + $d[] = $data; + } + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $d); + } + + public function testCalculatesPageSize() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"j\", \"k\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3, + 'limit' => 7 + )); + + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(1, $requests[2]->getQuery()->get('page_size')); + } + + public function testUseAsArray() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"g\", \"h\", \"i\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the key is never < 0 + $this->assertEquals(0, $ri->key()); + $this->assertEquals(0, count($ri)); + + // Ensure that the iterator can be used as KVP array + $data = array(); + foreach ($ri as $key => $value) { + $data[$key] = $value; + } + + // Ensure that the iterate is countable + $this->assertEquals(6, count($ri)); + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i'), $data); + } + + public function testBailsWhenSendReturnsNoResults() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the iterator can be used as KVP array + $data = $ri->toArray(); + + // Ensure that the iterate is countable + $this->assertEquals(3, count($ri)); + $this->assertEquals(array('d', 'e', 'f'), $data); + + $this->assertEquals(2, $ri->getRequestCount()); + } + + public function testHoldsDataOptions() + { + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $this->assertNull($ri->get('foo')); + $this->assertSame($ri, $ri->set('foo', 'bar')); + $this->assertEquals('bar', $ri->get('foo')); + } + + public function testSettingLimitOrPageSizeClearsData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + + $ri->setLimit(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + $ri->setPageSize(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + } + + public function testWorksWithCustomAppendIterator() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }" + )); + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $a = new \Guzzle\Iterator\AppendIterator(); + $a->append($ri); + $results = iterator_to_array($a, false); + $this->assertEquals(4, $ri->calledNext); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php new file mode 100644 index 000000000..083aaa0d9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php @@ -0,0 +1,172 @@ +client = new Client($this->getServer()->getUrl()); + $this->factory = new PhpStreamRequestFactory(); + } + + public function testOpensValidStreamByCreatingContext() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->get('/'); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + $this->assertEquals(2, $stream->getSize()); + } + + public function testOpensValidStreamByPassingContextAndMerging() + { + $request = $this->client->get('/'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createContext', 'createStream')) + ->getMock(); + $this->factory->expects($this->never()) + ->method('createContext'); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + + $context = array('http' => array('method' => 'HEAD', 'ignore_errors' => false)); + $this->factory->fromRequest($request, stream_context_create($context)); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertEquals('HEAD', $options['http']['method']); + $this->assertFalse($options['http']['ignore_errors']); + $this->assertEquals('1.1', $options['http']['protocol_version']); + } + + public function testAppliesProxySettings() + { + $request = $this->client->get('/'); + $request->getCurlOptions()->set(CURLOPT_PROXY, 'tcp://foo.com'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertEquals('tcp://foo.com', $options['http']['proxy']); + } + + public function testAddsPostFields() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->post('/', array('Foo' => 'Bar'), array('foo' => 'baz bar')); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + + $received = $this->getServer()->getReceivedRequests(); + $this->assertEquals(1, count($received)); + $this->assertContains('POST / HTTP/1.1', $received[0]); + $this->assertContains('host: ', $received[0]); + $this->assertContains('user-agent: Guzzle/', $received[0]); + $this->assertContains('foo: Bar', $received[0]); + $this->assertContains('content-length: 13', $received[0]); + $this->assertContains('foo=baz%20bar', $received[0]); + } + + public function testAddsBody() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->put('/', array('Foo' => 'Bar'), 'Testing...123'); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + + $received = $this->getServer()->getReceivedRequests(); + $this->assertEquals(1, count($received)); + $this->assertContains('PUT / HTTP/1.1', $received[0]); + $this->assertContains('host: ', $received[0]); + $this->assertContains('user-agent: Guzzle/', $received[0]); + $this->assertContains('foo: Bar', $received[0]); + $this->assertContains('content-length: 13', $received[0]); + $this->assertContains('Testing...123', $received[0]); + } + + public function testCanDisableSslValidation() + { + $request = $this->client->get('/'); + $request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertFalse($options['ssl']['verify_peer']); + } + + public function testUsesSslValidationByDefault() + { + $request = $this->client->get('/'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertTrue($options['ssl']['verify_peer']); + $this->assertSame($request->getCurlOptions()->get(CURLOPT_CAINFO), $options['ssl']['cafile']); + } + + public function testBasicAuthAddsUserAndPassToUrl() + { + $request = $this->client->get('/'); + $request->setAuth('Foo', 'Bar'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $this->assertContains('Foo:Bar@', (string) $this->readAttribute($this->factory, 'url')); + } + + public function testCanCreateCustomStreamClass() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->get('/'); + $stream = $this->factory->fromRequest($request, array(), array('stream_class' => 'Guzzle\Http\EntityBody')); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $stream); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php new file mode 100644 index 000000000..4973f252e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php @@ -0,0 +1,189 @@ +assertEquals($handle, $stream->getStream()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isLocal()); + $this->assertTrue($stream->isSeekable()); + $this->assertEquals('PHP', $stream->getWrapper()); + $this->assertEquals('TEMP', $stream->getStreamType()); + $this->assertEquals(4, $stream->getSize()); + $this->assertEquals('php://temp', $stream->getUri()); + $this->assertEquals(array(), $stream->getWrapperData()); + $this->assertFalse($stream->isConsumed()); + unset($stream); + } + + public function testCanModifyStream() + { + $handle1 = fopen('php://temp', 'r+'); + $handle2 = fopen('php://temp', 'r+'); + $stream = new Stream($handle1); + $this->assertSame($handle1, $stream->getStream()); + $stream->setStream($handle2, 10); + $this->assertEquals(10, $stream->getSize()); + $this->assertSame($handle2, $stream->getStream()); + } + + public function testStreamClosesHandleOnDestruct() + { + $handle = fopen('php://temp', 'r'); + $stream = new Stream($handle); + unset($stream); + $this->assertFalse(is_resource($handle)); + } + + public function testConvertsToString() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('data', (string) $stream); + unset($stream); + + $handle = fopen(__DIR__ . '/../TestData/FileBody.txt', 'r'); + $stream = new Stream($handle); + $this->assertEquals('', (string) $stream); + unset($stream); + } + + public function testConvertsToStringAndRestoresCursorPos() + { + $handle = fopen('php://temp', 'w+'); + $stream = new Stream($handle); + $stream->write('foobazbar'); + $stream->seek(3); + $this->assertEquals('foobazbar', (string) $stream); + $this->assertEquals(3, $stream->ftell()); + } + + public function testIsConsumed() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertFalse($stream->isConsumed()); + $stream->read(4); + $this->assertTrue($stream->isConsumed()); + } + + public function testAllowsSettingManualSize() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $stream->setSize(10); + $this->assertEquals(10, $stream->getSize()); + unset($stream); + } + + public function testWrapsStream() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertTrue($stream->isSeekable()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('da', $stream->read(2)); + $this->assertEquals('ta', $stream->read(2)); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('data', $stream->read(4)); + $stream->write('_appended'); + $stream->seek(0); + $this->assertEquals('data_appended', $stream->read(13)); + } + + public function testGetSize() + { + $size = filesize(__DIR__ . '/../../../bootstrap.php'); + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals($handle, $stream->getStream()); + $this->assertEquals($size, $stream->getSize()); + $this->assertEquals($size, $stream->getSize()); + unset($stream); + + // Make sure that false is returned when the size cannot be determined + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $handle = fopen('http://127.0.0.1:' . $this->getServer()->getPort(), 'r'); + $stream = new Stream($handle); + $this->assertEquals(false, $stream->getSize()); + unset($stream); + } + + public function testEnsuresSizeIsConsistent() + { + $h = fopen('php://temp', 'r+'); + fwrite($h, 'foo'); + $stream = new Stream($h); + $this->assertEquals(3, $stream->getSize()); + $stream->write('test'); + $this->assertEquals(7, $stream->getSize()); + fclose($h); + } + + public function testAbstractsMetaData() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals('plainfile', $stream->getMetaData('wrapper_type')); + $this->assertEquals(null, $stream->getMetaData('wrapper_data')); + $this->assertInternalType('array', $stream->getMetaData()); + } + + public function testDoesNotAttemptToWriteToReadonlyStream() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals(0, $stream->write('foo')); + } + + public function testProvidesStreamPosition() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $stream->read(2); + $this->assertSame(ftell($handle), $stream->ftell()); + $this->assertEquals(2, $stream->ftell()); + } + + public function testRewindIsSeekZero() + { + $stream = new Stream(fopen('php://temp', 'w+')); + $stream->write('foobazbar'); + $this->assertTrue($stream->rewind()); + $this->assertEquals('foobazbar', $stream->read(9)); + } + + public function testCanDetachStream() + { + $r = fopen('php://temp', 'w+'); + $stream = new Stream($r); + $stream->detachStream(); + $this->assertNull($stream->getStream()); + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt new file mode 100644 index 000000000..e69de29bb diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json new file mode 100644 index 000000000..c354ed788 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json"] +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json new file mode 100644 index 000000000..765237b3e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json", "bar.json"] +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json new file mode 100644 index 000000000..cee5005b8 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json @@ -0,0 +1,8 @@ +{ + "includes": ["recursive.json"], + "operations": { + "abstract": { + "httpMethod": "POST" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json new file mode 100644 index 000000000..c354ed788 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json"] +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response new file mode 100644 index 000000000..b6938a28c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Length: 0 + diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json new file mode 100644 index 000000000..7b2a9dad4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json @@ -0,0 +1,18 @@ +{ + "includes": [ "json2.json" ], + "services": { + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json new file mode 100644 index 000000000..08e5566f0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json @@ -0,0 +1,11 @@ +{ + "services": { + "foo": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "baz": "bar" + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json new file mode 100644 index 000000000..25452e4a4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json @@ -0,0 +1,71 @@ +{ + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + }, + + "test.abstract.aws": { + "params": { + "access_key": "12345", + "secret_key": "abcd" + } + }, + + "test.s3": { + "class": "Guzzle\\Service\\Aws\\S3Client", + "extends": "test.abstract.aws", + "params": { + "devpay_product_token": "", + "devpay_user_token": "" + } + }, + + "test.simple_db": { + "class": "Guzzle\\Service\\Aws\\SimpleDb\\SimpleDbClient", + "extends": "test.abstract.aws" + }, + + "test.sqs": { + "class": "Guzzle\\Service\\Aws\\Sqs\\SqsClient", + "extends": "test.abstract.aws" + }, + + "test.centinel": { + "class": "Guzzle\\Service\\CardinalCommerce\\Centinel.CentinelClient", + "params": { + "password": "test", + "processor_id": "123", + "merchant_id": "456" + } + }, + + "test.mws": { + "class": "Guzzle\\Service\\Mws\\MwsClient", + "extends": "test.abstract.aws", + "params": { + "merchant_id": "ABCDE", + "marketplace_id": "FGHIJ", + "application_name": "GuzzleTest", + "application_version": "0.1", + "base_url": "https://mws.amazonservices.com" + } + }, + + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "params": { + "username": "test_user", + "password": "****", + "subdomain": "test" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json new file mode 100644 index 000000000..01557ca11 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json @@ -0,0 +1,40 @@ +{ + "includes": [ "test_service2.json" ], + "operations": { + "test": { + "uri": "/path" + }, + "concrete": { + "extends": "abstract" + }, + "foo_bar": { + "uri": "/testing", + "parameters": { + "other": { + "location": "json", + "location_key": "Other" + }, + "test": { + "type": "object", + "location": "json", + "properties": { + "baz": { + "type": "boolean", + "default": true + }, + "bar": { + "type": "string", + "filters": [ + { + "method": "strtolower", + "args": ["test", "@value"] + }, + "strtoupper" + ] + } + } + } + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json new file mode 100644 index 000000000..66dd9ef7e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json @@ -0,0 +1,7 @@ +{ + "operations": { + "abstract": { + "uri": "/abstract" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json new file mode 100644 index 000000000..ae2ae0bd4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json @@ -0,0 +1,40 @@ +{ + "includes": [ "test_service2.json" ], + "operations": { + "test": { + "uri": "/path" + }, + "concrete": { + "extends": "abstract" + }, + "baz_qux": { + "uri": "/testing", + "parameters": { + "other": { + "location": "json", + "location_key": "Other" + }, + "test": { + "type": "object", + "location": "json", + "properties": { + "baz": { + "type": "boolean", + "default": true + }, + "bar": { + "type": "string", + "filters": [ + { + "method": "strtolower", + "args": ["test", "@value"] + }, + "strtoupper" + ] + } + } + } + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/bootstrap.php b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/bootstrap.php new file mode 100644 index 000000000..28908d310 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/guzzle/guzzle/tests/bootstrap.php @@ -0,0 +1,10 @@ += 5.3.2 with [cURL](http://php.net/manual/en/book.curl.php) extension, +* [Guzzle](https://github.com/guzzle/guzzle) library, +* (optional) PHPUnit to run tests. + +## Autoload + +The new version of `php-github-api` using [Composer](http://getcomposer.org). +The first step to use `php-github-api` is to download composer: + +```bash +$ curl -s http://getcomposer.org/installer | php +``` + +Then we have to install our dependencies using: +```bash +$ php composer.phar install +``` +Now we can use autoloader from Composer by: + +```json +{ + "require": { + "knplabs/github-api": "~1.2" + } +} +``` + +> `php-github-api` follows the PSR-0 convention names for its classes, which means you can easily integrate `php-github-api` classes loading in your own autoloader. + +## Using Laravel? + +[Laravel GitHub](https://github.com/GrahamCampbell/Laravel-GitHub) by [Graham Campbell](https://github.com/GrahamCampbell) might interest you. + +## Basic usage of `php-github-api` client + +```php +api('user')->repositories('ornicar'); +``` + +From `$client` object, you can access to all GitHub. + +## Cache usage + +```php + '/tmp/github-api-cache')) +); + +// Or select directly which cache you want to use +$client = new \Github\HttpClient\CachedHttpClient(); +$client->setCache( + // Built in one, or any cache implementing this interface: + // Github\HttpClient\Cache\CacheInterface + new \Github\HttpClient\Cache\FilesystemCache('/tmp/github-api-cache') +); + +$client = new \Github\Client($client); +``` + +Using cache, the client will get cached responses if resources haven't changed since last time, +**without** reaching the `X-Rate-Limit` [imposed by github](http://developer.github.com/v3/#rate-limiting). + + +## Documentation + +See the [`doc` directory](doc/) for more detailed documentation. + +## License + +`php-github-api` is licensed under the MIT License - see the LICENSE file for details + +## Credits + +### Sponsored by + +[![KnpLabs Team](http://knplabs.com/front/images/knp-labs-logo.png)](http://knplabs.com) + +### Contributors + +- Thanks to [Thibault Duplessis aka. ornicar](http://github.com/ornicar) for his first version of this library. +- Thanks to [Joseph Bielawski aka. stloyd](http://github.com/stloyd) for his contributions and support. +- Thanks to [noloh](http://github.com/noloh) for his contribution on the Object API. +- Thanks to [bshaffer](http://github.com/bshaffer) for his contribution on the Repo API. +- Thanks to [Rolf van de Krol](http://github.com/rolfvandekrol) for his countless contributions. +- Thanks to [Nicolas Pastorino](http://github.com/jeanvoye) for his contribution on the Pull Request API. +- Thanks to [Edoardo Rivello](http://github.com/erivello) for his contribution on the Gists API. + +Thanks to GitHub for the high quality API and documentation. diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/composer.json b/modules/devshop/devshop_github/vendor/knplabs/github-api/composer.json new file mode 100644 index 000000000..dc16d3e77 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/composer.json @@ -0,0 +1,38 @@ +{ + "name": "knplabs/github-api", + "type": "library", + "description": "GitHub API v3 client", + "homepage": "https://github.com/KnpLabs/php-github-api", + "keywords": ["github", "gh", "api", "gist"], + "license": "MIT", + "authors": [ + { + "name": "KnpLabs Team", + "homepage": "http://knplabs.com" + }, + { + "name": "Thibault Duplessis", + "email": "thibault.duplessis@gmail.com", + "homepage": "http://ornicar.github.com" + } + ], + "require": { + "php": ">=5.3.2", + "ext-curl": "*", + "guzzle/guzzle": "~3.7" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "knplabs/gaufrette": "Needed for optional Gaufrette cache" + }, + "autoload": { + "psr-0": { "Github\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php new file mode 100644 index 000000000..e7209936e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php @@ -0,0 +1,204 @@ + + */ +abstract class AbstractApi implements ApiInterface +{ + /** + * The client. + * + * @var Client + */ + protected $client; + + /** + * Number of items per page (GitHub pagination). + * + * @var null|int + */ + protected $perPage; + + /** + * @param Client $client + */ + public function __construct(Client $client) + { + $this->client = $client; + } + + public function configure() + { + } + + /** + * @return null|int + */ + public function getPerPage() + { + return $this->perPage; + } + + /** + * @param null|int $perPage + */ + public function setPerPage($perPage) + { + $this->perPage = (null === $perPage ? $perPage : (int) $perPage); + + return $this; + } + + /** + * Send a GET request with query parameters. + * + * @param string $path Request path. + * @param array $parameters GET parameters. + * @param array $requestHeaders Request Headers. + * + * @return \Guzzle\Http\EntityBodyInterface|mixed|string + */ + protected function get($path, array $parameters = array(), $requestHeaders = array()) + { + if (null !== $this->perPage && !isset($parameters['per_page'])) { + $parameters['per_page'] = $this->perPage; + } + if (array_key_exists('ref', $parameters) && is_null($parameters['ref'])) { + unset($parameters['ref']); + } + $response = $this->client->getHttpClient()->get($path, $parameters, $requestHeaders); + + return ResponseMediator::getContent($response); + } + + /** + * Send a HEAD request with query parameters. + * + * @param string $path Request path. + * @param array $parameters HEAD parameters. + * @param array $requestHeaders Request headers. + * + * @return \Guzzle\Http\Message\Response + */ + protected function head($path, array $parameters = array(), $requestHeaders = array()) + { + if (array_key_exists('ref', $parameters) && is_null($parameters['ref'])) { + unset($parameters['ref']); + } + + $response = $this->client->getHttpClient()->request($path, null, 'HEAD', $requestHeaders, array( + 'query' => $parameters + )); + + return $response; + } + + /** + * Send a POST request with JSON-encoded parameters. + * + * @param string $path Request path. + * @param array $parameters POST parameters to be JSON encoded. + * @param array $requestHeaders Request headers. + */ + protected function post($path, array $parameters = array(), $requestHeaders = array()) + { + return $this->postRaw( + $path, + $this->createJsonBody($parameters), + $requestHeaders + ); + } + + /** + * Send a POST request with raw data. + * + * @param string $path Request path. + * @param $body Request body. + * @param array $requestHeaders Request headers. + * + * @return \Guzzle\Http\EntityBodyInterface|mixed|string + */ + protected function postRaw($path, $body, $requestHeaders = array()) + { + $response = $this->client->getHttpClient()->post( + $path, + $body, + $requestHeaders + ); + + return ResponseMediator::getContent($response); + } + + /** + * Send a PATCH request with JSON-encoded parameters. + * + * @param string $path Request path. + * @param array $parameters POST parameters to be JSON encoded. + * @param array $requestHeaders Request headers. + */ + protected function patch($path, array $parameters = array(), $requestHeaders = array()) + { + $response = $this->client->getHttpClient()->patch( + $path, + $this->createJsonBody($parameters), + $requestHeaders + ); + + return ResponseMediator::getContent($response); + } + + /** + * Send a PUT request with JSON-encoded parameters. + * + * @param string $path Request path. + * @param array $parameters POST parameters to be JSON encoded. + * @param array $requestHeaders Request headers. + */ + protected function put($path, array $parameters = array(), $requestHeaders = array()) + { + $response = $this->client->getHttpClient()->put( + $path, + $this->createJsonBody($parameters), + $requestHeaders + ); + + return ResponseMediator::getContent($response); + } + + /** + * Send a DELETE request with JSON-encoded parameters. + * + * @param string $path Request path. + * @param array $parameters POST parameters to be JSON encoded. + * @param array $requestHeaders Request headers. + */ + protected function delete($path, array $parameters = array(), $requestHeaders = array()) + { + $response = $this->client->getHttpClient()->delete( + $path, + $this->createJsonBody($parameters), + $requestHeaders + ); + + return ResponseMediator::getContent($response); + } + + /** + * Create a JSON encoded version of an array of parameters. + * + * @param array $parameters Request parameters + * + * @return null|string + */ + protected function createJsonBody(array $parameters) + { + return (count($parameters) === 0) ? null : json_encode($parameters, empty($parameters) ? JSON_FORCE_OBJECT : 0); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/ApiInterface.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/ApiInterface.php new file mode 100644 index 000000000..893f538e1 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/ApiInterface.php @@ -0,0 +1,16 @@ + + */ +interface ApiInterface +{ + + public function getPerPage(); + + public function setPerPage($perPage); +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Authorizations.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Authorizations.php new file mode 100644 index 000000000..014e08b44 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Authorizations.php @@ -0,0 +1,45 @@ +get('authorizations'); + } + + public function show($number) + { + return $this->get('authorizations/'.rawurlencode($number)); + } + + public function create(array $params, $OTPCode = null) + { + $headers = null === $OTPCode ? array() : array('X-GitHub-OTP' => $OTPCode); + + return $this->post('authorizations', $params, $headers); + } + + public function update($id, array $params) + { + return $this->patch('authorizations/'.rawurlencode($id), $params); + } + + public function remove($id) + { + return $this->delete('authorizations/'.rawurlencode($id)); + } + + public function check($id, $token) + { + return $this->get('authorizations/'.rawurlencode($id).'/tokens/'.rawurlencode($token)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser.php new file mode 100644 index 000000000..3ab571d47 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser.php @@ -0,0 +1,154 @@ + + * @author Felipe Valtl de Mello + */ +class CurrentUser extends AbstractApi +{ + public function show() + { + return $this->get('user'); + } + + public function update(array $params) + { + return $this->patch('user', $params); + } + + /** + * @return Emails + */ + public function emails() + { + return new Emails($this->client); + } + + /** + * @return Followers + */ + public function follow() + { + return new Followers($this->client); + } + + public function followers($page = 1) + { + return $this->get('user/followers', array( + 'page' => $page + )); + } + + /** + * @link http://developer.github.com/v3/issues/#list-issues + * + * @param array $params + * @param bool $includeOrgIssues + * + * @return array + */ + public function issues(array $params = array(), $includeOrgIssues = true) + { + return $this->get($includeOrgIssues ? 'issues' : 'user/issues', array_merge(array('page' => 1), $params)); + } + + /** + * @return DeployKeys + */ + public function keys() + { + return new DeployKeys($this->client); + } + + /** + * @return Notifications + */ + public function notifications() + { + return new Notifications($this->client); + } + + /** + * @link http://developer.github.com/v3/orgs/#list-user-organizations + * + * @return array + */ + public function organizations() + { + return $this->get('user/orgs'); + } + + /** + * @link https://developer.github.com/v3/orgs/teams/#list-user-teams + * + * @return array + */ + public function teams() + { + return $this->get('user/teams'); + } + + /** + * @link http://developer.github.com/v3/repos/#list-your-repositories + * + * @param string $type role in the repository + * @param string $sort sort by + * @param string $direction direction of sort, ask or desc + * + * @return array + */ + public function repositories($type = 'owner', $sort = 'full_name', $direction = 'asc') + { + return $this->get('user/repos', array( + $type, + $sort, + $direction + )); + } + + /** + * @return Watchers + */ + public function watchers() + { + return new Watchers($this->client); + } + + /** + * @deprecated Use watchers() instead + */ + public function watched($page = 1) + { + return $this->get('user/watched', array( + 'page' => $page + )); + } + + /** + * @return Starring + */ + public function starring() + { + return new Starring($this->client); + } + + /** + * @deprecated Use starring() instead + */ + public function starred($page = 1) + { + return $this->get('user/starred', array( + 'page' => $page + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/DeployKeys.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/DeployKeys.php new file mode 100644 index 000000000..d4f8b080d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/DeployKeys.php @@ -0,0 +1,94 @@ + + */ +class DeployKeys extends AbstractApi +{ + /** + * List deploy keys for the authenticated user. + * + * @link http://developer.github.com/v3/repos/keys/ + * + * @return array + */ + public function all() + { + return $this->get('user/keys'); + } + + /** + * Shows deploy key for the authenticated user. + * + * @link http://developer.github.com/v3/repos/keys/ + * + * @param string $id + * + * @return array + */ + public function show($id) + { + return $this->get('user/keys/'.rawurlencode($id)); + } + + /** + * Adds deploy key for the authenticated user. + * + * @link http://developer.github.com/v3/repos/keys/ + * + * @param array $params + * + * @throws MissingArgumentException + * + * @return array + */ + public function create(array $params) + { + if (!isset($params['title'], $params['key'])) { + throw new MissingArgumentException(array('title', 'key')); + } + + return $this->post('user/keys', $params); + } + + /** + * Updates deploy key for the authenticated user. + * + * @link http://developer.github.com/v3/repos/keys/ + * + * @param string $id + * @param array $params + * + * @throws MissingArgumentException + * + * @return array + */ + public function update($id, array $params) + { + if (!isset($params['title'], $params['key'])) { + throw new MissingArgumentException(array('title', 'key')); + } + + return $this->patch('user/keys/'.rawurlencode($id), $params); + } + + /** + * Removes deploy key for the authenticated user. + * + * @link http://developer.github.com/v3/repos/keys/ + * + * @param string $id + * + * @return array + */ + public function remove($id) + { + return $this->delete('user/keys/'.rawurlencode($id)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Emails.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Emails.php new file mode 100644 index 000000000..587aa12c7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Emails.php @@ -0,0 +1,69 @@ + + */ +class Emails extends AbstractApi +{ + /** + * List emails for the authenticated user. + * + * @link http://developer.github.com/v3/users/emails/ + * + * @return array + */ + public function all() + { + return $this->get('user/emails'); + } + + /** + * Adds one or more email for the authenticated user. + * + * @link http://developer.github.com/v3/users/emails/ + * + * @param string|array $emails + * + * @throws InvalidArgumentException + * + * @return array + */ + public function add($emails) + { + if (is_string($emails)) { + $emails = array($emails); + } elseif (0 === count($emails)) { + throw new InvalidArgumentException(); + } + + return $this->post('user/emails', $emails); + } + + /** + * Removes one or more email for the authenticated user. + * + * @link http://developer.github.com/v3/users/emails/ + * + * @param string|array $emails + * + * @throws InvalidArgumentException + * + * @return array + */ + public function remove($emails) + { + if (is_string($emails)) { + $emails = array($emails); + } elseif (0 === count($emails)) { + throw new InvalidArgumentException(); + } + + return $this->delete('user/emails', $emails); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Followers.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Followers.php new file mode 100644 index 000000000..303637740 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Followers.php @@ -0,0 +1,70 @@ + + */ +class Followers extends AbstractApi +{ + /** + * List followed users by the authenticated user. + * + * @link http://developer.github.com/v3/repos/followers/ + * + * @param int $page + * + * @return array + */ + public function all($page = 1) + { + return $this->get('user/following', array( + 'page' => $page + )); + } + + /** + * Check that the authenticated user follows a user. + * + * @link http://developer.github.com/v3/repos/followers/ + * + * @param string $username the username to follow + * + * @return array + */ + public function check($username) + { + return $this->get('user/following/'.rawurlencode($username)); + } + + /** + * Make the authenticated user follow a user. + * + * @link http://developer.github.com/v3/repos/followers/ + * + * @param string $username the username to follow + * + * @return array + */ + public function follow($username) + { + return $this->put('user/following/'.rawurlencode($username)); + } + + /** + * Make the authenticated user un-follow a user. + * + * @link http://developer.github.com/v3/repos/followers/ + * + * @param string $username the username to un-follow + * + * @return array + */ + public function unfollow($username) + { + return $this->delete('user/following/'.rawurlencode($username)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Notifications.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Notifications.php new file mode 100644 index 000000000..92dc0a570 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Notifications.php @@ -0,0 +1,130 @@ + + */ +class Notifications extends AbstractApi +{ + /** + * List all notifications for the authenticated user. + * + * @link http://developer.github.com/v3/activity/notifications/#list-your-notifications + * + * @param array $params + * + * @return array + */ + public function all(array $params = array()) + { + return $this->get('notifications', $params); + } + + /** + * List all notifications for the authenticated user in selected repository. + * + * @link http://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param array $params + * + * @return array + */ + public function allInRepository($username, $repository, array $params = array()) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/notifications', $params); + } + + /** + * @link http://developer.github.com/v3/activity/notifications/#mark-as-read + * + * @param array $params + * + * @return array + */ + public function markAsReadAll(array $params = array()) + { + return $this->put('notifications', $params); + } + + /** + * @link http://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param array $params + * + * @return array + */ + public function markAsReadInRepository($username, $repository, array $params = array()) + { + return $this->put('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/notifications', $params); + } + + /** + * @link http://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read + * + * @param string $id the notification number + * @param array $params + * + * @return array + */ + public function markAsRead($id, array $params) + { + return $this->patch('notifications/threads/'.rawurlencode($id), $params); + } + + /** + * @link http://developer.github.com/v3/activity/notifications/#view-a-single-thread + * + * @param string $id the notification number + * + * @return array + */ + public function show($id) + { + return $this->get('notifications/threads/'.rawurlencode($id)); + } + + /** + * @link http://developer.github.com/v3/activity/notifications/#get-a-thread-subscription + * + * @param string $id the notification number + * + * @return array + */ + public function showSubscription($id) + { + return $this->get('notifications/threads/'.rawurlencode($id).'/subscription'); + } + + /** + * @link http://developer.github.com/v3/activity/notifications/#set-a-thread-subscription + * + * @param string $id the notification number + * @param array $params + * + * @return array + */ + public function createSubscription($id, array $params) + { + return $this->put('notifications/threads/'.rawurlencode($id).'/subscription', $params); + } + + /** + * @link http://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription + * + * @param string $id the notification number + * + * @return array + */ + public function removeSubscription($id) + { + return $this->delete('notifications/threads/'.rawurlencode($id).'/subscription'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Starring.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Starring.php new file mode 100644 index 000000000..0e75da2c5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Starring.php @@ -0,0 +1,73 @@ + + */ +class Starring extends AbstractApi +{ + /** + * List repositories starred by the authenticated user. + * + * @link https://developer.github.com/v3/activity/starring/ + * + * @param int $page + * + * @return array + */ + public function all($page = 1) + { + return $this->get('user/starred', array( + 'page' => $page + )); + } + + /** + * Check that the authenticated user starres a repository. + * + * @link https://developer.github.com/v3/activity/starring/ + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function check($username, $repository) + { + return $this->get('user/starred/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + /** + * Make the authenticated user star a repository. + * + * @link https://developer.github.com/v3/activity/starring/ + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function star($username, $repository) + { + return $this->put('user/starred/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + /** + * Make the authenticated user unstar a repository. + * + * @link https://developer.github.com/v3/activity/starring + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function unstar($username, $repository) + { + return $this->delete('user/starred/'.rawurlencode($username).'/'.rawurlencode($repository)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Watchers.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Watchers.php new file mode 100644 index 000000000..7f7b55457 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/CurrentUser/Watchers.php @@ -0,0 +1,74 @@ + + * @revised Felipe Valtl de Mello + */ +class Watchers extends AbstractApi +{ + /** + * List repositories watched by the authenticated user. + * + * @link https://developer.github.com/v3/activity/watching/ + * + * @param int $page + * + * @return array + */ + public function all($page = 1) + { + return $this->get('user/subscriptions', array( + 'page' => $page + )); + } + + /** + * Check that the authenticated user watches a repository. + * + * @link https://developer.github.com/v3/activity/watching/ + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function check($username, $repository) + { + return $this->get('user/subscriptions/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + /** + * Make the authenticated user watch a repository. + * + * @link https://developer.github.com/v3/activity/watching/ + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function watch($username, $repository) + { + return $this->put('user/subscriptions/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + /** + * Make the authenticated user unwatch a repository. + * + * @link https://developer.github.com/v3/activity/watching/ + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function unwatch($username, $repository) + { + return $this->delete('user/subscriptions/'.rawurlencode($username).'/'.rawurlencode($repository)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Deployment.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Deployment.php new file mode 100644 index 000000000..f8f3dbac9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Deployment.php @@ -0,0 +1,84 @@ +get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments', $params); + } + + /** + * Create a new deployment for the given username and repo. + * @link https://developer.github.com/v3/repos/deployments/#create-a-deployment + * + * Important: Once a deployment is created, it cannot be updated. Changes are indicated by creating new statuses. + * @see updateStatus + * + * @param string $username the username + * @param string $repository the repository + * @param array $params the new deployment data + * @return array information about the deployment + * + * @throws MissingArgumentException + */ + public function create($username, $repository, array $params) + { + if (!isset($params['ref'])) { + throw new MissingArgumentException(array('ref')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments', $params); + } + + /** + * Updates a deployment by creating a new status update. + * @link https://developer.github.com/v3/repos/deployments/#create-a-deployment-status + * + * @param string $username the username + * @param string $repository the repository + * @param string $id the deployment number + * @param array $params The information about the deployment update. + * Must include a "state" field of pending, success, error, or failure. + * May also be given a target_url and description, ßee link for more details. + * @return array information about the deployment + * + * @throws MissingArgumentException + */ + public function updateStatus($username, $repository, $id, array $params) + { + if (!isset($params['state'])) { + throw new MissingArgumentException(array('state')); + } + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id).'/statuses', $params); + } + + /** + * Gets all of the status updates tied to a given deployment. + * + * @param string $username the username + * @param string $repository the repository + * @param int $id the deployment identifier + * @return array the deployment statuses + */ + public function getStatuses($username, $repository, $id) { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id).'/statuses'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise.php new file mode 100644 index 000000000..c23171a66 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise.php @@ -0,0 +1,50 @@ + + * @author Guillermo A. Fisher + */ +class Enterprise extends AbstractApi +{ + /** + * @return Stats + */ + public function stats() + { + return new Stats($this->client); + } + + /** + * @return License + */ + public function license() + { + return new License($this->client); + } + + /** + * @return ManagementConsole + */ + public function console() + { + return new ManagementConsole($this->client); + } + + /** + * @return UserAdmin + */ + public function userAdmin() + { + return new UserAdmin($this->client); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/License.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/License.php new file mode 100644 index 000000000..de14f23ea --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/License.php @@ -0,0 +1,20 @@ +get('enterprise/settings/license'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/ManagementConsole.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/ManagementConsole.php new file mode 100644 index 000000000..8554425f3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/ManagementConsole.php @@ -0,0 +1,77 @@ +getWithLicenseHash('/setup/api/configcheck', $hash); + } + + /** + * Retrieves your installation’s settings. + * + * @link https://developer.github.com/v3/enterprise/management_console/#retrieve-settings + * + * @param string $hash md5 hash of your license + * + * @return array array of settings + */ + public function settings($hash) + { + return $this->getWithLicenseHash('/setup/api/settings', $hash); + } + + /** + * Checks your installation’s maintenance status. + * + * @link https://developer.github.com/v3/enterprise/management_console/#check-maintenance-status + * + * @param string $hash md5 hash of your license + * + * @return array array of maintenance status information + */ + public function maintenance($hash) + { + return $this->getWithLicenseHash('/setup/api/maintenance', $hash); + } + + /** + * Retrieves your installation’s authorized SSH keys. + * + * @link https://developer.github.com/v3/enterprise/management_console/#retrieve-authorized-ssh-keys + * + * @param string $hash md5 hash of your license + * + * @return array array of authorized keys + */ + public function keys($hash) + { + return $this->getWithLicenseHash('/setup/api/settings/authorized-keys', $hash); + } + + /** + * Sends an authenticated GET request. + * + * @param string $uri the request URI + * @param string $hash md5 hash of your license + * + * @return \Guzzle\Http\EntityBodyInterface|mixed|string + */ + protected function getWithLicenseHash($uri, $hash) + { + return $this->get($uri, array('license_md5' => rawurlencode($hash))); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/Stats.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/Stats.php new file mode 100644 index 000000000..7af932945 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/Stats.php @@ -0,0 +1,128 @@ +show('issues'); + } + + /** + * Returns the number of active and inactive hooks. + * + * @return array array with totals of active and inactive hooks + */ + public function hooks() + { + return $this->show('hooks'); + } + + /** + * Returns the number of open and closed milestones. + * + * @return array array with totals of open and closed milestones + */ + public function milestones() + { + return $this->show('milestones'); + } + + /** + * Returns the number of organizations, teams, team members, and disabled organizations. + * + * @return array array with totals of organizations, teams, team members, and disabled organizations + */ + public function orgs() + { + return $this->show('orgs'); + } + + /** + * Returns the number of comments on issues, pull requests, commits, and gists. + * + * @return array array with totals of comments on issues, pull requests, commits, and gists + */ + public function comments() + { + return $this->show('comments'); + } + + /** + * Returns the number of GitHub Pages sites. + * + * @return array array with totals of GitHub Pages sites + */ + public function pages() + { + return $this->show('pages'); + } + + /** + * Returns the number of suspended and admin users. + * + * @return array array with totals of suspended and admin users + */ + public function users() + { + return $this->show('users'); + } + + /** + * Returns the number of private and public gists. + * + * @return array array with totals of private and public gists + */ + public function gists() + { + return $this->show('gists'); + } + + /** + * Returns the number of merged, mergeable, and unmergeable pull requests. + * + * @return array array with totals of merged, mergeable, and unmergeable pull requests + */ + public function pulls() + { + return $this->show('pulls'); + } + + /** + * Returns the number of organization-owned repositories, root repositories, forks, pushed commits, and wikis. + * + * @return array array with totals of organization-owned repositories, root repositories, forks, pushed commits, and wikis + */ + public function repos() + { + return $this->show('repos'); + } + + /** + * Returns all of the statistics. + * + * @return array array with all of the statistics + */ + public function all() + { + return $this->show('all'); + } + + /** + * @param string $type The type of statistics to show + * + * @return array + */ + public function show($type) + { + return $this->get('enterprise/stats/' . rawurlencode($type)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/UserAdmin.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/UserAdmin.php new file mode 100644 index 000000000..5a14114bc --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Enterprise/UserAdmin.php @@ -0,0 +1,36 @@ +put('users/'.rawurldecode($username).'/suspended', array('Content-Length' => 0)); + } + + /** + * Unsuspend a user. + * + * @link https://developer.github.com/v3/users/administration/#unsuspend-a-user + * + * @param string $username + * + * @return array + */ + public function unsuspend($username) + { + return $this->delete('users/'.rawurldecode($username).'/suspended'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gist/Comments.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gist/Comments.php new file mode 100644 index 000000000..2d8896500 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gist/Comments.php @@ -0,0 +1,37 @@ + + */ +class Comments extends AbstractApi +{ + public function all($gist) + { + return $this->get('gists/'.rawurlencode($gist).'/comments'); + } + + public function show($gist, $comment) + { + return $this->get('gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment)); + } + + public function create($gist, $body) + { + return $this->post('gists/'.rawurlencode($gist).'/comments', array($body)); + } + + public function update($gist, $comment_id, $body) + { + return $this->patch('gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment_id), array($body)); + } + + public function remove($gist, $comment) + { + return $this->delete('gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment)); + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gists.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gists.php new file mode 100644 index 000000000..eb24f0b3f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Gists.php @@ -0,0 +1,88 @@ + + * @author Edoardo Rivello + */ +class Gists extends AbstractApi +{ + public function all($type = null) + { + if (!in_array($type, array('public', 'starred'))) { + return $this->get('gists'); + } + + return $this->get('gists/'.rawurlencode($type)); + } + + public function show($number) + { + return $this->get('gists/'.rawurlencode($number)); + } + + public function create(array $params) + { + if (!isset($params['files']) || (!is_array($params['files']) || 0 === count($params['files']))) { + throw new MissingArgumentException('files'); + } + + $params['public'] = (bool) $params['public']; + + return $this->post('gists', $params); + } + + public function update($id, array $params) + { + return $this->patch('gists/'.rawurlencode($id), $params); + } + + public function commits($id) + { + return $this->get('gists/'.rawurlencode($id).'/commits'); + } + + public function fork($id) + { + return $this->post('gists/'.rawurlencode($id).'/fork'); + } + + public function remove($id) + { + return $this->delete('gists/'.rawurlencode($id)); + } + + public function check($id) + { + return $this->get('gists/'.rawurlencode($id).'/star'); + } + + public function star($id) + { + return $this->put('gists/'.rawurlencode($id).'/star'); + } + + public function unstar($id) + { + return $this->delete('gists/'.rawurlencode($id).'/star'); + } + + /** + * Get a gist's comments. + * + * @link http://developer.github.com/v3/gists/comments/ + * + * @return Comments + */ + public function comments() + { + return new Comments($this->client); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData.php new file mode 100644 index 000000000..21395a8ba --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData.php @@ -0,0 +1,58 @@ + + */ +class GitData extends AbstractApi +{ + /** + * @return Blobs + */ + public function blobs() + { + return new Blobs($this->client); + } + + /** + * @return Commits + */ + public function commits() + { + return new Commits($this->client); + } + + /** + * @return References + */ + public function references() + { + return new References($this->client); + } + + /** + * @return Tags + */ + public function tags() + { + return new Tags($this->client); + } + + /** + * @return Trees + */ + public function trees() + { + return new Trees($this->client); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Blobs.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Blobs.php new file mode 100644 index 000000000..2ff844db2 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Blobs.php @@ -0,0 +1,38 @@ + + */ +class Blobs extends AbstractApi +{ + public function configure($bodyType = null) + { + if ('raw' == $bodyType) { + $this->client->setHeaders(array( + 'Accept' => sprintf('application/vnd.github.%s.raw', $this->client->getOption('api_version')) + )); + } + } + + public function show($username, $repository, $sha) + { + $response = $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/blobs/'.rawurlencode($sha)); + + return $response; + } + + public function create($username, $repository, array $params) + { + if (!isset($params['content'], $params['encoding'])) { + throw new MissingArgumentException(array('content', 'encoding')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/blobs', $params); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Commits.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Commits.php new file mode 100644 index 000000000..1c161e1d7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Commits.php @@ -0,0 +1,27 @@ + + */ +class Commits extends AbstractApi +{ + public function show($username, $repository, $sha) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/commits/'.rawurlencode($sha)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['message'], $params['tree'], $params['parents'])) { + throw new MissingArgumentException(array('message', 'tree', 'parents')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/commits', $params); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/References.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/References.php new file mode 100644 index 000000000..70d8fa37a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/References.php @@ -0,0 +1,56 @@ + + */ +class References extends AbstractApi +{ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs'); + } + + public function branches($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/heads'); + } + + public function tags($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/tags'); + } + + public function show($username, $repository, $reference) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/'.rawurlencode($reference)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['ref'], $params['sha'])) { + throw new MissingArgumentException(array('ref', 'sha')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs', $params); + } + + public function update($username, $repository, $reference, array $params) + { + if (!isset($params['sha'])) { + throw new MissingArgumentException('sha'); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/'.rawurlencode($reference), $params); + } + + public function remove($username, $repository, $reference) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/'.rawurlencode($reference)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Tags.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Tags.php new file mode 100644 index 000000000..b27dddd12 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Tags.php @@ -0,0 +1,40 @@ + + */ +class Tags extends AbstractApi +{ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs/tags'); + } + + public function show($username, $repository, $sha) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/tags/'.rawurlencode($sha)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['tag'], $params['message'], $params['object'], $params['type'])) { + throw new MissingArgumentException(array('tag', 'message', 'object', 'type')); + } + + if (!isset($params['tagger'])) { + throw new MissingArgumentException('tagger'); + } + + if (!isset($params['tagger']['name'], $params['tagger']['email'], $params['tagger']['date'])) { + throw new MissingArgumentException(array('tagger.name', 'tagger.email', 'tagger.date')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/tags', $params); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Trees.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Trees.php new file mode 100644 index 000000000..92bd21ac4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/GitData/Trees.php @@ -0,0 +1,42 @@ + + */ +class Trees extends AbstractApi +{ + public function show($username, $repository, $sha, $recursive = false) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/trees/'.rawurlencode($sha), array('recursive' => $recursive ? 1 : null)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['tree']) || !is_array($params['tree'])) { + throw new MissingArgumentException('tree'); + } + + if (!isset($params['tree'][0])) { + $params['tree'] = array($params['tree']); + } + + foreach ($params['tree'] as $key => $tree) { + if (!isset($tree['path'], $tree['mode'], $tree['type'])) { + throw new MissingArgumentException(array("tree.$key.path", "tree.$key.mode", "tree.$key.type")); + } + + // If `sha` is not set, `content` is required + if (!isset($tree['sha']) && !isset($tree['content'])) { + throw new MissingArgumentException("tree.$key.content"); + } + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/trees', $params); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue.php new file mode 100644 index 000000000..c1b9a6e79 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue.php @@ -0,0 +1,182 @@ + + * @author Joseph Bielawski + */ +class Issue extends AbstractApi +{ + /** + * List issues by username, repo and state. + * + * @link http://developer.github.com/v3/issues/ + * + * @param string $username the username + * @param string $repository the repository + * @param array $params the additional parameters like milestone, assignees, labels, sort, direction + * + * @return array list of issues found + */ + public function all($username, $repository, array $params = array()) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues', array_merge(array('page' => 1), $params)); + } + + /** + * Search issues by username, repo, state and keyword. + * + * @link http://developer.github.com/v3/search/#search-issues + * + * @param string $username the username + * @param string $repository the repository + * @param string $state the issue state, can be open or closed + * @param string $keyword the keyword to filter issues by + * + * @return array list of issues found + */ + public function find($username, $repository, $state, $keyword) + { + if (!in_array($state, array('open', 'closed'))) { + $state = 'open'; + } + + return $this->get('legacy/issues/search/'.rawurlencode($username).'/'.rawurlencode($repository).'/'.rawurlencode($state).'/'.rawurlencode($keyword)); + } + + /** + * List issues by organization. + * + * @link http://developer.github.com/v3/issues/ + * + * @param string $organization the organization + * @param string $state the issue state, can be open or closed + * @param array $params the additional parameters like milestone, assignees, labels, sort, direction + * + * @return array list of issues found + */ + public function org($organization, $state, array $params = array()) + { + if (!in_array($state, array('open', 'closed'))) { + $state = 'open'; + } + + return $this->get('orgs/'.rawurlencode($organization).'/issues', array_merge(array('page' => 1, 'state' => $state), $params)); + } + + /** + * Get extended information about an issue by its username, repo and number. + * + * @link http://developer.github.com/v3/issues/ + * + * @param string $username the username + * @param string $repository the repository + * @param string $id the issue number + * + * @return array information about the issue + */ + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id)); + } + + /** + * Create a new issue for the given username and repo. + * The issue is assigned to the authenticated user. Requires authentication. + * + * @link http://developer.github.com/v3/issues/ + * + * @param string $username the username + * @param string $repository the repository + * @param array $params the new issue data + * + * @throws MissingArgumentException + * + * @return array information about the issue + */ + public function create($username, $repository, array $params) + { + if (!isset($params['title'], $params['body'])) { + throw new MissingArgumentException(array('title', 'body')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues', $params); + } + + /** + * Update issue information's by username, repo and issue number. Requires authentication. + * + * @link http://developer.github.com/v3/issues/ + * + * @param string $username the username + * @param string $repository the repository + * @param string $id the issue number + * @param array $params key=>value user attributes to update. + * key can be title or body + * + * @return array information about the issue + */ + public function update($username, $repository, $id, array $params) + { + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id), $params); + } + + /** + * List an issue comments. + * + * @link http://developer.github.com/v3/issues/comments/ + * + * @return Comments + */ + public function comments() + { + return new Comments($this->client); + } + + /** + * List all project events. + * + * @link http://developer.github.com/v3/issues/events/ + * + * @return Events + */ + public function events() + { + return new Events($this->client); + } + + /** + * List all project labels. + * + * @link http://developer.github.com/v3/issues/labels/ + * + * @return Labels + */ + public function labels() + { + return new Labels($this->client); + } + + /** + * List all project milestones. + * + * @link http://developer.github.com/v3/issues/milestones/ + * + * @return Milestones + */ + public function milestones() + { + return new Milestones($this->client); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Comments.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Comments.php new file mode 100644 index 000000000..ae6370fc6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Comments.php @@ -0,0 +1,70 @@ + + */ +class Comments extends AbstractApi +{ + public function configure($bodyType = null) + { + switch ($bodyType) { + case 'raw': + $header = sprintf('Accept: application/vnd.github.%s.raw+json', $this->client->getOption('api_version')); + break; + + case 'text': + $header = sprintf('Accept: application/vnd.github.%s.text+json', $this->client->getOption('api_version')); + break; + + case 'html': + $header = sprintf('Accept: application/vnd.github.%s.html+json', $this->client->getOption('api_version')); + break; + + default: + $header = sprintf('Accept: application/vnd.github.%s.full+json', $this->client->getOption('api_version')); + } + + $this->client->setHeaders(array($header)); + } + + public function all($username, $repository, $issue, $page = 1) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/comments', array( + 'page' => $page + )); + } + + public function show($username, $repository, $comment) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment)); + } + + public function create($username, $repository, $issue, array $params) + { + if (!isset($params['body'])) { + throw new MissingArgumentException('body'); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/comments', $params); + } + + public function update($username, $repository, $comment, array $params) + { + if (!isset($params['body'])) { + throw new MissingArgumentException('body'); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment), $params); + } + + public function remove($username, $repository, $comment) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Events.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Events.php new file mode 100644 index 000000000..17bcef839 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Events.php @@ -0,0 +1,30 @@ + + */ +class Events extends AbstractApi +{ + public function all($username, $repository, $issue = null, $page = 1) + { + if (null !== $issue) { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/events', array( + 'page' => $page + )); + } + + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/events', array( + 'page' => $page + )); + } + + public function show($username, $repository, $event) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/events/'.rawurlencode($event)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Labels.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Labels.php new file mode 100644 index 000000000..bdce14371 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Labels.php @@ -0,0 +1,76 @@ + + */ +class Labels extends AbstractApi +{ + public function all($username, $repository, $issue = null) + { + if ($issue === null) { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels'); + } + + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels'); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['name'])) { + throw new MissingArgumentException('name'); + } + if (!isset($params['color'])) { + $params['color'] = 'FFFFFF'; + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels', $params); + } + + public function deleteLabel($username, $repository, $label) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label)); + } + + public function update($username, $repository, $label, $newName, $color) + { + $params = array( + 'name' => $newName, + 'color' => $color, + ); + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label), $params); + } + + public function add($username, $repository, $issue, $labels) + { + if (is_string($labels)) { + $labels = array($labels); + } elseif (0 === count($labels)) { + throw new InvalidArgumentException(); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels', $labels); + } + + public function replace($username, $repository, $issue, array $params) + { + return $this->put('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels', $params); + } + + public function remove($username, $repository, $issue, $label) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels/'.rawurlencode($label)); + } + + public function clear($username, $repository, $issue) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Milestones.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Milestones.php new file mode 100644 index 000000000..a2e945a98 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Issue/Milestones.php @@ -0,0 +1,64 @@ + + */ +class Milestones extends AbstractApi +{ + public function all($username, $repository, array $params = array()) + { + if (isset($params['state']) && !in_array($params['state'], array('open', 'closed', 'all'))) { + $params['state'] = 'open'; + } + if (isset($params['sort']) && !in_array($params['sort'], array('due_date', 'completeness'))) { + $params['sort'] = 'due_date'; + } + if (isset($params['direction']) && !in_array($params['direction'], array('asc', 'desc'))) { + $params['direction'] = 'desc'; + } + + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones', array_merge(array('page' => 1, 'state' => 'open', 'sort' => 'due_date', 'direction' => 'desc'), $params)); + } + + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['title'])) { + throw new MissingArgumentException('title'); + } + if (isset($params['state']) && !in_array($params['state'], array('open', 'closed'))) { + $params['state'] = 'open'; + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones', $params); + } + + public function update($username, $repository, $milestone, array $params) + { + if (isset($params['state']) && !in_array($params['state'], array('open', 'closed'))) { + $params['state'] = 'open'; + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($milestone), $params); + } + + public function remove($username, $repository, $milestone) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($milestone)); + } + + public function labels($username, $repository, $milestone) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($milestone).'/labels'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Markdown.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Markdown.php new file mode 100644 index 000000000..f97a142df --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Markdown.php @@ -0,0 +1,48 @@ + + */ +class Markdown extends AbstractApi +{ + /** + * @param string $text + * @param string $mode + * @param string $context + * + * @return string + */ + public function render($text, $mode = 'markdown', $context = null) + { + if (!in_array($mode, array('gfm', 'markdown'))) { + $mode = 'markdown'; + } + + $params = array( + 'text' => $text, + 'mode' => $mode + ); + if (null !== $context && 'gfm' === $mode) { + $params['context'] = $context; + } + + return $this->post('markdown', $params); + } + + /** + * @param string $file + * + * @return string + */ + public function renderRaw($file) + { + return $this->post('markdown/raw', array( + 'file' => $file + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Meta.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Meta.php new file mode 100644 index 000000000..73a43301f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Meta.php @@ -0,0 +1,22 @@ + + */ +class Meta extends AbstractApi +{ + /** + * Get the ip address of the hook and git servers for the GitHub.com service. + * + * @return array Informations about the service of GitHub.com + */ + public function service() + { + return $this->get('meta'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Notification.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Notification.php new file mode 100644 index 000000000..c351927ec --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Notification.php @@ -0,0 +1,60 @@ + + */ +class Notification extends AbstractApi +{ + /** + * Get a listing of notifications. + * + * @link https://developer.github.com/v3/activity/notifications/ + * + * @param bool $includingRead + * @param bool $participating + * @param DateTime|null $since + * + * @return array array of notifications + */ + public function all($includingRead = false, $participating = false, DateTime $since = null) + { + $parameters = array( + 'all' => $includingRead, + 'participating' => $participating + ); + + if($since !== null) { + $parameters['since'] = $since->format(DateTime::ISO8601); + } + + return $this->get('notifications', $parameters); + } + + /** + * Marks all notifications as read from the current date + * Optionally give DateTime to mark as read before that date. + * + * @link https://developer.github.com/v3/activity/notifications/#mark-as-read + * + * @param DateTime|null $since + */ + public function markRead(DateTime $since = null) + { + $parameters = array(); + + if($since !== null) { + $parameters['last_read_at'] = $since->format(DateTime::ISO8601); + } + + $this->put('notifications', $parameters); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization.php new file mode 100644 index 000000000..37322b218 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization.php @@ -0,0 +1,68 @@ + + * @author Joseph Bielawski + */ +class Organization extends AbstractApi +{ + /** + * Get extended information about an organization by its name. + * + * @link http://developer.github.com/v3/orgs/#get + * + * @param string $organization the organization to show + * + * @return array informations about the organization + */ + public function show($organization) + { + return $this->get('orgs/'.rawurlencode($organization)); + } + + public function update($organization, array $params) + { + return $this->patch('orgs/'.rawurlencode($organization), $params); + } + + /** + * List all repositories across all the organizations that you can access. + * + * @link http://developer.github.com/v3/repos/#list-organization-repositories + * + * @param string $organization the user name + * @param string $type the type of repositories + * + * @return array the repositories + */ + public function repositories($organization, $type = 'all') + { + return $this->get('orgs/'.rawurlencode($organization).'/repos', array( + 'type' => $type + )); + } + + /** + * @return Members + */ + public function members() + { + return new Members($this->client); + } + + /** + * @return Teams + */ + public function teams() + { + return new Teams($this->client); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Members.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Members.php new file mode 100644 index 000000000..7358ed276 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Members.php @@ -0,0 +1,53 @@ + + */ +class Members extends AbstractApi +{ + public function all($organization, $type = null, $filter = 'all') + { + $parameters = array(); + $path = 'orgs/'.rawurlencode($organization).'/'; + if (null === $type) { + $path .= 'members'; + if (null !== $filter) { + $parameters['filter'] = $filter; + } + } else { + $path .= 'public_members'; + } + + return $this->get($path, $parameters); + } + + public function show($organization, $username) + { + return $this->get('orgs/'.rawurlencode($organization).'/members/'.rawurlencode($username)); + } + + public function check($organization, $username) + { + return $this->get('orgs/'.rawurlencode($organization).'/public_members/'.rawurlencode($username)); + } + + public function publicize($organization, $username) + { + return $this->put('orgs/'.rawurlencode($organization).'/public_members/'.rawurlencode($username)); + } + + public function conceal($organization, $username) + { + return $this->delete('orgs/'.rawurlencode($organization).'/public_members/'.rawurlencode($username)); + } + + public function remove($organization, $username) + { + return $this->delete('orgs/'.rawurlencode($organization).'/members/'.rawurlencode($username)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Teams.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Teams.php new file mode 100644 index 000000000..6acca32a0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Organization/Teams.php @@ -0,0 +1,95 @@ + + */ +class Teams extends AbstractApi +{ + public function all($organization) + { + return $this->get('orgs/'.rawurlencode($organization).'/teams'); + } + + public function create($organization, array $params) + { + if (!isset($params['name'])) { + throw new MissingArgumentException('name'); + } + if (isset($params['repo_names']) && !is_array($params['repo_names'])) { + $params['repo_names'] = array($params['repo_names']); + } + if (isset($params['permission']) && !in_array($params['permission'], array('pull', 'push', 'admin'))) { + $params['permission'] = 'pull'; + } + + return $this->post('orgs/'.rawurlencode($organization).'/teams', $params); + } + + public function show($team) + { + return $this->get('teams/'.rawurlencode($team)); + } + + public function update($team, array $params) + { + if (!isset($params['name'])) { + throw new MissingArgumentException('name'); + } + if (isset($params['permission']) && !in_array($params['permission'], array('pull', 'push', 'admin'))) { + $params['permission'] = 'pull'; + } + + return $this->patch('teams/'.rawurlencode($team), $params); + } + + public function remove($team) + { + return $this->delete('teams/'.rawurlencode($team)); + } + + public function members($team) + { + return $this->get('teams/'.rawurlencode($team).'/members'); + } + + public function check($team, $username) + { + return $this->get('teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); + } + + public function addMember($team, $username) + { + return $this->put('teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); + } + + public function removeMember($team, $username) + { + return $this->delete('teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); + } + + public function repositories($team) + { + return $this->get('teams/'.rawurlencode($team).'/repos'); + } + + public function repository($team, $username, $repository) + { + return $this->get('teams/'.rawurlencode($team).'/repos/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + public function addRepository($team, $username, $repository) + { + return $this->put('teams/'.rawurlencode($team).'/repos/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + public function removeRepository($team, $username, $repository) + { + return $this->delete('teams/'.rawurlencode($team).'/repos/'.rawurlencode($username).'/'.rawurlencode($repository)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest.php new file mode 100755 index 000000000..14db3bc7a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest.php @@ -0,0 +1,124 @@ + + */ +class PullRequest extends AbstractApi +{ + /** + * Get a listing of a project's pull requests by the username, repository and (optionally) state. + * + * @link http://developer.github.com/v3/pulls/ + * + * @param string $username the username + * @param string $repository the repository + * @param array $params a list of extra parameters. + * + * @return array array of pull requests for the project + */ + public function all($username, $repository, array $params = array()) + { + $parameters = array_merge(array( + 'page' => 1, + 'per_page' => 30 + ), $params); + + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls', $parameters); + } + + /** + * Show all details of a pull request, including the discussions. + * + * @link http://developer.github.com/v3/pulls/ + * + * @param string $username the username + * @param string $repository the repository + * @param string $id the ID of the pull request for which details are retrieved + * + * @return array array of pull requests for the project + */ + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id)); + } + + public function commits($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/commits'); + } + + public function files($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/files'); + } + + public function comments() + { + return new Comments($this->client); + } + + /** + * Create a pull request. + * + * @link http://developer.github.com/v3/pulls/ + * + * @param string $username the username + * @param string $repository the repository + * @param array $params A String of the branch or commit SHA that you want your changes to be pulled to. + * A String of the branch or commit SHA of your changes. Typically this will be a branch. + * If the branch is in a fork of the original repository, specify the username first: + * "my-user:some-branch". The String title of the Pull Request. The String body of + * the Pull Request. The issue number. Used when title and body is not set. + * + * @throws MissingArgumentException + * + * @return array + */ + public function create($username, $repository, array $params) + { + // Two ways to create PR, using issue or title + if (!isset($params['issue']) && !isset($params['title'])) { + throw new MissingArgumentException(array('issue', 'title')); + } + + if (!isset($params['base'], $params['head'])) { + throw new MissingArgumentException(array('base', 'head')); + } + + // If `issue` is not sent, then `body` must be sent + if (!isset($params['issue']) && !isset($params['body'])) { + throw new MissingArgumentException(array('issue', 'body')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls', $params); + } + + public function update($username, $repository, $id, array $params) + { + if (isset($params['state']) && !in_array($params['state'], array('open', 'closed'))) { + $params['state'] = 'open'; + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id), $params); + } + + public function merged($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/merge'); + } + + public function merge($username, $repository, $id, $message = '') + { + return $this->put('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/merge', array( + 'commit_message' => $message + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Comments.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Comments.php new file mode 100644 index 000000000..dec30fb05 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/PullRequest/Comments.php @@ -0,0 +1,51 @@ + + */ +class Comments extends AbstractApi +{ + public function all($username, $repository, $pullRequest) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/comments'); + } + + public function show($username, $repository, $comment) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment)); + } + + public function create($username, $repository, $pullRequest, array $params) + { + if (!isset($params['body'])) { + throw new MissingArgumentException('body'); + } + + // If `in_reply_to` is set, other options are not necessary anymore + if (!isset($params['in_reply_to']) && !isset($params['commit_id'], $params['path'], $params['position'])) { + throw new MissingArgumentException(array('commit_id', 'path', 'position')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/comments', $params); + } + + public function update($username, $repository, $comment, array $params) + { + if (!isset($params['body'])) { + throw new MissingArgumentException('body'); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment), $params); + } + + public function remove($username, $repository, $comment) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repo.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repo.php new file mode 100644 index 000000000..fcafd4b25 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repo.php @@ -0,0 +1,466 @@ + + * @author Thibault Duplessis + */ +class Repo extends AbstractApi +{ + /** + * Search repositories by keyword. + * + * @link http://developer.github.com/v3/search/#search-repositories + * + * @param string $keyword the search query + * @param array $params + * + * @return array list of found repositories + */ + public function find($keyword, array $params = array()) + { + return $this->get('legacy/repos/search/'.rawurlencode($keyword), array_merge(array('start_page' => 1), $params)); + } + + /** + * Get the last year of commit activity for a repository grouped by week. + * + * @link http://developer.github.com/v3/repos/statistics/#commit-activity + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * + * @return array commit activity grouped by week + */ + public function activity($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/stats/commit_activity'); + } + + /** + * Get contributor commit statistics for a repository. + * + * @link http://developer.github.com/v3/repos/statistics/#contributors + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * + * @return array list of contributors and their commit statistics + */ + public function statistics($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/stats/contributors'); + } + + /** + * List all repositories for an organization. + * + * @link http://developer.github.com/v3/repos/#list-organization-repositories + * + * @param string $organization the name of the organization + * @param array $params + * + * @return array list of organization repositories + */ + public function org($organization, array $params = array()) + { + return $this->get('orgs/'.$organization.'/repos', array_merge(array('start_page' => 1), $params)); + } + + /** + * Get extended information about a repository by its username and repository name. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * + * @return array informations about the repository + */ + public function show($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + /** + * Create repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $name name of the repository + * @param string $description repository description + * @param string $homepage homepage url + * @param bool $public `true` for public, `false` for private + * @param null|string $organization username of organization if applicable + * @param bool $hasIssues `true` to enable issues for this repository, `false` to disable them + * @param bool $hasWiki `true` to enable the wiki for this repository, `false` to disable it + * @param bool $hasDownloads `true` to enable downloads for this repository, `false` to disable them + * @param int $teamId The id of the team that will be granted access to this repository. This is only valid when creating a repo in an organization. + * @param bool $autoInit `true` to create an initial commit with empty README, `false` for no initial commit + * + * @return array returns repository data + */ + public function create( + $name, + $description = '', + $homepage = '', + $public = true, + $organization = null, + $hasIssues = false, + $hasWiki = false, + $hasDownloads = false, + $teamId = null, + $autoInit = false + ) { + $path = null !== $organization ? 'orgs/'.$organization.'/repos' : 'user/repos'; + + $parameters = array( + 'name' => $name, + 'description' => $description, + 'homepage' => $homepage, + 'private' => !$public, + 'has_issues' => $hasIssues, + 'has_wiki' => $hasWiki, + 'has_downloads' => $hasDownloads, + 'auto_init' => $autoInit + ); + + if ($organization && $teamId) { + $parameters['team_id'] = $teamId; + } + + return $this->post($path, $parameters); + } + + /** + * Set information of a repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param array $values the key => value pairs to post + * + * @return array informations about the repository + */ + public function update($username, $repository, array $values) + { + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository), $values); + } + + /** + * Delete a repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * + * @return mixed null on success, array on error with 'message' + */ + public function remove($username, $repository) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository)); + } + + /** + * Get the readme content for a repository by its username and repository name. + * + * @link http://developer.github.com/v3/repos/contents/#get-the-readme + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * + * @return array the readme content + */ + public function readme($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/readme'); + } + + /** + * Manage the collaborators of a repository. + * + * @link http://developer.github.com/v3/repos/collaborators/ + * + * @return Collaborators + */ + public function collaborators() + { + return new Collaborators($this->client); + } + + /** + * Manage the comments of a repository. + * + * @link http://developer.github.com/v3/repos/comments/ + * + * @return Comments + */ + public function comments() + { + return new Comments($this->client); + } + + /** + * Manage the commits of a repository. + * + * @link http://developer.github.com/v3/repos/commits/ + * + * @return Commits + */ + public function commits() + { + return new Commits($this->client); + } + + /** + * Manage the content of a repository. + * + * @link http://developer.github.com/v3/repos/contents/ + * + * @return Contents + */ + public function contents() + { + return new Contents($this->client); + } + + /** + * Manage the content of a repository. + * + * @link http://developer.github.com/v3/repos/downloads/ + * + * @return Downloads + */ + public function downloads() + { + return new Downloads($this->client); + } + + /** + * Manage the releases of a repository (Currently Undocumented). + * + * @link http://developer.github.com/v3/repos/ + * + * @return Releases + */ + public function releases() + { + return new Releases($this->client); + } + + /** + * Manage the deploy keys of a repository. + * + * @link http://developer.github.com/v3/repos/keys/ + * + * @return DeployKeys + */ + public function keys() + { + return new DeployKeys($this->client); + } + + /** + * Manage the forks of a repository. + * + * @link http://developer.github.com/v3/repos/forks/ + * + * @return Forks + */ + public function forks() + { + return new Forks($this->client); + } + + /** + * Manage the hooks of a repository. + * + * @link http://developer.github.com/v3/issues/jooks/ + * + * @return Hooks + */ + public function hooks() + { + return new Hooks($this->client); + } + + /** + * Manage the labels of a repository. + * + * @link http://developer.github.com/v3/issues/labels/ + * + * @return Labels + */ + public function labels() + { + return new Labels($this->client); + } + + /** + * Manage the statuses of a repository. + * + * @link http://developer.github.com/v3/repos/statuses/ + * + * @return Statuses + */ + public function statuses() + { + return new Statuses($this->client); + } + + /** + * Get the branch(es) of a repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the username + * @param string $repository the name of the repository + * @param string $branch the name of the branch + * + * @return array list of the repository branches + */ + public function branches($username, $repository, $branch = null) + { + $url = 'repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches'; + if (null !== $branch) { + $url .= '/'.rawurlencode($branch); + } + + return $this->get($url); + } + + /** + * Get the contributors of a repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param bool $includingAnonymous by default, the list only shows GitHub users. + * You can include non-users too by setting this to true + * + * @return array list of the repo contributors + */ + public function contributors($username, $repository, $includingAnonymous = false) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contributors', array( + 'anon' => $includingAnonymous ?: null + )); + } + + /** + * Get the language breakdown of a repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * + * @return array list of the languages + */ + public function languages($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/languages'); + } + + /** + * Get the tags of a repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * + * @return array list of the repository tags + */ + public function tags($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/tags'); + } + + /** + * Get the teams of a repository. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array list of the languages + */ + public function teams($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/teams'); + } + + /** + * @deprecated see subscribers method + * + * @param string $username + * @param string $repository + * @param int $page + * + * @return array + */ + public function watchers($username, $repository, $page = 1) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/watchers', array( + 'page' => $page + )); + } + + /** + * @param string $username + * @param string $repository + * @param int $page + * + * @return array + */ + public function subscribers($username, $repository, $page = 1) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/subscribers', array( + 'page' => $page + )); + } + + /** + * Perform a merge. + * + * @link http://developer.github.com/v3/repos/merging/ + * + * @param string $username + * @param string $repository + * @param string $base The name of the base branch that the head will be merged into. + * @param string $head The head to merge. This can be a branch name or a commit SHA1. + * @param string $message Commit message to use for the merge commit. If omitted, a default message will be used. + * + * @return array|null + */ + public function merge($username, $repository, $base, $head, $message = null) + { + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/merges', array( + 'base' => $base, + 'head' => $head, + 'commit_message' => $message + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Assets.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Assets.php new file mode 100644 index 000000000..dbd448c7e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Assets.php @@ -0,0 +1,124 @@ + + */ +class Assets extends AbstractApi +{ + /** + * Get all release's assets in selected repository + * GET /repos/:owner/:repo/releases/:id/assets. + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the release + * + * @return array + */ + public function all($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id).'/assets'); + } + + /** + * Get an asset in selected repository's release + * GET /repos/:owner/:repo/releases/assets/:id. + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the asset + * + * @return array + */ + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id)); + } + + /** + * Create an asset for selected repository's release + * POST /repos/:owner/:repo/releases/:id/assets?name=:filename. + * + * Creating an asset requires support for server name indentification (SNI) + * so this must be supported by your PHP version. + * + * @see http://developer.github.com/v3/repos/releases/#upload-a-release-asset + * @see http://php.net/manual/en/openssl.constsni.php + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the release + * @param string $name the filename for the asset + * @param string $contentType the content type for the asset + * @param string $content the content of the asset + * + * @throws MissingArgumentException + * @throws ErrorException + * + * @return array + */ + public function create($username, $repository, $id, $name, $contentType, $content) + { + if (!defined('OPENSSL_TLSEXT_SERVER_NAME') || !OPENSSL_TLSEXT_SERVER_NAME) { + throw new ErrorException('Asset upload support requires Server Name Indication. This is not supported by your PHP version. See http://php.net/manual/en/openssl.constsni.php.'); + } + + // Asset creation requires a separate endpoint, uploads.github.com. + // Change the base url for the HTTP client temporarily while we execute + // this request. + $baseUrl = $this->client->getHttpClient()->client->getBaseUrl(); + $this->client->getHttpClient()->client->setBaseUrl('https://uploads.github.com/'); + + $response = $this->postRaw('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id).'/assets?name='.$name, $content, array('Content-Type' => $contentType)); + + // Reset the base url. + $this->client->getHttpClient()->client->setBaseUrl($baseUrl); + + return $response; + } + + /** + * Edit an asset in selected repository's release + * PATCH /repos/:owner/:repo/releases/assets/:id. + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the asset + * @param array $params request parameters + * + * @throws MissingArgumentException + * + * @return array + */ + public function edit($username, $repository, $id, array $params) + { + if (!isset($params['name'])) { + throw new MissingArgumentException('name'); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id), $params); + } + + /** + * Delete an asset in selected repository's release + * DELETE /repos/:owner/:repo/releases/assets/:id. + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the asset + * + * @return array + */ + public function remove($username, $repository, $id) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Collaborators.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Collaborators.php new file mode 100644 index 000000000..89a950685 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Collaborators.php @@ -0,0 +1,32 @@ + + */ +class Collaborators extends AbstractApi +{ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators'); + } + + public function check($username, $repository, $collaborator) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators/'.rawurlencode($collaborator)); + } + + public function add($username, $repository, $collaborator) + { + return $this->put('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators/'.rawurlencode($collaborator)); + } + + public function remove($username, $repository, $collaborator) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/collaborators/'.rawurlencode($collaborator)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Comments.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Comments.php new file mode 100644 index 000000000..cce0e5059 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Comments.php @@ -0,0 +1,72 @@ + + */ +class Comments extends AbstractApi +{ + public function configure($bodyType = null) + { + switch ($bodyType) { + case 'raw': + $header = sprintf('Accept: application/vnd.github.%s.raw+json', $this->client->getOption('api_version')); + break; + + case 'text': + $header = sprintf('Accept: application/vnd.github.%s.text+json', $this->client->getOption('api_version')); + break; + + case 'html': + $header = sprintf('Accept: application/vnd.github.%s.html+json', $this->client->getOption('api_version')); + break; + + default: + $header = sprintf('Accept: application/vnd.github.%s.full+json', $this->client->getOption('api_version')); + } + + $this->client->setHeaders(array($header)); + } + + public function all($username, $repository, $sha = null) + { + if (null === $sha) { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments'); + } + + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/comments'); + } + + public function show($username, $repository, $comment) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments/'.rawurlencode($comment)); + } + + public function create($username, $repository, $sha, array $params) + { + if (!isset($params['body'])) { + throw new MissingArgumentException('body'); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/comments', $params); + } + + public function update($username, $repository, $comment, array $params) + { + if (!isset($params['body'])) { + throw new MissingArgumentException('body'); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments/'.rawurlencode($comment), $params); + } + + public function remove($username, $repository, $comment) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/comments/'.rawurlencode($comment)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Commits.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Commits.php new file mode 100644 index 000000000..909849868 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Commits.php @@ -0,0 +1,31 @@ + + */ +class Commits extends AbstractApi +{ + public function all($username, $repository, array $params) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits', $params); + } + + public function compare($username, $repository, $base, $head, $mediaType = null) + { + $headers = array(); + if (null !== $mediaType) { + $headers['Accept'] = $mediaType; + } + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/compare/'.rawurlencode($base).'...'.rawurlencode($head), array(), $headers); + } + + public function show($username, $repository, $sha) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Contents.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Contents.php new file mode 100644 index 000000000..c98898af6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Contents.php @@ -0,0 +1,274 @@ + + */ +class Contents extends AbstractApi +{ + /** + * Get content of README file in a repository. + * + * @link http://developer.github.com/v3/repos/contents/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param null|string $reference reference to a branch or commit + * + * @return array information for README file + */ + public function readme($username, $repository, $reference = null) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/readme', array( + 'ref' => $reference + )); + } + + /** + * Get contents of any file or directory in a repository. + * + * @link http://developer.github.com/v3/repos/contents/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param null|string $path path to file or directory + * @param null|string $reference reference to a branch or commit + * + * @return array information for file | information for each item in directory + */ + public function show($username, $repository, $path = null, $reference = null) + { + $url = 'repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents'; + if (null !== $path) { + $url .= '/'.rawurlencode($path); + } + + return $this->get($url, array( + 'ref' => $reference + )); + } + + /** + * Creates a new file in a repository. + * + * @link http://developer.github.com/v3/repos/contents/#create-a-file + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $path path to file + * @param string $content contents of the new file + * @param string $message the commit message + * @param null|string $branch name of a branch + * @param null|array $committer information about the committer + * + * @throws MissingArgumentException + * + * @return array information about the new file + */ + public function create($username, $repository, $path, $content, $message, $branch = null, array $committer = null) + { + $url = 'repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path); + + $parameters = array( + 'content' => base64_encode($content), + 'message' => $message, + ); + + if (null !== $branch) { + $parameters['branch'] = $branch; + } + + if (null !== $committer) { + if (!isset($committer['name'], $committer['email'])) { + throw new MissingArgumentException(array('name', 'email')); + } + $parameters['committer'] = $committer; + } + + return $this->put($url, $parameters); + } + + /** + * Checks that a given path exists in a repository. + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $path path of file to check + * @param null|string $reference reference to a branch or commit + * + * @return bool + */ + public function exists($username, $repository, $path, $reference = null) + { + $url = 'repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents'; + + if (null !== $path) { + $url .= '/'.rawurlencode($path); + } + + try { + $response = $this->head($url, array( + 'ref' => $reference + )); + + if ($response->getStatusCode() != 200) { + return false; + } + } catch (TwoFactorAuthenticationRequiredException $ex) { + throw $ex; + } catch (\Exception $ex) { + return false; + } + + return true; + } + + /** + * Updates the contents of a file in a repository. + * + * @link http://developer.github.com/v3/repos/contents/#update-a-file + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $path path to file + * @param string $content contents of the new file + * @param string $message the commit message + * @param string $sha blob SHA of the file being replaced + * @param null|string $branch name of a branch + * @param null|array $committer information about the committer + * + * @throws MissingArgumentException + * + * @return array information about the updated file + */ + public function update($username, $repository, $path, $content, $message, $sha, $branch = null, array $committer = null) + { + $url = 'repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path); + + $parameters = array( + 'content' => base64_encode($content), + 'message' => $message, + 'sha' => $sha, + ); + + if (null !== $branch) { + $parameters['branch'] = $branch; + } + + if (null !== $committer) { + if (!isset($committer['name'], $committer['email'])) { + throw new MissingArgumentException(array('name', 'email')); + } + $parameters['committer'] = $committer; + } + + return $this->put($url, $parameters); + } + + + /** + * Deletes a file from a repository. + * + * @link http://developer.github.com/v3/repos/contents/#delete-a-file + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $path path to file + * @param string $message the commit message + * @param string $sha blob SHA of the file being deleted + * @param null|string $branch name of a branch + * @param null|array $committer information about the committer + * + * @throws MissingArgumentException + * + * @return array information about the updated file + */ + public function rm($username, $repository, $path, $message, $sha, $branch = null, array $committer = null) + { + $url = 'repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path); + + $parameters = array( + 'message' => $message, + 'sha' => $sha, + ); + + if (null !== $branch) { + $parameters['branch'] = $branch; + } + + if (null !== $committer) { + if (!isset($committer['name'], $committer['email'])) { + throw new MissingArgumentException(array('name', 'email')); + } + $parameters['committer'] = $committer; + } + + return $this->delete($url, $parameters); + } + + /** + * Get content of archives in a repository. + * + * @link http://developer.github.com/v3/repos/contents/ + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $format format of archive: tarball or zipball + * @param null|string $reference reference to a branch or commit + * + * @return array information for archives + */ + public function archive($username, $repository, $format, $reference = null) + { + if (!in_array($format, array('tarball', 'zipball'))) { + $format = 'tarball'; + } + + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/'.rawurlencode($format). + ((null !== $reference) ? ('/'.rawurlencode($reference)) : '')); + } + + /** + * Get the contents of a file in a repository. + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $path path to file + * @param null|string $reference reference to a branch or commit + * + * @throws InvalidArgumentException If $path is not a file or if its encoding is different from base64 + * @throws ErrorException If $path doesn't include a 'content' index + * + * @return null|string content of file, or null in case of base64_decode failure + */ + public function download($username, $repository, $path, $reference = null) + { + $file = $this->show($username, $repository, $path, $reference); + + if (!isset($file['type']) || 'file' !== $file['type']) { + throw new InvalidArgumentException(sprintf('Path "%s" is not a file.', $path)); + } + + if (!isset($file['content'])) { + throw new ErrorException(sprintf('Unable to access "content" for file "%s" (possible keys: "%s").', $path, implode(', ', array_keys($file)))); + } + + if (!isset($file['encoding'])) { + throw new InvalidArgumentException(sprintf('Can\'t decode content of file "%s", as no encoding is defined.', $path)); + } + + if ('base64' !== $file['encoding']) { + throw new InvalidArgumentException(sprintf('Encoding "%s" of file "%s" is not supported.', $file['encoding'], $path)); + } + + return base64_decode($file['content']) ?: null; + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/DeployKeys.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/DeployKeys.php new file mode 100644 index 000000000..390835798 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/DeployKeys.php @@ -0,0 +1,46 @@ + + */ +class DeployKeys extends AbstractApi +{ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys'); + } + + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys/'.rawurlencode($id)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['title'], $params['key'])) { + throw new MissingArgumentException(array('title', 'key')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys', $params); + } + + public function update($username, $repository, $id, array $params) + { + if (!isset($params['title'], $params['key'])) { + throw new MissingArgumentException(array('title', 'key')); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys/'.rawurlencode($id), $params); + } + + public function remove($username, $repository, $id) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/keys/'.rawurlencode($id)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Downloads.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Downloads.php new file mode 100644 index 000000000..63177e499 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Downloads.php @@ -0,0 +1,59 @@ + + */ +class Downloads extends AbstractApi +{ + /** + * List downloads in selected repository. + * + * @link http://developer.github.com/v3/repos/downloads/#list-downloads-for-a-repository + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads'); + } + + /** + * Get a download in selected repository. + * + * @link http://developer.github.com/v3/repos/downloads/#get-a-single-download + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the download file + * + * @return array + */ + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.rawurlencode($id)); + } + + /** + * Delete a download in selected repository. + * + * @link http://developer.github.com/v3/repos/downloads/#delete-a-download + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the download file + * + * @return array + */ + public function remove($username, $repository, $id) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.rawurlencode($id)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Forks.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Forks.php new file mode 100644 index 000000000..c16596185 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Forks.php @@ -0,0 +1,26 @@ + + */ +class Forks extends AbstractApi +{ + public function all($username, $repository, array $params = array()) + { + if (isset($params['sort']) && !in_array($params['sort'], array('newest', 'oldest', 'watchers'))) { + $params['sort'] = 'newest'; + } + + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/forks', array_merge(array('page' => 1), $params)); + } + + public function create($username, $repository, array $params = array()) + { + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/forks', $params); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Hooks.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Hooks.php new file mode 100644 index 000000000..f4866ad0a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Hooks.php @@ -0,0 +1,51 @@ + + */ +class Hooks extends AbstractApi +{ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks'); + } + + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['name'], $params['config'])) { + throw new MissingArgumentException(array('name', 'config')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks', $params); + } + + public function update($username, $repository, $id, array $params) + { + if (!isset($params['name'], $params['config'])) { + throw new MissingArgumentException(array('name', 'config')); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id), $params); + } + + public function test($username, $repository, $id) + { + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id).'/test'); + } + + public function remove($username, $repository, $id) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Labels.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Labels.php new file mode 100644 index 000000000..e35b4ded7 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Labels.php @@ -0,0 +1,46 @@ + + */ +class Labels extends AbstractApi +{ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels'); + } + + public function show($username, $repository, $label) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label)); + } + + public function create($username, $repository, array $params) + { + if (!isset($params['name'], $params['color'])) { + throw new MissingArgumentException(array('name', 'color')); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels', $params); + } + + public function update($username, $repository, $label, array $params) + { + if (!isset($params['name'], $params['color'])) { + throw new MissingArgumentException(array('name', 'color')); + } + + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label), $params); + } + + public function remove($username, $repository, $label) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Releases.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Releases.php new file mode 100644 index 000000000..652c1b3a6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Releases.php @@ -0,0 +1,98 @@ + + * @author Evgeniy Guseletov + */ +class Releases extends AbstractApi +{ + /** + * List releases in selected repository. + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * + * @return array + */ + public function all($username, $repository) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases'); + } + + /** + * Get a release in selected repository. + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the release + * + * @return array + */ + public function show($username, $repository, $id) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id)); + } + + /** + * Create new release in selected repository. + * + * @param string $username + * @param string $repository + * @param array $params + * + * @throws MissingArgumentException + * + * @return array + */ + public function create($username, $repository, array $params) + { + if (!isset($params['tag_name'])) { + throw new MissingArgumentException('tag_name'); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases', $params); + } + + /** + * Edit release in selected repository. + * + * @param string $username + * @param string $repository + * @param int $id + * @param array $params + * + * @return array + */ + public function edit($username, $repository, $id, array $params) + { + return $this->patch('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id), $params); + } + + /** + * Delete a release in selected repository (Not thoroughly tested!). + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param int $id the id of the release + * + * @return array + */ + public function remove($username, $repository, $id) + { + return $this->delete('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id)); + } + + /** + * @return Assets + */ + public function assets() + { + return new Assets($this->client); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Statuses.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Statuses.php new file mode 100644 index 000000000..caab1e2b5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Repository/Statuses.php @@ -0,0 +1,62 @@ + + */ +class Statuses extends AbstractApi +{ + /** + * @link http://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-sha + * + * @param string $username + * @param string $repository + * @param string $sha + * + * @return array + */ + public function show($username, $repository, $sha) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/statuses'); + } + + /** + * @link https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref + * + * @param string $username + * @param string $repository + * @param string $sha + * + * @return array + */ + public function combined($username, $repository, $sha) + { + return $this->get('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/status'); + } + + /** + * @link http://developer.github.com/v3/repos/statuses/#create-a-status + * + * @param string $username + * @param string $repository + * @param string $sha + * @param array $params + * + * @throws MissingArgumentException + * + * @return array + */ + public function create($username, $repository, $sha, array $params = array()) + { + if (!isset($params['state'])) { + throw new MissingArgumentException('state'); + } + + return $this->post('repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/statuses/'.rawurlencode($sha), $params); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Search.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Search.php new file mode 100644 index 000000000..bd9f79110 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/Search.php @@ -0,0 +1,83 @@ + + */ +class Search extends AbstractApi +{ + + /** + * Search repositories by filter (q). + * + * @link https://developer.github.com/v3/search/#search-repositories + * + * @param string $q the filter + * @param string $sort the sort field + * @param string $order asc/desc + * + * @return array list of repositories found + */ + public function repositories($q, $sort = 'updated', $order = 'desc') + { + return $this->get('search/repositories', array('q' => $q, 'sort' => $sort, 'order' => $order)); + } + + /** + * Search issues by filter (q). + * + * @link https://developer.github.com/v3/search/#search-issues + * + * @param string $q the filter + * @param string $sort the sort field + * @param string $order asc/desc + * + * @return array list of issues found + */ + public function issues($q, $sort = 'updated', $order = 'desc') + { + return $this->get('search/issues', array('q' => $q, 'sort' => $sort, 'order' => $order)); + } + + /** + * Search code by filter (q). + * + * @link https://developer.github.com/v3/search/#search-code + * + * @param string $q the filter + * @param string $sort the sort field + * @param string $order asc/desc + * + * @return array list of code found + */ + public function code($q, $sort = 'updated', $order = 'desc') + { + return $this->get('search/code', array('q' => $q, 'sort' => $sort, 'order' => $order)); + } + + /** + * Search users by filter (q). + * + * @link https://developer.github.com/v3/search/#search-users + * + * @param string $q the filter + * @param string $sort the sort field + * @param string $order asc/desc + * + * @return array list of users found + */ + public function users($q, $sort = 'updated', $order = 'desc') + { + return $this->get('search/users', array('q' => $q, 'sort' => $sort, 'order' => $order)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/User.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/User.php new file mode 100644 index 000000000..0cc707b0b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Api/User.php @@ -0,0 +1,208 @@ + + * @author Thibault Duplessis + */ +class User extends AbstractApi +{ + /** + * Search users by username. + * + * @link http://developer.github.com/v3/search/#search-users + * + * @param string $keyword the keyword to search + * + * @return array list of users found + */ + public function find($keyword) + { + return $this->get('legacy/user/search/'.rawurlencode($keyword)); + } + + /** + * Request all users. + * + * @link https://developer.github.com/v3/users/#get-all-users + * + * @param int|null $id ID of the last user that you've seen + * + * @return array list of users found + */ + public function all($id = null) + { + if (!is_int($id)) { + return $this->get('users'); + } + return $this->get('users?since=' . rawurldecode($id)); + } + + /** + * Get extended information about a user by its username. + * + * @link http://developer.github.com/v3/users/ + * + * @param string $username the username to show + * + * @return array informations about the user + */ + public function show($username) + { + return $this->get('users/'.rawurlencode($username)); + } + + /** + * Get extended information about a user by its username. + * + * @link https://developer.github.com/v3/orgs/ + * + * @param string $username the username to show + * + * @return array information about organizations that user belongs to + */ + public function organizations($username) + { + return $this->get('users/'.rawurlencode($username).'/orgs'); + } + + /** + * Request the users that a specific user is following. + * + * @link http://developer.github.com/v3/users/followers/ + * + * @param string $username the username + * + * @return array list of followed users + */ + public function following($username) + { + return $this->get('users/'.rawurlencode($username).'/following'); + } + + /** + * Request the users following a specific user. + * + * @link http://developer.github.com/v3/users/followers/ + * + * @param string $username the username + * + * @return array list of following users + */ + public function followers($username) + { + return $this->get('users/'.rawurlencode($username).'/followers'); + } + + /** + * Request the repository that a specific user is watching. + * + * @deprecated see subscriptions method + * + * @param string $username the username + * + * @return array list of watched repositories + */ + public function watched($username) + { + return $this->get('users/'.rawurlencode($username).'/watched'); + } + + /** + * Request starred repositories that a specific user has starred. + * + * @link http://developer.github.com/v3/activity/starring/ + * + * @param string $username the username + * @param int $page the page number of the paginated result set + * + * @return array list of starred repositories + */ + public function starred($username, $page = 1) + { + return $this->get('users/'.rawurlencode($username).'/starred', array( + 'page' => $page + )); + } + + /** + * Request the repository that a specific user is watching. + * + * @link http://developer.github.com/v3/activity/watching/ + * + * @param string $username the username + * + * @return array list of watched repositories + */ + public function subscriptions($username) + { + return $this->get('users/'.rawurlencode($username).'/subscriptions'); + } + + /** + * Get the repositories of a user. + * + * @link http://developer.github.com/v3/repos/ + * + * @param string $username the username + * @param string $type role in the repository + * @param string $sort sort by + * @param string $direction direction of sort, asc or desc + * + * @return array list of the user repositories + */ + public function repositories($username, $type = 'owner', $sort = 'full_name', $direction = 'asc') + { + return $this->get('users/'.rawurlencode($username).'/repos', array( + 'type' => $type, + 'sort' => $sort, + 'direction' => $direction + )); + } + + /** + * Get the public gists for a user. + * + * @link http://developer.github.com/v3/gists/ + * + * @param string $username the username + * + * @return array list of the user gists + */ + public function gists($username) + { + return $this->get('users/'.rawurlencode($username).'/gists'); + } + + /** + * Get the public keys for a user. + * + * @link http://developer.github.com/v3/users/keys/#list-public-keys-for-a-user + * + * @param string $username the username + * + * @return array list of the user public keys + */ + public function keys($username) + { + return $this->get('users/'.rawurlencode($username).'/keys'); + } + + /** + * List events performed by a user. + * + * @link http://developer.github.com/v3/activity/events/#list-public-events-performed-by-a-user + * + * @param string $username + * + * @return array + */ + public function publicEvents($username) + { + return $this->get('users/'.rawurlencode($username) . '/events/public'); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Client.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Client.php new file mode 100644 index 000000000..8182bf18b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Client.php @@ -0,0 +1,343 @@ + + * + * Website: http://github.com/KnpLabs/php-github-api + */ +class Client +{ + /** + * Constant for authentication method. Indicates the default, but deprecated + * login with username and token in URL. + */ + const AUTH_URL_TOKEN = 'url_token'; + + /** + * Constant for authentication method. Not indicates the new login, but allows + * usage of unauthenticated rate limited requests for given client_id + client_secret. + */ + const AUTH_URL_CLIENT_ID = 'url_client_id'; + + /** + * Constant for authentication method. Indicates the new favored login method + * with username and password via HTTP Authentication. + */ + const AUTH_HTTP_PASSWORD = 'http_password'; + + /** + * Constant for authentication method. Indicates the new login method with + * with username and token via HTTP Authentication. + */ + const AUTH_HTTP_TOKEN = 'http_token'; + + /** + * @var array + */ + private $options = array( + 'base_url' => 'https://api.github.com/', + + 'user_agent' => 'php-github-api (http://github.com/KnpLabs/php-github-api)', + 'timeout' => 10, + + 'api_limit' => 5000, + 'api_version' => 'beta', + + 'cache_dir' => null + ); + + /** + * The Buzz instance used to communicate with GitHub. + * + * @var HttpClient + */ + private $httpClient; + + /** + * Instantiate a new GitHub client. + * + * @param null|HttpClientInterface $httpClient Github http client + */ + public function __construct(HttpClientInterface $httpClient = null) + { + $this->httpClient = $httpClient; + } + + /** + * @param string $name + * + * @throws InvalidArgumentException + * + * @return ApiInterface + */ + public function api($name) + { + switch ($name) { + case 'me': + case 'current_user': + case 'currentUser': + $api = new Api\CurrentUser($this); + break; + + case 'deployment': + case 'deployments': + $api = new Api\Deployment($this); + break; + + case 'ent': + case 'enterprise': + $api = new Api\Enterprise($this); + break; + + case 'git': + case 'git_data': + case 'gitData': + $api = new Api\GitData($this); + break; + + case 'gist': + case 'gists': + $api = new Api\Gists($this); + break; + + case 'issue': + case 'issues': + $api = new Api\Issue($this); + break; + + case 'markdown': + $api = new Api\Markdown($this); + break; + + case 'notification': + case 'notifications': + $api = new Api\Notification($this); + break; + + case 'organization': + case 'organizations': + $api = new Api\Organization($this); + break; + + case 'pr': + case 'pullRequest': + case 'pull_request': + case 'pullRequests': + case 'pull_requests': + $api = new Api\PullRequest($this); + break; + + case 'repo': + case 'repos': + case 'repository': + case 'repositories': + $api = new Api\Repo($this); + break; + + case 'search': + $api = new Api\Search($this); + break; + + case 'team': + case 'teams': + $api = new Api\Organization\Teams($this); + break; + + case 'user': + case 'users': + $api = new Api\User($this); + break; + + case 'authorization': + case 'authorizations': + $api = new Api\Authorizations($this); + break; + + case 'meta': + $api = new Api\Meta($this); + break; + + default: + throw new InvalidArgumentException(sprintf('Undefined api instance called: "%s"', $name)); + } + + return $api; + } + + /** + * Authenticate a user for all next requests. + * + * @param string $tokenOrLogin GitHub private token/username/client ID + * @param null|string $password GitHub password/secret (optionally can contain $authMethod) + * @param null|string $authMethod One of the AUTH_* class constants + * + * @throws InvalidArgumentException If no authentication method was given + */ + public function authenticate($tokenOrLogin, $password = null, $authMethod = null) + { + if (null === $password && null === $authMethod) { + throw new InvalidArgumentException('You need to specify authentication method!'); + } + + if (null === $authMethod && in_array($password, array(self::AUTH_URL_TOKEN, self::AUTH_URL_CLIENT_ID, self::AUTH_HTTP_PASSWORD, self::AUTH_HTTP_TOKEN))) { + $authMethod = $password; + $password = null; + } + + if (null === $authMethod) { + $authMethod = self::AUTH_HTTP_PASSWORD; + } + + $this->getHttpClient()->authenticate($tokenOrLogin, $password, $authMethod); + } + + /** + * Sets the URL of your GitHub Enterprise instance. + * + * @param string $enterpriseUrl URL of the API in the form of http(s)://hostname + */ + public function setEnterpriseUrl($enterpriseUrl) + { + $baseUrl = (substr($enterpriseUrl, -1) == '/') ? substr($enterpriseUrl, 0, -1) : $enterpriseUrl; + $this->getHttpClient()->client->setBaseUrl($baseUrl . '/api/v3'); + } + + /** + * @return HttpClient + */ + public function getHttpClient() + { + if (null === $this->httpClient) { + $this->httpClient = new HttpClient($this->options); + } + + return $this->httpClient; + } + + /** + * @param HttpClientInterface $httpClient + */ + public function setHttpClient(HttpClientInterface $httpClient) + { + $this->httpClient = $httpClient; + } + + /** + * Clears used headers. + */ + public function clearHeaders() + { + $this->getHttpClient()->clearHeaders(); + } + + /** + * @param array $headers + */ + public function setHeaders(array $headers) + { + $this->getHttpClient()->setHeaders($headers); + } + + /** + * @param string $name + * + * @throws InvalidArgumentException + * + * @return mixed + */ + public function getOption($name) + { + if (!array_key_exists($name, $this->options)) { + throw new InvalidArgumentException(sprintf('Undefined option called: "%s"', $name)); + } + + return $this->options[$name]; + } + + /** + * @param string $name + * @param mixed $value + * + * @throws InvalidArgumentException + * @throws InvalidArgumentException + */ + public function setOption($name, $value) + { + if (!array_key_exists($name, $this->options)) { + throw new InvalidArgumentException(sprintf('Undefined option called: "%s"', $name)); + } + $supportedApiVersions = $this->getSupportedApiVersions(); + if ('api_version' == $name && !in_array($value, $supportedApiVersions)) { + throw new InvalidArgumentException(sprintf('Invalid API version ("%s"), valid are: %s', $name, implode(', ', $supportedApiVersions))); + } + + $this->options[$name] = $value; + } + + /** + * Returns an array of valid API versions supported by this client. + * + * @return array + */ + public function getSupportedApiVersions() + { + return array('v3', 'beta'); + } + + /** + * @param string $name + * + * @throws InvalidArgumentException + * + * @return ApiInterface + */ + public function __call($name, $args) + { + try { + return $this->api($name); + } catch (InvalidArgumentException $e) { + throw new BadMethodCallException(sprintf('Undefined method called: "%s"', $name)); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ApiLimitExceedException.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ApiLimitExceedException.php new file mode 100644 index 000000000..539243a68 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ApiLimitExceedException.php @@ -0,0 +1,16 @@ + + */ +class ApiLimitExceedException extends RuntimeException +{ + public function __construct($limit = 5000, $code = 0, $previous = null) + { + parent::__construct('You have reached GitHub hour limit! Actual limit is: '. $limit, $code, $previous); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/BadMethodCallException.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/BadMethodCallException.php new file mode 100644 index 000000000..83e05437b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/BadMethodCallException.php @@ -0,0 +1,12 @@ + + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ErrorException.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ErrorException.php new file mode 100644 index 000000000..61f61f36f --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ErrorException.php @@ -0,0 +1,12 @@ + + */ +class ErrorException extends \ErrorException implements ExceptionInterface +{ +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ExceptionInterface.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ExceptionInterface.php new file mode 100644 index 000000000..cba560542 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ExceptionInterface.php @@ -0,0 +1,7 @@ + + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/MissingArgumentException.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/MissingArgumentException.php new file mode 100644 index 000000000..4a7e372e9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/MissingArgumentException.php @@ -0,0 +1,20 @@ + + */ +class MissingArgumentException extends ErrorException +{ + public function __construct($required, $code = 0, $previous = null) + { + if (is_string($required)) { + $required = array($required); + } + + parent::__construct(sprintf('One or more of required ("%s") parameters is missing!', implode('", "', $required)), $code, $previous); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/RuntimeException.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/RuntimeException.php new file mode 100644 index 000000000..676cb9573 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/RuntimeException.php @@ -0,0 +1,12 @@ + + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php new file mode 100644 index 000000000..6f93fe40b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php @@ -0,0 +1,19 @@ +type = $type; + parent::__construct('Two factor authentication is enabled on this account', $code, $previous); + } + + public function getType() + { + return $this->type; + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ValidationFailedException.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ValidationFailedException.php new file mode 100644 index 000000000..e43bc43e0 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/Exception/ValidationFailedException.php @@ -0,0 +1,12 @@ + + */ +class ValidationFailedException extends ErrorException +{ +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/CacheInterface.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/CacheInterface.php new file mode 100644 index 000000000..77bb4f89e --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/CacheInterface.php @@ -0,0 +1,51 @@ + + */ +interface CacheInterface +{ + /** + * @param string $id The id of the cached resource + * + * @return bool if present + */ + public function has($id); + + /** + * @param string $id The id of the cached resource + * + * @return null|int The modified since timestamp + */ + public function getModifiedSince($id); + + /** + * @param string $id The id of the cached resource + * + * @return null|string The ETag value + */ + public function getETag($id); + + /** + * @param string $id The id of the cached resource + * + * @throws \InvalidArgumentException If cache data don't exists + * + * @return Response The cached response object + */ + public function get($id); + + /** + * @param string $id The id of the cached resource + * @param Response $response The response to cache + * + * @throws \InvalidArgumentException If cache data cannot be saved + */ + public function set($id, Response $response); +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/FilesystemCache.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/FilesystemCache.php new file mode 100644 index 000000000..ff332b570 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/FilesystemCache.php @@ -0,0 +1,85 @@ +path = $path; + } + + /** + * {@inheritdoc} + */ + public function get($id) + { + if (false !== $content = @file_get_contents($this->getPath($id))) { + return unserialize($content); + } + + throw new \InvalidArgumentException(sprintf('File "%s" not found', $this->getPath($id))); + } + + /** + * {@inheritdoc} + */ + public function set($id, Response $response) + { + if (!is_dir($this->path)) { + @mkdir($this->path, 0777, true); + } + + if (false === @file_put_contents($this->getPath($id), serialize($response))) { + throw new \InvalidArgumentException(sprintf('Cannot put content in file "%s"', $this->getPath($id))); + } + if (false === @file_put_contents($this->getPath($id).'.etag', $response->getHeader('ETag'))) { + throw new \InvalidArgumentException(sprintf('Cannot put content in file "%s"', $this->getPath($id).'.etag')); + } + } + + /** + * {@inheritdoc} + */ + public function has($id) + { + return file_exists($this->getPath($id)); + } + + /** + * {@inheritdoc} + */ + public function getModifiedSince($id) + { + if ($this->has($id)) { + return filemtime($this->getPath($id)); + } + } + + public function getETag($id) + { + if (file_exists($this->getPath($id).'.etag')) { + return file_get_contents($this->getPath($id).'.etag'); + } + } + + /** + * @param $id string + * + * @return string + */ + protected function getPath($id) + { + return sprintf('%s%s%s', rtrim($this->path, DIRECTORY_SEPARATOR), DIRECTORY_SEPARATOR, md5($id)); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/GaufretteCache.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/GaufretteCache.php new file mode 100644 index 000000000..b72a104c9 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Cache/GaufretteCache.php @@ -0,0 +1,71 @@ + + */ +class GaufretteCache implements CacheInterface +{ + /** + * @var Filesystem + */ + protected $filesystem; + + /** + * @param Filesystem $filesystem + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + /** + * {@inheritdoc} + */ + public function get($id) + { + $content = $this->filesystem->read($id); + + return unserialize($content); + } + + /** + * {@inheritdoc} + */ + public function set($id, Response $response) + { + $this->filesystem->write($id, serialize($response), true); + $this->filesystem->write($id.'.etag', $response->getHeader('ETag'), true); + } + + /** + * {@inheritdoc} + */ + public function has($id) + { + $this->filesystem->has($id); + } + + /** + * {@inheritdoc} + */ + public function getModifiedSince($id) + { + if ($this->filesystem->has($id)) { + return $this->filesystem->mtime($id); + } + } + + public function getETag($id) + { + if ($this->filesystem->has($id)) { + return $this->filesystem->read($id.'.etag'); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/CachedHttpClient.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/CachedHttpClient.php new file mode 100644 index 000000000..f0e1368e6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/CachedHttpClient.php @@ -0,0 +1,108 @@ + + */ +class CachedHttpClient extends HttpClient +{ + /** + * @var CacheInterface + */ + protected $cache; + + /** + * Contains the lastResponse fetched from cache. + * + * @var Guzzle\Http\Message\Response + */ + private $lastCachedResponse; + + /** + * @return CacheInterface + */ + public function getCache() + { + if (null === $this->cache) { + $this->cache = new FilesystemCache($this->options['cache_dir'] ?: sys_get_temp_dir().DIRECTORY_SEPARATOR.'php-github-api-cache'); + } + + return $this->cache; + } + + /** + * @param $cache CacheInterface + */ + public function setCache(CacheInterface $cache) + { + $this->cache = $cache; + } + + /** + * {@inheritdoc} + */ + public function request($path, $body = null, $httpMethod = 'GET', array $headers = array(), array $options = array()) + { + $response = parent::request($path, $body, $httpMethod, $headers, $options); + + if (304 == $response->getStatusCode()) { + $cacheResponse = $this->getCache()->get($path); + $this->lastCachedResponse = $cacheResponse; + + return $cacheResponse; + } + + $this->getCache()->set($path, $response); + + return $response; + } + + /** + * Create requests with If-Modified-Since headers. + * + * {@inheritdoc} + */ + protected function createRequest($httpMethod, $path, $body = null, array $headers = array(), array $options = array()) + { + $request = parent::createRequest($httpMethod, $path, $body, $headers, $options); + + if ($modifiedAt = $this->getCache()->getModifiedSince($path)) { + $modifiedAt = new \DateTime('@'.$modifiedAt); + $modifiedAt->setTimezone(new \DateTimeZone('GMT')); + + $request->addHeader( + 'If-Modified-Since', + sprintf('%s GMT', $modifiedAt->format('l, d-M-y H:i:s')) + ); + } + if ($etag = $this->getCache()->getETag($path)) { + $request->addHeader( + 'If-None-Match', + $etag + ); + } + + return $request; + } + + /** + * @return Guzzle\Http\Message\Response + */ + public function getLastResponse($force = false) + { + $lastResponse = parent::getLastResponse(); + if (304 != $lastResponse->getStatusCode()) { + $force = true; + } + + return ($force) ? $lastResponse : $this->lastCachedResponse; + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClient.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClient.php new file mode 100644 index 000000000..84b2b09bc --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClient.php @@ -0,0 +1,190 @@ + + */ +class HttpClient implements HttpClientInterface +{ + protected $options = array( + 'base_url' => 'https://api.github.com/', + + 'user_agent' => 'php-github-api (http://github.com/KnpLabs/php-github-api)', + 'timeout' => 10, + + 'api_limit' => 5000, + 'api_version' => 'v3', + + 'cache_dir' => null + ); + + protected $headers = array(); + + private $lastResponse; + private $lastRequest; + + /** + * @param array $options + * @param ClientInterface $client + */ + public function __construct(array $options = array(), ClientInterface $client = null) + { + $this->options = array_merge($this->options, $options); + $client = $client ?: new GuzzleClient($this->options['base_url'], $this->options); + $this->client = $client; + + $this->addListener('request.error', array(new ErrorListener($this->options), 'onRequestError')); + $this->clearHeaders(); + } + + /** + * {@inheritDoc} + */ + public function setOption($name, $value) + { + $this->options[$name] = $value; + } + + /** + * {@inheritDoc} + */ + public function setHeaders(array $headers) + { + $this->headers = array_merge($this->headers, $headers); + } + + /** + * Clears used headers. + */ + public function clearHeaders() + { + $this->headers = array( + 'Accept' => sprintf('application/vnd.github.%s+json', $this->options['api_version']), + 'User-Agent' => sprintf('%s', $this->options['user_agent']), + ); + } + + public function addListener($eventName, $listener) + { + $this->client->getEventDispatcher()->addListener($eventName, $listener); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->client->addSubscriber($subscriber); + } + + /** + * {@inheritDoc} + */ + public function get($path, array $parameters = array(), array $headers = array()) + { + return $this->request($path, null, 'GET', $headers, array('query' => $parameters)); + } + + /** + * {@inheritDoc} + */ + public function post($path, $body = null, array $headers = array()) + { + return $this->request($path, $body, 'POST', $headers); + } + + /** + * {@inheritDoc} + */ + public function patch($path, $body = null, array $headers = array()) + { + return $this->request($path, $body, 'PATCH', $headers); + } + + /** + * {@inheritDoc} + */ + public function delete($path, $body = null, array $headers = array()) + { + return $this->request($path, $body, 'DELETE', $headers); + } + + /** + * {@inheritDoc} + */ + public function put($path, $body, array $headers = array()) + { + return $this->request($path, $body, 'PUT', $headers); + } + + /** + * {@inheritDoc} + */ + public function request($path, $body = null, $httpMethod = 'GET', array $headers = array(), array $options = array()) + { + $request = $this->createRequest($httpMethod, $path, $body, $headers, $options); + + try { + $response = $this->client->send($request); + } catch (\LogicException $e) { + throw new ErrorException($e->getMessage(), $e->getCode(), $e); + } catch (TwoFactorAuthenticationRequiredException $e) { + throw $e; + } catch (\RuntimeException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + + $this->lastRequest = $request; + $this->lastResponse = $response; + + return $response; + } + + /** + * {@inheritDoc} + */ + public function authenticate($tokenOrLogin, $password = null, $method) + { + $this->addListener('request.before_send', array( + new AuthListener($tokenOrLogin, $password, $method), 'onRequestBeforeSend' + )); + } + + /** + * @return Request + */ + public function getLastRequest() + { + return $this->lastRequest; + } + + /** + * @return Response + */ + public function getLastResponse() + { + return $this->lastResponse; + } + + protected function createRequest($httpMethod, $path, $body = null, array $headers = array(), array $options = array()) + { + return $this->client->createRequest( + $httpMethod, + $path, + array_merge($this->headers, $headers), + $body, + $options + ); + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClientInterface.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClientInterface.php new file mode 100644 index 000000000..5ed0a9e34 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/HttpClientInterface.php @@ -0,0 +1,112 @@ + + */ +interface HttpClientInterface +{ + /** + * Send a GET request. + * + * @param string $path Request path + * @param array $parameters GET Parameters + * @param array $headers Reconfigure the request headers for this call only + * + * @return Response + */ + public function get($path, array $parameters = array(), array $headers = array()); + + /** + * Send a POST request. + * + * @param string $path Request path + * @param mixed $body Request body + * @param array $headers Reconfigure the request headers for this call only + * + * @return Response + */ + public function post($path, $body = null, array $headers = array()); + + /** + * Send a PATCH request. + * + * @param string $path Request path + * @param mixed $body Request body + * @param array $headers Reconfigure the request headers for this call only + * + * @internal param array $parameters Request body + * + * @return Response + */ + public function patch($path, $body = null, array $headers = array()); + + /** + * Send a PUT request. + * + * @param string $path Request path + * @param mixed $body Request body + * @param array $headers Reconfigure the request headers for this call only + * + * @return Response + */ + public function put($path, $body, array $headers = array()); + + /** + * Send a DELETE request. + * + * @param string $path Request path + * @param mixed $body Request body + * @param array $headers Reconfigure the request headers for this call only + * + * @return Response + */ + public function delete($path, $body = null, array $headers = array()); + + /** + * Send a request to the server, receive a response, + * decode the response and returns an associative array. + * + * @param string $path Request path + * @param mixed $body Request body + * @param string $httpMethod HTTP method to use + * @param array $headers Request headers + * + * @return Response + */ + public function request($path, $body, $httpMethod = 'GET', array $headers = array()); + + /** + * Change an option value. + * + * @param string $name The option name + * @param mixed $value The value + * + * @throws InvalidArgumentException + */ + public function setOption($name, $value); + + /** + * Set HTTP headers. + * + * @param array $headers + */ + public function setHeaders(array $headers); + + /** + * Authenticate a user for all next requests. + * + * @param string $tokenOrLogin GitHub private token/username/client ID + * @param null|string $password GitHub password/secret (optionally can contain $authMethod) + * @param null|string $authMethod One of the AUTH_* class constants + * + * @throws InvalidArgumentException If no authentication method was given + */ + public function authenticate($tokenOrLogin, $password, $authMethod); +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/AuthListener.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/AuthListener.php new file mode 100644 index 000000000..41ad42e46 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/AuthListener.php @@ -0,0 +1,68 @@ +tokenOrLogin = $tokenOrLogin; + $this->password = $password; + $this->method = $method; + } + + public function onRequestBeforeSend(Event $event) + { + // Skip by default + if (null === $this->method) { + return; + } + + switch ($this->method) { + case Client::AUTH_HTTP_PASSWORD: + $event['request']->setHeader( + 'Authorization', + sprintf('Basic %s', base64_encode($this->tokenOrLogin . ':' . $this->password)) + ); + break; + + case Client::AUTH_HTTP_TOKEN: + $event['request']->setHeader('Authorization', sprintf('token %s', $this->tokenOrLogin)); + break; + + case Client::AUTH_URL_CLIENT_ID: + $url = $event['request']->getUrl(); + + $parameters = array( + 'client_id' => $this->tokenOrLogin, + 'client_secret' => $this->password, + ); + + $url .= (false === strpos($url, '?') ? '?' : '&'); + $url .= utf8_encode(http_build_query($parameters, '', '&')); + + $event['request']->setUrl($url); + break; + + case Client::AUTH_URL_TOKEN: + $url = $event['request']->getUrl(); + $url .= (false === strpos($url, '?') ? '?' : '&'); + $url .= utf8_encode(http_build_query(array('access_token' => $this->tokenOrLogin), '', '&')); + + $event['request']->setUrl($url); + break; + + default: + throw new RuntimeException(sprintf('%s not yet implemented', $this->method)); + break; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/ErrorListener.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/ErrorListener.php new file mode 100644 index 000000000..a945c45c6 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Listener/ErrorListener.php @@ -0,0 +1,94 @@ + + */ +class ErrorListener +{ + /** + * @var array + */ + private $options; + + /** + * @param array $options + */ + public function __construct(array $options) + { + $this->options = $options; + } + + /** + * {@inheritDoc} + */ + public function onRequestError(Event $event) + { + /** @var $request \Guzzle\Http\Message\Request */ + $request = $event['request']; + $response = $request->getResponse(); + + if ($response->isClientError() || $response->isServerError()) { + $remaining = (string) $response->getHeader('X-RateLimit-Remaining'); + + if (null != $remaining && 1 > $remaining && 'rate_limit' !== substr($request->getResource(), 1, 10)) { + throw new ApiLimitExceedException($this->options['api_limit']); + } + + if (401 === $response->getStatusCode()) { + if ($response->hasHeader('X-GitHub-OTP') && 0 === strpos((string) $response->getHeader('X-GitHub-OTP'), 'required;')) { + $type = substr((string) $response->getHeader('X-GitHub-OTP'), 9); + + throw new TwoFactorAuthenticationRequiredException($type); + } + } + + $content = ResponseMediator::getContent($response); + if (is_array($content) && isset($content['message'])) { + if (400 == $response->getStatusCode()) { + throw new ErrorException($content['message'], 400); + } elseif (422 == $response->getStatusCode() && isset($content['errors'])) { + $errors = array(); + foreach ($content['errors'] as $error) { + switch ($error['code']) { + case 'missing': + $errors[] = sprintf('The %s %s does not exist, for resource "%s"', $error['field'], $error['value'], $error['resource']); + break; + + case 'missing_field': + $errors[] = sprintf('Field "%s" is missing, for resource "%s"', $error['field'], $error['resource']); + break; + + case 'invalid': + $errors[] = sprintf('Field "%s" is invalid, for resource "%s"', $error['field'], $error['resource']); + break; + + case 'already_exists': + $errors[] = sprintf('Field "%s" already exists, for resource "%s"', $error['field'], $error['resource']); + break; + + default: + $errors[] = $error['message']; + break; + + } + } + + throw new ValidationFailedException('Validation Failed: ' . implode(', ', $errors), 422); + } + } + + throw new RuntimeException(isset($content['message']) ? $content['message'] : $content, $response->getStatusCode()); + }; + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Message/ResponseMediator.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Message/ResponseMediator.php new file mode 100644 index 000000000..72fe7c712 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/HttpClient/Message/ResponseMediator.php @@ -0,0 +1,50 @@ +getBody(true); + $content = json_decode($body, true); + + if (JSON_ERROR_NONE !== json_last_error()) { + return $body; + } + + return $content; + } + + public static function getPagination(Response $response) + { + $header = $response->getHeader('Link'); + + if (empty($header)) { + return null; + } + + $pagination = array(); + foreach (explode(',', $header) as $link) { + preg_match('/<(.*)>; rel="(.*)"/i', trim($link, ','), $match); + + if (3 === count($match)) { + $pagination[$match[2]] = $match[1]; + } + } + + return $pagination; + } + + public static function getApiLimit(Response $response) + { + $remainingCalls = $response->getHeader('X-RateLimit-Remaining'); + + if (null !== $remainingCalls && 1 > $remainingCalls) { + throw new ApiLimitExceedException($remainingCalls); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPager.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPager.php new file mode 100644 index 000000000..01c689a58 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPager.php @@ -0,0 +1,168 @@ + + * @author Mitchel Verschoof + */ +class ResultPager implements ResultPagerInterface +{ + /** + * The GitHub Client to use for pagination. + * + * @var \Github\Client + */ + protected $client; + + /** + * Comes from pagination headers in Github API results. + * + * @var array + */ + protected $pagination; + + /** + * The Github client to use for pagination. + * + * This must be the same instance that you got the Api instance from. + * + * Example code: + * + * $client = new \Github\Client(); + * $api = $client->api('someApi'); + * $pager = new \Github\ResultPager($client); + * + * @param \Github\Client $client + */ + public function __construct(Client $client) + { + $this->client = $client; + } + + /** + * {@inheritdoc} + */ + public function getPagination() + { + return $this->pagination; + } + + /** + * {@inheritdoc} + */ + public function fetch(ApiInterface $api, $method, array $parameters = array()) + { + $result = call_user_func_array(array($api, $method), $parameters); + $this->postFetch(); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function fetchAll(ApiInterface $api, $method, array $parameters = array()) + { + // get the perPage from the api + $perPage = $api->getPerPage(); + + // set parameters per_page to GitHub max to minimize number of requests + $api->setPerPage(100); + + $result = array(); + $result = call_user_func_array(array($api, $method), $parameters); + $this->postFetch(); + + while ($this->hasNext()) { + $result = array_merge($result, $this->fetchNext()); + } + + // restore the perPage + $api->setPerPage($perPage); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function postFetch() + { + $this->pagination = ResponseMediator::getPagination($this->client->getHttpClient()->getLastResponse()); + } + + /** + * {@inheritdoc} + */ + public function hasNext() + { + return $this->has('next'); + } + + /** + * {@inheritdoc} + */ + public function fetchNext() + { + return $this->get('next'); + } + + /** + * {@inheritdoc} + */ + public function hasPrevious() + { + return $this->has('prev'); + } + + /** + * {@inheritdoc} + */ + public function fetchPrevious() + { + return $this->get('prev'); + } + + /** + * {@inheritdoc} + */ + public function fetchFirst() + { + return $this->get('first'); + } + + /** + * {@inheritdoc} + */ + public function fetchLast() + { + return $this->get('last'); + } + + /** + * {@inheritdoc} + */ + protected function has($key) + { + return !empty($this->pagination) && isset($this->pagination[$key]); + } + + /** + * {@inheritdoc} + */ + protected function get($key) + { + if ($this->has($key)) { + $result = $this->client->getHttpClient()->get($this->pagination[$key]); + $this->postFetch(); + + return ResponseMediator::getContent($result); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPagerInterface.php b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPagerInterface.php new file mode 100644 index 000000000..1130e8ec4 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/knplabs/github-api/lib/Github/ResultPagerInterface.php @@ -0,0 +1,90 @@ + + * @author Mitchel Verschoof + */ +interface ResultPagerInterface +{ + /** + * @return null|array pagination result of last request + */ + public function getPagination(); + + /** + * Fetch a single result (page) from an api call. + * + * @param ApiInterface $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array + * + * @return array returns the result of the Api::$method() call + */ + public function fetch(ApiInterface $api, $method, array $parameters = array()); + + /** + * Fetch all results (pages) from an api call. + * + * Use with care - there is no maximum. + * + * @param ApiInterface $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array + * + * @return array returns a merge of the results of the Api::$method() call + */ + public function fetchAll(ApiInterface $api, $method, array $parameters = array()); + + /** + * Method that performs the actual work to refresh the pagination property. + */ + public function postFetch(); + + /** + * Check to determine the availability of a next page. + * + * @return bool + */ + public function hasNext(); + + /** + * Check to determine the availability of a previous page. + * + * @return bool + */ + public function hasPrevious(); + + /** + * Fetch the next page. + * + * @return array + */ + public function fetchNext(); + + /** + * Fetch the previous page. + * + * @return array + */ + public function fetchPrevious(); + + /** + * Fetch the first page. + * + * @return array + */ + public function fetchFirst(); + + /** + * Fetch the last page. + * + * @return array + */ + public function fetchLast(); +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md new file mode 100644 index 000000000..bb42ee19c --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md @@ -0,0 +1,23 @@ +CHANGELOG +========= + +2.5.0 +----- + + * added Debug\TraceableEventDispatcher (originally in HttpKernel) + * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface + * added RegisterListenersPass (originally in HttpKernel) + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 000000000..af0b6cef5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container. + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded. + * + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers. + * + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners. + * + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener. + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param int $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + $key = $serviceId.'.'.$method; + if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * @see EventDispatcherInterface::hasListeners() + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (bool) count($this->listenerIds) || (bool) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * @see EventDispatcherInterface::getListeners() + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach (array_keys($this->listenerIds) as $serviceEventName) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber. + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritdoc} + * + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @throws \InvalidArgumentException if the service is not defined + */ + public function dispatch($eventName, Event $event = null) + { + $this->lazyLoad($eventName); + + return parent::dispatch($eventName, $event); + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php new file mode 100644 index 000000000..2119b81b3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -0,0 +1,320 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; +use Psr\Log\LoggerInterface; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher implements TraceableEventDispatcherInterface +{ + protected $logger; + protected $stopwatch; + + private $called; + private $dispatcher; + + /** + * Constructor. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) + { + $this->dispatcher = $dispatcher; + $this->stopwatch = $stopwatch; + $this->logger = $logger; + $this->called = array(); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->dispatcher->addListener($eventName, $listener, $priority); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + return $this->dispatcher->removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + return $this->dispatcher->removeSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $this->preProcess($eventName); + $this->preDispatch($eventName, $event); + + $e = $this->stopwatch->start($eventName, 'section'); + + $this->dispatcher->dispatch($eventName, $event); + + if ($e->isStarted()) { + $e->stop(); + } + + $this->postDispatch($eventName, $event); + $this->postProcess($eventName); + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getCalledListeners() + { + $called = array(); + foreach ($this->called as $eventName => $listeners) { + foreach ($listeners as $listener) { + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + $called[$eventName.'.'.$info['pretty']] = $info; + } + } + + return $called; + } + + /** + * {@inheritdoc} + */ + public function getNotCalledListeners() + { + try { + $allListeners = $this->getListeners(); + } catch (\Exception $e) { + if (null !== $this->logger) { + $this->logger->info(sprintf('An exception was thrown while getting the uncalled listeners (%s)', $e->getMessage()), array('exception' => $e)); + } + + // unable to retrieve the uncalled listeners + return array(); + } + + $notCalled = array(); + foreach ($allListeners as $eventName => $listeners) { + foreach ($listeners as $listener) { + $called = false; + if (isset($this->called[$eventName])) { + foreach ($this->called[$eventName] as $l) { + if ($l->getWrappedListener() === $listener) { + $called = true; + + break; + } + } + } + + if (!$called) { + $info = $this->getListenerInfo($listener, $eventName); + $notCalled[$eventName.'.'.$info['pretty']] = $info; + } + } + } + + return $notCalled; + } + + /** + * Proxies all method calls to the original event dispatcher. + * + * @param string $method The method name + * @param array $arguments The method arguments + * + * @return mixed + */ + public function __call($method, $arguments) + { + return call_user_func_array(array($this->dispatcher, $method), $arguments); + } + + /** + * Called before dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function preDispatch($eventName, Event $event) + { + } + + /** + * Called after dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function postDispatch($eventName, Event $event) + { + } + + private function preProcess($eventName) + { + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + $this->dispatcher->removeListener($eventName, $listener); + $info = $this->getListenerInfo($listener, $eventName); + $name = isset($info['class']) ? $info['class'] : $info['type']; + $this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch, $this)); + } + } + + private function postProcess($eventName) + { + $skipped = false; + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch. + continue; + } + // Unwrap listener + $this->dispatcher->removeListener($eventName, $listener); + $this->dispatcher->addListener($eventName, $listener->getWrappedListener()); + + $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + if ($listener->wasCalled()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty'])); + } + + if (!isset($this->called[$eventName])) { + $this->called[$eventName] = new \SplObjectStorage(); + } + + $this->called[$eventName]->attach($listener); + } + + if (null !== $this->logger && $skipped) { + $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName)); + } + + if ($listener->stoppedPropagation()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName)); + } + + $skipped = true; + } + } + } + + /** + * Returns information about the listener + * + * @param object $listener The listener + * @param string $eventName The event name + * + * @return array Information about the listener + */ + private function getListenerInfo($listener, $eventName) + { + $info = array( + 'event' => $eventName, + ); + if ($listener instanceof \Closure) { + $info += array( + 'type' => 'Closure', + 'pretty' => 'closure', + ); + } elseif (is_string($listener)) { + try { + $r = new \ReflectionFunction($listener); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Function', + 'function' => $listener, + 'file' => $file, + 'line' => $line, + 'pretty' => $listener, + ); + } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) { + if (!is_array($listener)) { + $listener = array($listener, '__invoke'); + } + $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + try { + $r = new \ReflectionMethod($class, $listener[1]); + $file = $r->getFileName(); + $line = $r->getStartLine(); + } catch (\ReflectionException $e) { + $file = null; + $line = null; + } + $info += array( + 'type' => 'Method', + 'class' => $class, + 'method' => $listener[1], + 'file' => $file, + 'line' => $line, + 'pretty' => $class.'::'.$listener[1], + ); + } + + return $info; + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 000000000..5483e8150 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier + */ +interface TraceableEventDispatcherInterface extends EventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php new file mode 100644 index 000000000..e16627d6a --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier + */ +class WrappedListener +{ + private $listener; + private $name; + private $called; + private $stoppedPropagation; + private $stopwatch; + private $dispatcher; + + public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) + { + $this->listener = $listener; + $this->name = $name; + $this->stopwatch = $stopwatch; + $this->dispatcher = $dispatcher; + $this->called = false; + $this->stoppedPropagation = false; + } + + public function getWrappedListener() + { + return $this->listener; + } + + public function wasCalled() + { + return $this->called; + } + + public function stoppedPropagation() + { + return $this->stoppedPropagation; + } + + public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) + { + $this->called = true; + + $e = $this->stopwatch->start($this->name, 'event_listener'); + + call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher); + + if ($e->isStarted()) { + $e->stop(); + } + + if ($event->isPropagationStopped()) { + $this->stoppedPropagation = true; + } + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php new file mode 100644 index 000000000..7e74a37aa --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Compiler pass to register tagged services for an event dispatcher. + */ +class RegisterListenersPass implements CompilerPassInterface +{ + /** + * @var string + */ + protected $dispatcherService; + + /** + * @var string + */ + protected $listenerTag; + + /** + * @var string + */ + protected $subscriberTag; + + /** + * Constructor. + * + * @param string $dispatcherService Service name of the event dispatcher in processed container + * @param string $listenerTag Tag name used for listener + * @param string $subscriberTag Tag name used for subscribers + */ + public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + { + $this->dispatcherService = $dispatcherService; + $this->listenerTag = $listenerTag; + $this->subscriberTag = $subscriberTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) { + return; + } + + $definition = $container->findDefinition($this->dispatcherService); + + foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id)); + } + + foreach ($events as $event) { + $priority = isset($event['priority']) ? $event['priority'] : 0; + + if (!isset($event['event'])) { + throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); + } + + if (!isset($event['method'])) { + $event['method'] = 'on'.preg_replace_callback(array( + '/(?<=\b)[a-z]/i', + '/[^a-z0-9]/i', + ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); + $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + } + + $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority)); + } + } + + foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id)); + } + + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $container->getParameterBag()->resolveValue($def->getClass()); + + $refClass = new \ReflectionClass($class); + $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!$refClass->implementsInterface($interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $definition->addMethodCall('addSubscriberService', array($id, $class)); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php new file mode 100644 index 000000000..dc39b05d5 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +class Event +{ + /** + * @var bool Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation() + * + * @return bool Whether propagation was already stopped for this event. + * + * @api + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + * + * @api + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event. + * + * @param EventDispatcherInterface $dispatcher + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event. + * + * @return EventDispatcherInterface + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function getDispatcher() + { + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php new file mode 100644 index 000000000..3b032fb08 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + * + * @api + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * @see EventDispatcherInterface::dispatch() + * + * @api + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if (!isset($this->listeners[$eventName])) { + return $event; + } + + $this->doDispatch($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * @see EventDispatcherInterface::getListeners() + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach (array_keys($this->listeners) as $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return array_filter($this->sorted); + } + + /** + * @see EventDispatcherInterface::hasListeners() + */ + public function hasListeners($eventName = null) + { + return (bool) count($this->getListeners($eventName)); + } + + /** + * @see EventDispatcherInterface::addListener() + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * @see EventDispatcherInterface::removeListener() + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * @see EventDispatcherInterface::addSubscriber() + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * @see EventDispatcherInterface::removeSubscriber() + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param callable[] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + call_user_func($listener, $event, $eventName, $this); + if ($event->isPropagationStopped()) { + break; + } + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php new file mode 100644 index 000000000..efb7c5bec --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + * + * @api + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + * + * @api + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string $eventName The event to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return bool true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php new file mode 100644 index 000000000..ff7e305cd --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + * + * @api + */ + public static function getSubscribedEvents(); +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php new file mode 100644 index 000000000..a8955ca42 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Event subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @throws \InvalidArgumentException If key is not found. + * + * @return mixed Contents of array key. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return bool + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + * + * @return mixed + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return bool + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php new file mode 100644 index 000000000..7ef9ece75 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE new file mode 100644 index 000000000..43028bc60 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md new file mode 100644 index 000000000..8031f4dd3 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md @@ -0,0 +1,27 @@ +EventDispatcher Component +========================= + +The Symfony EventDispatcher component implements the Mediator pattern in a +simple and effective way to make your projects truly extensible. + +```php +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\Event; + +$dispatcher = new EventDispatcher(); + +$dispatcher->addListener('event_name', function (Event $event) { + // ... +}); + +$dispatcher->dispatch('event_name'); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/EventDispatcher/ + $ composer install + $ phpunit diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php new file mode 100644 index 000000000..b9e419496 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php @@ -0,0 +1,369 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** + * @var EventDispatcher + */ + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = $this->createEventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + abstract protected function createEventDispatcher(); + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testEventReceivesTheDispatcherInstance() + { + $dispatcher = null; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + public function testEventReceivesTheDispatcherInstanceAsArgument() + { + $listener = new TestWithDispatcher(); + $this->dispatcher->addListener('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->assertNull($listener->dispatcher); + $this->dispatcher->dispatch('test'); + $this->assertEquals('test', $listener->name); + $this->assertSame($this->dispatcher, $listener->dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = $this->createEventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } + + public function testHasListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertFalse($this->dispatcher->hasListeners()); + } + + public function testGetListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertSame(array(), $this->dispatcher->getListeners()); + } + + public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() + { + $this->assertFalse($this->dispatcher->hasListeners('foo')); + $this->assertFalse($this->dispatcher->hasListeners()); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestWithDispatcher +{ + public $name; + public $dispatcher; + + public function foo(Event $e, $name, $dispatcher) + { + $this->name = $name; + $this->dispatcher = $dispatcher; + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10), + )); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 000000000..6f2fbcb9d --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + $container = new Container(); + + return new ContainerAwareEventDispatcher($container); + } + + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTriggerAListenerServiceOutOfScope() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $container->leaveScope('scope'); + $dispatcher->dispatch('onEvent'); + } + + public function testReEnteringAScope() + { + $event = new Event(); + + $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') + ->with($event) + ; + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service1, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + $dispatcher->dispatch('onEvent', $event); + + $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service2 + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); + + $dispatcher->dispatch('onEvent', $event); + + $container->leaveScope('scope'); + + $dispatcher->dispatch('onEvent'); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $event->setDispatcher($dispatcher); + $event->setName('onEvent'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertTrue(isset($listeners['onEvent'])); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => array('onEvent'), + ); + } + + public function onEvent(Event $e) + { + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 000000000..68b952365 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\Debug; + +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; + +class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testAddRemoveListener() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame($listener, $listeners[0]); + + $tdispatcher->removeListener('foo', $listener); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo')); + } + + public function testHasListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $this->assertFalse($dispatcher->hasListeners('foo')); + $this->assertFalse($tdispatcher->hasListeners('foo')); + + $tdispatcher->addListener('foo', $listener = function () {; }); + $this->assertTrue($dispatcher->hasListeners('foo')); + $this->assertTrue($tdispatcher->hasListeners('foo')); + } + + public function testAddRemoveSubscriber() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $subscriber = new EventSubscriber(); + + $tdispatcher->addSubscriber($subscriber); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame(array($subscriber, 'call'), $listeners[0]); + + $tdispatcher->removeSubscriber($subscriber); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetCalledListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener = function () {; }); + + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getNotCalledListeners()); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getCalledListeners()); + $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); + } + + public function testGetCalledListenersNested() + { + $tdispatcher = null; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { + $tdispatcher = $dispatcher; + $dispatcher->dispatch('bar'); + }); + $dispatcher->addListener('bar', function (Event $event) {}); + $dispatcher->dispatch('foo'); + $this->assertSame($dispatcher, $tdispatcher); + $this->assertCount(2, $dispatcher->getCalledListeners()); + } + + public function testLogger() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function () {; }); + $tdispatcher->addListener('foo', $listener2 = function () {; }); + + $logger->expects($this->at(0))->method('debug')->with("Notified event \"foo\" to listener \"closure\"."); + $logger->expects($this->at(1))->method('debug')->with("Notified event \"foo\" to listener \"closure\"."); + + $tdispatcher->dispatch('foo'); + } + + public function testLoggerWithStoppedEvent() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); }); + $tdispatcher->addListener('foo', $listener2 = function () {; }); + + $logger->expects($this->at(0))->method('debug')->with("Notified event \"foo\" to listener \"closure\"."); + $logger->expects($this->at(1))->method('debug')->with("Listener \"closure\" stopped propagation of the event \"foo\"."); + $logger->expects($this->at(2))->method('debug')->with("Listener \"closure\" was not called for event \"foo\"."); + + $tdispatcher->dispatch('foo'); + } + + public function testDispatchCallListeners() + { + $called = array(); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', $listener1 = function () use (&$called) { $called[] = 'foo1'; }); + $tdispatcher->addListener('foo', $listener2 = function () use (&$called) { $called[] = 'foo2'; }); + + $tdispatcher->dispatch('foo'); + + $this->assertEquals(array('foo1', 'foo2'), $called); + } + + public function testDispatchNested() + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $loop = 1; + $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { + ++$loop; + if (2 == $loop) { + $dispatcher->dispatch('foo'); + } + }); + + $dispatcher->dispatch('foo'); + } + + public function testDispatchReusedEventNested() + { + $nestedCall = false; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) { + $dispatcher->dispatch('bar', $e); + }); + $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) { + $nestedCall = true; + }); + + $this->assertFalse($nestedCall); + $dispatcher->dispatch('foo'); + $this->assertTrue($nestedCall); + } +} + +class EventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('foo' => 'call'); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php new file mode 100644 index 000000000..0fdd6372b --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; + +class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests that event subscribers not implementing EventSubscriberInterface + * trigger an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testEventSubscriberWithoutInterface() + { + // one service, not implementing any interface + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('isPublic') + ->will($this->returnValue(true)); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('stdClass')); + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + public function testValidEventSubscriber() + { + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('isPublic') + ->will($this->returnValue(true)); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')); + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'findDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $builder->expects($this->atLeastOnce()) + ->method('findDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded. + */ + public function testPrivateEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded. + */ + public function testPrivateEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded. + */ + public function testAbstractEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded. + */ + public function testAbstractEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + public function testEventSubscriberResolvableClassName() + { + $container = new ContainerBuilder(); + + $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + + $definition = $container->getDefinition('event_dispatcher'); + $expected_calls = array( + array( + 'addSubscriberService', + array( + 'foo', + 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService', + ), + ), + ); + $this->assertSame($expected_calls, $definition->getMethodCalls()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class" + */ + public function testEventSubscriberUnresolvableClassName() + { + $container = new ContainerBuilder(); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } +} + +class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 000000000..5faa5c8be --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\EventDispatcher; + +class EventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + return new EventDispatcher(); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php new file mode 100644 index 000000000..4bd269722 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Test class for Event. + */ +class EventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcher + */ + protected $dispatcher; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event(); + $this->dispatcher = new EventDispatcher(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + $this->dispatcher = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } + + /** + * @group legacy + */ + public function testLegacySetDispatcher() + { + $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); + $this->event->setDispatcher($this->dispatcher); + $this->assertSame($this->dispatcher, $this->event->getDispatcher()); + } + + /** + * @group legacy + */ + public function testLegacyGetDispatcher() + { + $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); + $this->assertNull($this->event->getDispatcher()); + } + + /** + * @group legacy + */ + public function testLegacyGetName() + { + $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); + $this->assertNull($this->event->getName()); + } + + /** + * @group legacy + */ + public function testLegacySetName() + { + $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); + $this->event->setName('foo'); + $this->assertEquals('foo', $this->event->getName()); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php new file mode 100644 index 000000000..aebd82dab --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \stdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event')); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs(). + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->setExpectedException('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertTrue(isset($this->event['name'])); + $this->assertFalse(isset($this->event['nameNotExist'])); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 000000000..80a7e43be --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * @author Bernhard Schussek + */ +class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json new file mode 100644 index 000000000..a516d4670 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json @@ -0,0 +1,43 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/config": "~2.0,>=2.0.5", + "symfony/stopwatch": "~2.3", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } + }, + "target-dir": "Symfony/Component/EventDispatcher", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + } +} diff --git a/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist new file mode 100644 index 000000000..b14fde575 --- /dev/null +++ b/modules/devshop/devshop_github/vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + From a6a6867c2f463e8f5890c12c5cad25dafabca575 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 08:04:24 -0400 Subject: [PATCH 1334/3476] Adding update to enable devshop github. --- modules/devshop/devshop_hosting.install | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/devshop/devshop_hosting.install b/modules/devshop/devshop_hosting.install index f4071aa36..fe02d65e6 100644 --- a/modules/devshop/devshop_hosting.install +++ b/modules/devshop/devshop_hosting.install @@ -26,4 +26,11 @@ function devshop_hosting_update_7001() { */ function devshop_hosting_update_7002() { variable_set('hosting_welcome_page', 0); +} + +/** + * Enable devshop_github module. + */ +function devshop_hosting_update_7003() { + module_enable(array('devshop_hosting')); } \ No newline at end of file From 9dbff38fd311814e9b181fb6fe29de18d1f5d0cd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 08:06:15 -0400 Subject: [PATCH 1335/3476] Adding message to devshop github enable hook to encourage users to go to the settings page. --- modules/devshop/devshop_github/devshop_github.install | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/devshop/devshop_github/devshop_github.install b/modules/devshop/devshop_github/devshop_github.install index 1b323e087..48f8f027b 100644 --- a/modules/devshop/devshop_github/devshop_github.install +++ b/modules/devshop/devshop_github/devshop_github.install @@ -55,6 +55,11 @@ function devshop_github_schema() { function devshop_github_install() { // Create tables. drupal_install_schema('devshop_github'); + + // Display a message about setting a github personal token. + drupal_set_message(t('DevShop GitHub module has been enabled. You must add an access token to enable full functionality at !link.', array( + '!link' => l(t('the settings page'), 'admin/hosting/devshop/github'), + ))); } /** From ab24a8029dbafc062e1acc9f3d762937c4241b79 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 08:42:59 -0400 Subject: [PATCH 1336/3476] Switching back to using deployments API for environment creation. --- .../devshop_github/devshop_github.module | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index 4166a44c3..38324b539 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -261,25 +261,40 @@ function devshop_github_webhook($project_node){ $repo = $project->github_repo; $message .= "About to try to create a deployment for $owner/$repo... \n"; + // Send a "deployment" to github. try { $token = variable_get('devshop_github_token', ''); $client = new \Github\Client(); $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); - // Create a status $sha = $data->pull_request->head->sha; - $url = "http://{$environment_name}.{$project->base_url}"; + $environment_name_url = str_replace('_', '-', $environment_name); + $url = "http://{$environment_name_url}.{$project->base_url}"; + + $params = new stdClass(); + $params->ref = $sha; + $params->environment = $environment_name; + $params->required_contexts = array(); + $post_url = "/repos/$owner/$repo/deployments"; + $deployment = json_decode($client->getHttpClient()->post($post_url, json_encode($params))->getBody(TRUE)); + + // Save deployment to pull request data for later access. + $data->pull_request->deployment = $deployment; + $message .= " Deployment Created! \n"; + + // Create deployment status $params = new stdClass(); $params->state = 'pending'; - $params->target_url = "http://{$environment_name}.{$project->base_url}"; - $params->description = 'Deploying and testing with DevShop...'; - $params->context = 'devshop/test'; + $params->target_url = $url; + $params->description = t('New environment is being created. Please stand by.'); + $deployment_status = $client->getHttpClient()->post("/repos/$owner/$repo/deployments/{$deployment->id}/statuses", json_encode($params)); - $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + $message .= " Deployment Status Created! \n"; } catch (Github\Exception\RuntimeException $e) { watchdog('devshop_github', 'GitHub API Error: ' . $e->getMessage()); + $message .= 'GitHub API Error: ' . $e->getMessage(); } From c89b03349d9ce124cb50d9177df2ddfedbb48ccc Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 09:03:38 -0400 Subject: [PATCH 1337/3476] Creating a new deployment status when PR is "syncronized". --- .../devshop_github/devshop_github.module | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index 38324b539..cc2f32182 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -314,33 +314,39 @@ function devshop_github_webhook($project_node){ } } - // When PR is closed, delete environment. + // When PR is updated, send a new deployment status environment. elseif ($data->action == 'synchronize') { // Save a status on the SHA $owner = $project->github_owner; $repo = $project->github_repo; - $message .= "About to set commit status for $owner/$repo... \n"; + $message .= "About to set deployment status for $owner/$repo... \n"; try { $token = variable_get('devshop_github_token', ''); $client = new \Github\Client(); $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); - // Create a status + // Create deployment status + $environment = $project->environments[$environment_name]; $sha = $data->pull_request->head->sha; - $url = url("node/{$project->nid}", array('absolute' => TRUE)); + $deployment_id = $environment->github_pull_request->pull_request_object->deployment->id; $params = new stdClass(); $params->state = 'pending'; - $params->target_url = "http://{$environment_name}.{$project->base_url}"; - $params->description = 'Deploying and testing with DevShop...'; - $params->context = 'devshop/devshop-deploy'; + $params->target_url = $environment->url; + $params->description = t('Code is being deployed. Please stand by.'); + + $post_url = "/repos/$owner/$repo/deployments/{$deployment_id}/statuses"; + $message .= "Attempting to create deployment status: $post_url \n"; - $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + $deployment_status = $client->getHttpClient()->post($post_url, json_encode($params)); + + $message .= " Deployment Status Created! \n"; } catch (Github\Exception\RuntimeException $e) { watchdog('devshop_github', 'GitHub API Error: ' . $e->getMessage()); + $message .= 'GitHub API Error: ' . $e->getMessage() . "\n"; } @@ -364,7 +370,7 @@ function devshop_github_webhook($project_node){ $message .= ' ' . t('Pull Request info saved to DevShop.'); } } - // When PR is cloded, delete environment. + // When PR is closed, delete environment. elseif ($data->action == 'closed'){ $message .= "Pull Request Closed \n"; if ($project->settings->github['pull_request_environments_delete']){ From 1878d452c6f5eb87fae297427da62ff9691c9759 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 09:36:53 -0400 Subject: [PATCH 1338/3476] Moving task status hook to devshop_github.drush.inc --- .../devshop_github/devshop_github.drush.inc | 51 +++++ .../devshop_github/devshop_github.module | 180 +++++++++--------- 2 files changed, 143 insertions(+), 88 deletions(-) diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc index 216f4cf6c..525e8337a 100644 --- a/modules/devshop/devshop_github/devshop_github.drush.inc +++ b/modules/devshop/devshop_github/devshop_github.drush.inc @@ -1,5 +1,8 @@ task_type == 'deploy') { + + // Create a deployment status + $site = $task->ref; + + if ($site->environment->github_pull_request->pull_request_object->deployment){ + + } + + } + + // When verified, update deployment. + // This only needs to happen on the first deployment of a pull request environment. + if ($task->task_type == 'verify' || $task->task_type == 'devshop-deploy' && isset($task->ref->environment->github_pull_request->pull_request_object->deployment)) { + + // Create a deployment status + $project = $task->ref->project; + $owner = $project->github_owner; + $repo = $project->github_repo; + $site = node_load($task->rid); + $deployment_id = $site->environment->github_pull_request->pull_request_object->deployment->id; + + try { + $token = variable_get('devshop_github_token', ''); + $client = new \Github\Client(); + $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); + + $params = new stdClass(); + $params->state = $status == HOSTING_TASK_SUCCESS? 'success': 'failure'; + $params->target_url = $task->ref->environment->url; + $params->description = t('Pull Request environment is ready!'); + $post_url = "/repos/$owner/$repo/deployments/{$deployment_id}/statuses"; + + drush_log('Attempting to create github deployment status: ' . $post_url, 'success'); + + $deployment_status = $client->getHttpClient()->post($post_url, json_encode($params)); + drush_log('Deployment status created!', 'success'); + + } catch (Github\Exception\RuntimeException $e) { + drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); + } } } \ No newline at end of file diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index cc2f32182..197a2ab8e 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -53,93 +53,93 @@ function devshop_github_nodeapi_project_view(&$node, $a3, $a4) { } - -/** - * Implements hook_hosting_task_update_status() - */ -function devshop_github_hosting_task_update_status($task, $status) { - - $task_types = array( - 'test', - 'import', - 'devshop-deploy', - ); - - if (in_array($task->task_type, $task_types) && $task->ref->type == 'site' && isset($task->ref->environment->github_pull_request)) { - - // If autoloader is not available, return. - if (!file_exists(__DIR__ . '/vendor/autoload.php')) { - return; - } - - // If project is not from github, return. - if ($task->ref->project->git_provider != 'github') { - return; - } - - // Include vendors - require_once 'vendor/autoload.php'; - - drush_log('===========================================', 'ok'); - drush_log('Notifying github...', 'ok'); - - // Extract username and repo - list($s, $owner, $repo) = explode('/', parse_url($task->ref->project->git_repo_url, PHP_URL_PATH)); - - try { - $token = variable_get('devshop_github_token', ''); - $client = new \Github\Client(); - $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); - - // Create a status - $sha = $task->ref->environment->github_pull_request->pull_request_object->head->sha; - - if ($task->task_type == 'devshop-deploy'){ - $description = t('Deployed to Environment: ') . _hosting_parse_error_code($status); - $url = $task->ref->environment->url; - } - elseif ($task->task_type == 'test') { - $description = t('Tests: ') . _hosting_parse_error_code($status); - $url = url("devshop_tests/{$task->nid}/{$task->vid}", array('absolute' => TRUE)); - } - else { - $description = 'Something happened...'; - $url = $task->ref->environment->url; - } - - if ($status == HOSTING_TASK_ERROR) { - $state = 'error'; - } - elseif ($status == HOSTING_TASK_PROCESSING) { - $state = 'pending'; - } - elseif ($status == HOSTING_TASK_SUCCESS || $status == HOSTING_TASK_WARNING) { - $state = 'success'; - } - else { - $state = 'error'; - } - - $params = new stdClass(); - $params->state = $state; - $params->target_url = $url; - $params->description = $description; - $params->context = 'devshop/' . $task->task_type; - - $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); - - drush_log('Status posted! ', 'ok'); - } catch (Github\Exception\RuntimeException $e) { - drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); - drush_log(l(t('Configure GitHub API'), 'admin/hosting/devshop/github'), 'error'); - } catch (Github\Exception\ValidationFailedException $e) { - drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); - } - - drush_log('done trying... ', 'ok'); - - } -} +// +///** +// * Implements hook_hosting_task_update_status() +// */ +//function devshop_github_hosting_task_update_status($task, $status) { +// +// $task_types = array( +// 'test', +// 'import', +// 'devshop-deploy', +// ); +// +// if (in_array($task->task_type, $task_types) && $task->ref->type == 'site' && isset($task->ref->environment->github_pull_request)) { +// +// // If autoloader is not available, return. +// if (!file_exists(__DIR__ . '/vendor/autoload.php')) { +// return; +// } +// +// // If project is not from github, return. +// if ($task->ref->project->git_provider != 'github') { +// return; +// } +// +// // Include vendors +// require_once 'vendor/autoload.php'; +// +// drush_log('===========================================', 'ok'); +// drush_log('Notifying github...', 'ok'); +// +// // Extract username and repo +// list($s, $owner, $repo) = explode('/', parse_url($task->ref->project->git_repo_url, PHP_URL_PATH)); +// +// try { +// $token = variable_get('devshop_github_token', ''); +// $client = new \Github\Client(); +// $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); +// +// // Create a status +// $sha = $task->ref->environment->github_pull_request->pull_request_object->head->sha; +// +// if ($task->task_type == 'devshop-deploy'){ +// $description = t('Deployed to Environment: ') . _hosting_parse_error_code($status); +// $url = $task->ref->environment->url; +// } +// elseif ($task->task_type == 'test') { +// $description = t('Tests: ') . _hosting_parse_error_code($status); +// $url = url("devshop_tests/{$task->nid}/{$task->vid}", array('absolute' => TRUE)); +// } +// else { +// $description = 'Something happened...'; +// $url = $task->ref->environment->url; +// } +// +// if ($status == HOSTING_TASK_ERROR) { +// $state = 'error'; +// } +// elseif ($status == HOSTING_TASK_PROCESSING) { +// $state = 'pending'; +// } +// elseif ($status == HOSTING_TASK_SUCCESS || $status == HOSTING_TASK_WARNING) { +// $state = 'success'; +// } +// else { +// $state = 'error'; +// } +// +// $params = new stdClass(); +// $params->state = $state; +// $params->target_url = $url; +// $params->description = $description; +// $params->context = 'devshop/' . $task->task_type; +// +// $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); +// +// drush_log('Status posted! ', 'ok'); +// } catch (Github\Exception\RuntimeException $e) { +// drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); +// drush_log(l(t('Configure GitHub API'), 'admin/hosting/devshop/github'), 'error'); +// } catch (Github\Exception\ValidationFailedException $e) { +// drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); +// } +// +// drush_log('done trying... ', 'ok'); +// +// } +//} /** * @@ -330,7 +330,11 @@ function devshop_github_webhook($project_node){ // Create deployment status $environment = $project->environments[$environment_name]; $sha = $data->pull_request->head->sha; - $deployment_id = $environment->github_pull_request->pull_request_object->deployment->id; + $deployment = $environment->github_pull_request->pull_request_object->deployment; + $deployment_id = $deployment->id; + + // Don't lose the deployment. + $data->pull_request->deployment = $deployment; $params = new stdClass(); $params->state = 'pending'; From 47ec0245267c77626c106a1fe878467bd1ee1eb8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 09:44:27 -0400 Subject: [PATCH 1339/3476] Create a new deployment when syncronizing. --- .../devshop_github/devshop_github.module | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index 197a2ab8e..6545bb9cd 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -317,7 +317,7 @@ function devshop_github_webhook($project_node){ // When PR is updated, send a new deployment status environment. elseif ($data->action == 'synchronize') { - // Save a status on the SHA + // Create a new deployment $owner = $project->github_owner; $repo = $project->github_repo; $message .= "About to set deployment status for $owner/$repo... \n"; @@ -327,15 +327,24 @@ function devshop_github_webhook($project_node){ $client = new \Github\Client(); $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); - // Create deployment status - $environment = $project->environments[$environment_name]; $sha = $data->pull_request->head->sha; - $deployment = $environment->github_pull_request->pull_request_object->deployment; - $deployment_id = $deployment->id; - // Don't lose the deployment. + $params = new stdClass(); + $params->ref = $sha; + $params->environment = $environment_name; + $params->required_contexts = array(); + $post_url = "/repos/$owner/$repo/deployments"; + $deployment = json_decode($client->getHttpClient()->post($post_url, json_encode($params))->getBody(TRUE)); + + // Save deployment to pull request data for later access. $data->pull_request->deployment = $deployment; + $message .= " Deployment Created! \n"; + + // Create deployment status + $environment = $project->environments[$environment_name]; + $deployment_id = $deployment->id; + $params = new stdClass(); $params->state = 'pending'; $params->target_url = $environment->url; From 70743f3016fe49e61958a0cf7740fab32084040a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 09:44:43 -0400 Subject: [PATCH 1340/3476] Send an error to github when there is a warning, a failure otherwise. --- .../devshop/devshop_github/devshop_github.drush.inc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc index 525e8337a..8ff7a0793 100644 --- a/modules/devshop/devshop_github/devshop_github.drush.inc +++ b/modules/devshop/devshop_github/devshop_github.drush.inc @@ -89,7 +89,16 @@ function devshop_github_hosting_task_update_status($task, $status) { $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); $params = new stdClass(); - $params->state = $status == HOSTING_TASK_SUCCESS? 'success': 'failure'; + if ($status == HOSTING_TASK_SUCCESS) { + $params->state = 'success'; + } + elseif ($status == HOSTING_TASK_WARNING) { + $params->state = 'error'; + } + else { + $params->state = 'failure'; + } + $params->target_url = $task->ref->environment->url; $params->description = t('Pull Request environment is ready!'); $post_url = "/repos/$owner/$repo/deployments/{$deployment_id}/statuses"; From 8d2d4f7e4382e043f077c02227a07a272a036c0f Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 10:09:09 -0400 Subject: [PATCH 1341/3476] Use correct d() property when calling provision_backend_invoke. --- deploy_hooks_example/devshop.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy_hooks_example/devshop.drush.inc b/deploy_hooks_example/devshop.drush.inc index 682e044e9..ef63cd87c 100644 --- a/deploy_hooks_example/devshop.drush.inc +++ b/deploy_hooks_example/devshop.drush.inc @@ -32,7 +32,7 @@ function drush_devshop_post_provision_devshop_deploy($branch) { drush_log("[CUSTOM] Successfully checked out $branch to environment $environment_name in project $project_name.", 'ok'); - provision_backend_invoke(d(), 'status'); + provision_backend_invoke(d()->name, 'status'); } } From e4d6e08b89cf499d3ccfb42ab58e0c4313dcc045 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 10:14:28 -0400 Subject: [PATCH 1342/3476] ensuring logic. --- modules/devshop/devshop_github/devshop_github.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc index 8ff7a0793..959f89b24 100644 --- a/modules/devshop/devshop_github/devshop_github.drush.inc +++ b/modules/devshop/devshop_github/devshop_github.drush.inc @@ -74,7 +74,7 @@ function devshop_github_hosting_task_update_status($task, $status) { // When verified, update deployment. // This only needs to happen on the first deployment of a pull request environment. - if ($task->task_type == 'verify' || $task->task_type == 'devshop-deploy' && isset($task->ref->environment->github_pull_request->pull_request_object->deployment)) { + if (($task->task_type == 'verify' || $task->task_type == 'devshop-deploy') && isset($task->ref->environment->github_pull_request->pull_request_object->deployment)) { // Create a deployment status $project = $task->ref->project; From 1ad1543b9185cc554efa726bac10d4607be881b8 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 10:15:01 -0400 Subject: [PATCH 1343/3476] better comments. --- modules/devshop/devshop_github/devshop_github.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc index 959f89b24..bc9263dbe 100644 --- a/modules/devshop/devshop_github/devshop_github.drush.inc +++ b/modules/devshop/devshop_github/devshop_github.drush.inc @@ -73,7 +73,7 @@ function devshop_github_hosting_task_update_status($task, $status) { } // When verified, update deployment. - // This only needs to happen on the first deployment of a pull request environment. + // On the first deployment of a pull request environment, we need to hook into verify. Subsequent pushes need to hook into devshop-deploy. if (($task->task_type == 'verify' || $task->task_type == 'devshop-deploy') && isset($task->ref->environment->github_pull_request->pull_request_object->deployment)) { // Create a deployment status From d4e3d0f345e5e2919e3bc679474475d3cf954bd0 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Mon, 4 May 2015 10:28:29 -0400 Subject: [PATCH 1344/3476] Only run on deploy status on sites. --- modules/devshop/devshop_github/devshop_github.drush.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc index bc9263dbe..0501ba2f8 100644 --- a/modules/devshop/devshop_github/devshop_github.drush.inc +++ b/modules/devshop/devshop_github/devshop_github.drush.inc @@ -74,10 +74,11 @@ function devshop_github_hosting_task_update_status($task, $status) { // When verified, update deployment. // On the first deployment of a pull request environment, we need to hook into verify. Subsequent pushes need to hook into devshop-deploy. - if (($task->task_type == 'verify' || $task->task_type == 'devshop-deploy') && isset($task->ref->environment->github_pull_request->pull_request_object->deployment)) { + $site = node_load($task->rid); + if (($task->task_type == 'verify' || $task->task_type == 'devshop-deploy') && $site->type == 'site' && isset($site->environment->github_pull_request->pull_request_object->deployment)) { // Create a deployment status - $project = $task->ref->project; + $project = $site->project; $owner = $project->github_owner; $repo = $project->github_repo; $site = node_load($task->rid); From 06d2dfb17371dc26e2b7ca7c4aef5bcdf7b414df Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 5 May 2015 21:13:10 -0400 Subject: [PATCH 1345/3476] Adding back modalframe, it's required by hosting module. --- devmaster.make | 1 + 1 file changed, 1 insertion(+) diff --git a/devmaster.make b/devmaster.make index 2c68fe3a9..083ec9a1c 100644 --- a/devmaster.make +++ b/devmaster.make @@ -9,6 +9,7 @@ includes[devshop] = "drupal-org.make" ; Aegir Modules projects[hosting][version] = "2.3" +projects[modalframe][version] = "1.9" ; Contrib Modules projects[admin_menu][version] = "1.9" From 303917e60450a5c2db38bfbe612aa1c99522bd88 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 9 May 2015 08:25:07 -0400 Subject: [PATCH 1346/3476] Adding in GitHub Status API integration for devshop deploy runs. --- .../devshop_github/devshop_github.drush.inc | 46 ++++++++++++------- .../devshop_github/devshop_github.module | 35 +++++++++++++- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc index 0501ba2f8..9af63b5a5 100644 --- a/modules/devshop/devshop_github/devshop_github.drush.inc +++ b/modules/devshop/devshop_github/devshop_github.drush.inc @@ -4,6 +4,8 @@ require_once 'vendor/autoload.php'; /** + * @TODO: Remove this. GitHub is notified immediately on webhook request via devshop_github_webhook() + * * Implements drush_HOOK_pre_COMMAND() * * This runs for each task during the command @@ -13,7 +15,6 @@ require_once 'vendor/autoload.php'; * This hook should ONLY be used to pass options from a hostmaster task form to * the $task object, or if you don't need this functionality from the command * line. - */ function drush_devshop_github_pre_hosting_task() { $task =& drush_get_context('HOSTING_TASK'); @@ -56,21 +57,13 @@ function drush_devshop_github_pre_hosting_task() { } } + */ + /** * Implements hook_hosting_task_update_status() */ function devshop_github_hosting_task_update_status($task, $status) { - if ($task->task_type == 'deploy') { - - // Create a deployment status - $site = $task->ref; - - if ($site->environment->github_pull_request->pull_request_object->deployment){ - - } - - } // When verified, update deployment. // On the first deployment of a pull request environment, we need to hook into verify. Subsequent pushes need to hook into devshop-deploy. @@ -90,14 +83,11 @@ function devshop_github_hosting_task_update_status($task, $status) { $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN); $params = new stdClass(); - if ($status == HOSTING_TASK_SUCCESS) { - $params->state = 'success'; - } - elseif ($status == HOSTING_TASK_WARNING) { - $params->state = 'error'; + if ($status == HOSTING_TASK_SUCCESS || $status == HOSTING_TASK_WARNING) { + $params->state = $state = 'success'; } else { - $params->state = 'failure'; + $params->state = $state = 'failure'; } $params->target_url = $task->ref->environment->url; @@ -109,6 +99,28 @@ function devshop_github_hosting_task_update_status($task, $status) { $deployment_status = $client->getHttpClient()->post($post_url, json_encode($params)); drush_log('Deployment status created!', 'success'); + + // Update Status API + + // Create a status + $sha = $site->environment->github_pull_request->pull_request_object->head->sha; + + $params = new stdClass(); + $params->state = $state; + $params->target_url = url("node/{$task->nid}", array('absolute' => TRUE));; + + if ($status == HOSTING_TASK_WARNING) { + $params->description = t('DevShop: Deploy success with warnings'); + } + else { + $params->description = t('DevShop: Deploy !status', array('!status' => $state)); + } + $params->context = 'devshop/deploy'; + + $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + drush_log('Commit status created!', 'success'); + + } catch (Github\Exception\RuntimeException $e) { drush_log('GitHub API Error: ' . $e->getMessage(), 'error'); } diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module index 6545bb9cd..a32eb0e4d 100644 --- a/modules/devshop/devshop_github/devshop_github.module +++ b/modules/devshop/devshop_github/devshop_github.module @@ -292,12 +292,29 @@ function devshop_github_webhook($project_node){ $message .= " Deployment Status Created! \n"; + // Set a commit status for this REF for devshop/deploy context + $sha =$data->pull_request->head->sha; + + $params = new stdClass(); + $params->state = 'pending'; + $params->target_url = url("node/$project->nid", array('absolute' => TRUE)); + $params->description = t('DevShop: Deploy'); + $params->context = 'devshop/deploy'; + + // Post status to github + $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + $message .= " Commit Status Created! \n"; + + // @TODO: If environment is configured to run tests, add another status. + } catch (Github\Exception\RuntimeException $e) { watchdog('devshop_github', 'GitHub API Error: ' . $e->getMessage()); $message .= 'GitHub API Error: ' . $e->getMessage(); + } catch (Github\Exception\ValidationFailedException $e) { + watchdog('devshop_github', 'GitHub API Error: ' . $e->getMessage()); + $message .= 'GitHub API Error: ' . $e->getMessage(); } - // Insert PR record if (!$already_have_pr_info) { $info = new stdClass(); @@ -357,6 +374,22 @@ function devshop_github_webhook($project_node){ $message .= " Deployment Status Created! \n"; + // Set a commit status for this REF for devshop/deploy context + $sha =$data->pull_request->head->sha; + + $params = new stdClass(); + $params->state = 'pending'; + $params->target_url = url("node/$project->nid", array('absolute' => TRUE)); + $params->description = t('DevShop: Deploy'); + $params->context = 'devshop/deploy'; + + // Post status to github + $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params)); + + $message .= " Commit Status Created! \n"; + + // @TODO: If environment is configured to run tests, add another status. + } catch (Github\Exception\RuntimeException $e) { watchdog('devshop_github', 'GitHub API Error: ' . $e->getMessage()); $message .= 'GitHub API Error: ' . $e->getMessage() . "\n"; From 1c27e54ca132c21f1ebfd106c108085b64808a71 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 May 2015 22:34:42 -0400 Subject: [PATCH 1347/3476] bumping version of hosting. --- devmaster.make | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devmaster.make b/devmaster.make index 083ec9a1c..43ea87756 100644 --- a/devmaster.make +++ b/devmaster.make @@ -8,7 +8,7 @@ projects[drupal][type] = "core" includes[devshop] = "drupal-org.make" ; Aegir Modules -projects[hosting][version] = "2.3" +projects[hosting][version] = "2.4" projects[modalframe][version] = "1.9" ; Contrib Modules From 94078ebe923cb423d350ee2aebbf4656801bfaa1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Wed, 20 May 2015 22:36:43 -0400 Subject: [PATCH 1348/3476] adding back modalframe, sadly --- devmaster.profile | 1 + 1 file changed, 1 insertion(+) diff --git a/devmaster.profile b/devmaster.profile index 348f17930..03ab82652 100644 --- a/devmaster.profile +++ b/devmaster.profile @@ -20,6 +20,7 @@ function devmaster_profile_modules() { 'install_profile_api', 'jquery_ui', 'jquery_update', + 'modalframe', 'admin_menu', 'views', 'views_bulk_operations', From c0f71e01f2e63b0535b5b0c25bc239fe93f50c04 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Jun 2015 09:59:54 -0400 Subject: [PATCH 1349/3476] Use latest hosting 2.x for source install, leave tagged version with a note for releases. --- devmaster.make | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/devmaster.make b/devmaster.make index 43ea87756..8732208b6 100644 --- a/devmaster.make +++ b/devmaster.make @@ -8,10 +8,15 @@ projects[drupal][type] = "core" includes[devshop] = "drupal-org.make" ; Aegir Modules -projects[hosting][version] = "2.4" -projects[modalframe][version] = "1.9" +; For development, use latest branch. +projects[hosting][version] = "2.x" + +; For release, use tagged version +;projects[hosting][version] = "2.4" ; Contrib Modules +projects[modalframe][version] = "1.9" + projects[admin_menu][version] = "1.9" projects[admin_menu][subdir] = contrib From d12a1b459efd83e4df3ab6b3674104b0b57ca174 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Jun 2015 10:13:22 -0400 Subject: [PATCH 1350/3476] Fixing incorrect variable name for project base path! --- modules/devshop/devshop_projects/inc/admin.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/devshop/devshop_projects/inc/admin.inc b/modules/devshop/devshop_projects/inc/admin.inc index 82935be39..6cda4b71a 100644 --- a/modules/devshop/devshop_projects/inc/admin.inc +++ b/modules/devshop/devshop_projects/inc/admin.inc @@ -22,7 +22,7 @@ function devshop_projects_settings_form() { '#title' => t('Projects Base Path'), '#type' => 'textfield', '#description' => t('The default base path that all projects will be created in. Projects each get their own folder inside this path.'), - '#default_value' => variable_get('devshop_project_default_path', '/var/aegir/projects'), + '#default_value' => variable_get('devshop_project_base_path', '/var/aegir/projects'), ); $form['paths']['devshop_project_default_drupal_path'] = array( '#title' => t('Default path to Drupal'), From cce446aa29d2302e94864f905fd9d70bc31e8129 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Jun 2015 11:38:11 -0400 Subject: [PATCH 1351/3476] Adding "Extra Users" module. --- modules/devshop/devshop_extra_users/README.md | 0 .../devshop_extra_users.drush.inc | 34 +++++++++++++++++++ .../devshop_extra_users.info | 5 +++ .../devshop_extra_users.module | 14 ++++++++ 4 files changed, 53 insertions(+) create mode 100644 modules/devshop/devshop_extra_users/README.md create mode 100644 modules/devshop/devshop_extra_users/devshop_extra_users.drush.inc create mode 100644 modules/devshop/devshop_extra_users/devshop_extra_users.info create mode 100644 modules/devshop/devshop_extra_users/devshop_extra_users.module diff --git a/modules/devshop/devshop_extra_users/README.md b/modules/devshop/devshop_extra_users/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/modules/devshop/devshop_extra_users/devshop_extra_users.drush.inc b/modules/devshop/devshop_extra_users/devshop_extra_users.drush.inc new file mode 100644 index 000000000..ac8ce964a --- /dev/null +++ b/modules/devshop/devshop_extra_users/devshop_extra_users.drush.inc @@ -0,0 +1,34 @@ +ref->type == 'site' && isset($task->ref->environment->settings->manager_email)) { + + // Create an extra user. + $email = $task->ref->environment->settings->manager_email; + $password = provision_password(); + $arguments = array('name' => $email); + $data = array( + 'mail' => $email, + 'password' => $password, + ); + + drush_log('Manager email is found! running user-create...', 'ok'); + provision_backend_invoke($task->ref->title, 'user-create', $arguments, $data); + + drush_log(dt('User !user has been created with password !password', array( + '!user' => $email, + '!password' => $password, + )), 'ok'); + + drush_log('No email has been sent! Please notify your new user.'); + } +} diff --git a/modules/devshop/devshop_extra_users/devshop_extra_users.info b/modules/devshop/devshop_extra_users/devshop_extra_users.info new file mode 100644 index 000000000..bde86885b --- /dev/null +++ b/modules/devshop/devshop_extra_users/devshop_extra_users.info @@ -0,0 +1,5 @@ +name = DevShop Extra Install +description = Example module for loading extra info for an install profile. +core = 6.x +package = DevShop + diff --git a/modules/devshop/devshop_extra_users/devshop_extra_users.module b/modules/devshop/devshop_extra_users/devshop_extra_users.module new file mode 100644 index 000000000..7229598b7 --- /dev/null +++ b/modules/devshop/devshop_extra_users/devshop_extra_users.module @@ -0,0 +1,14 @@ + 'textfield', + '#title' => t('Manager Email'), + '#description' => t('Enter an email address and a user will be created.'), + ); + } +} \ No newline at end of file From 404099b9842a7f09fc006cd62c1cdc0b2849e240 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Jun 2015 11:40:41 -0400 Subject: [PATCH 1352/3476] Ensuring our form alter only runs on the devshop-create task form. --- modules/devshop/devshop_extra_users/devshop_extra_users.module | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/devshop/devshop_extra_users/devshop_extra_users.module b/modules/devshop/devshop_extra_users/devshop_extra_users.module index 7229598b7..2cfb838e8 100644 --- a/modules/devshop/devshop_extra_users/devshop_extra_users.module +++ b/modules/devshop/devshop_extra_users/devshop_extra_users.module @@ -4,7 +4,8 @@ * Implements hook_form_alter(). */ function devshop_extra_users_form_alter(&$form, $form_state, $form_id) { - if ($form_id == 'hosting_task_confirm_form') { + if ($form_id == 'hosting_task_confirm_form' && $form['#parameters'][3] == 'devshop-create') { + $form['parameters']['environment']['settings']['manager_email'] = array( '#type' => 'textfield', '#title' => t('Manager Email'), From 9a9b1d00ccdf34e960c570d1a10d7dfe369735e7 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Tue, 2 Jun 2015 11:45:05 -0400 Subject: [PATCH 1353/3476] Adding to devshop extras readme. --- modules/devshop/devshop_extra_users/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/modules/devshop/devshop_extra_users/README.md b/modules/devshop/devshop_extra_users/README.md index e69de29bb..9abb25762 100644 --- a/modules/devshop/devshop_extra_users/README.md +++ b/modules/devshop/devshop_extra_users/README.md @@ -0,0 +1,18 @@ +DevShop Extras: Users +===================== + +This module provides an example of how to use the DevShop front-end to take action +after an environment is installed. + +Enable it, then visit the "Create Environment" form for a project. + +You will see a form field for "Manager Email". This field gets saved into the environment settings automatically. + +Then, in `devshop_extras_users.drush.inc` during the `hook_post_hosting_TASKTYPE_task` +hook, this module creates a new user in your drupal site immediately after the +installation. + +Nothing else happens here. Typically you would want to set a role or send an +email. + +Use this code as an example for extending your own environments. \ No newline at end of file From 29a34202ed0be7837262d9159b0f4c0c220b3c03 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 4 Jun 2015 17:12:49 -0400 Subject: [PATCH 1354/3476] When cloning an environment, use the source site's database server. --- .../devshop/devshop_projects/devshop_projects.drush.inc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/devshop/devshop_projects/devshop_projects.drush.inc b/modules/devshop/devshop_projects/devshop_projects.drush.inc index 63f84e946..e794baee1 100644 --- a/modules/devshop/devshop_projects/devshop_projects.drush.inc +++ b/modules/devshop/devshop_projects/devshop_projects.drush.inc @@ -284,15 +284,14 @@ function devshop_projects_post_hosting_verify_task($task, $data) { // Check for site to clone if ($platform->environment->settings->site_to_clone) { - $servers = hosting_get_servers('db'); - + $site = node_load($platform->environment->settings->site_to_clone); $args['target_platform'] = $platform->nid; // Ensure new URI doesn't contain underscores or other invalid characters. $args['new_uri'] = str_replace('_', '-', $platform->environment->name) .'.'. $platform->project->base_url; - // @TODO: Match settings of cloned environment. - $args['new_db_server'] = key($servers); + // Use the default database server for the project. + $args['new_db_server'] = $site->db_server; drush_log('[DEVSHOP] Cloning environment: ' . $platform->environment->name); // Queue the Cloning of the site. From def8721ecab07c35d76b86751818c78e522d1352 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 4 Jun 2015 17:14:12 -0400 Subject: [PATCH 1355/3476] When creating a new "site" node, use the project's default database server. --- modules/devshop/devshop_projects/devshop_projects.drush.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/devshop/devshop_projects/devshop_projects.drush.inc b/modules/devshop/devshop_projects/devshop_projects.drush.inc index e794baee1..5232a7488 100644 --- a/modules/devshop/devshop_projects/devshop_projects.drush.inc +++ b/modules/devshop/devshop_projects/devshop_projects.drush.inc @@ -299,7 +299,7 @@ function devshop_projects_post_hosting_verify_task($task, $data) { hosting_add_task($platform->environment->settings->site_to_clone, 'clone', $args); } else { - devshop_projects_create_site($platform->project, $platform, $platform->environment->name); + devshop_projects_create_site($platform->project, $platform, $platform->environment->name, $platform->project->settings->default_environment['db_server']); drush_log('[DEVSHOP] Site Created! Please wait for it to be installed. '); } } From d73591a700d3c995308e8ee7a8564b857bc94e1a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Thu, 4 Jun 2015 17:18:16 -0400 Subject: [PATCH 1356/3476] Use the project default environment settings to determine the platform to use for new environments. --- modules/devshop/devshop_projects/tasks/create.inc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/devshop/devshop_projects/tasks/create.inc b/modules/devshop/devshop_projects/tasks/create.inc index eea21ac07..13fccfee4 100644 --- a/modules/devshop/devshop_projects/tasks/create.inc +++ b/modules/devshop/devshop_projects/tasks/create.inc @@ -208,10 +208,8 @@ function hosting_create_environment($project, $new_environment_name, $new_enviro $platform->uid = $user->uid; $platform->name = $user->name; - // Set servers - // @TODO: Use $settings to retrieve these. - $servers = hosting_get_servers('http'); - $platform->web_server = variable_get('devshop_projects_default_web_server', key($servers));; + // Set web server to the project default. + $platform->web_server = $project->settings->default_environment['web_server']; // If drupal path, append to code path/environment name if ($project->drupal_path) { From 68a45bafdd461fc94036f8a2267cf0cc062a988c Mon Sep 17 00:00:00 2001 From: Chris Hertzog Date: Tue, 16 Jun 2015 12:24:43 -0400 Subject: [PATCH 1357/3476] devshop_digital_ocean --- .../devshop_cloud/devshop_cloud.drush.inc | 17 +- .../devshop_cloud/devshop_cloud.module | 8 - .../devshop_cloud/devshop_cloud.service.inc | 7 - .../devshop_digital_ocean.drush.inc | 23 + .../digitalocean/devshop_digital_ocean.info | 6 + .../digitalocean/devshop_digital_ocean.module | 68 + .../devshop_digital_ocean.service.inc | 62 + .../digital-ocean-master/.scrutinizer.yml | 8 + .../digitalocean/digital-ocean-master/LICENSE | 19 + .../digital-ocean-master/composer.json | 58 + .../digital-ocean-master/composer.lock | 1138 +++++ .../src/Adapter/AbstractAdapter.php | 31 + .../src/Adapter/AdapterInterface.php | 62 + .../src/Adapter/BuzzAdapter.php | 135 + .../src/Adapter/BuzzOAuthListener.php | 49 + .../src/Adapter/Guzzle5Adapter.php | 134 + .../src/Adapter/GuzzleAdapter.php | 147 + .../src/Api/AbstractApi.php | 66 + .../digital-ocean-master/src/Api/Account.php | 31 + .../digital-ocean-master/src/Api/Action.php | 50 + .../digital-ocean-master/src/Api/Domain.php | 80 + .../src/Api/DomainRecord.php | 156 + .../digital-ocean-master/src/Api/Droplet.php | 444 ++ .../digital-ocean-master/src/Api/Image.php | 137 + .../digital-ocean-master/src/Api/Key.php | 109 + .../src/Api/RateLimit.php | 30 + .../digital-ocean-master/src/Api/Region.php | 35 + .../digital-ocean-master/src/Api/Size.php | 35 + .../src/DigitalOceanV2.php | 128 + .../src/Entity/AbstractEntity.php | 99 + .../src/Entity/Account.php | 38 + .../src/Entity/Action.php | 93 + .../src/Entity/Domain.php | 33 + .../src/Entity/DomainRecord.php | 53 + .../src/Entity/Droplet.php | 200 + .../digital-ocean-master/src/Entity/Image.php | 71 + .../src/Entity/Kernel.php | 33 + .../digital-ocean-master/src/Entity/Key.php | 38 + .../digital-ocean-master/src/Entity/Meta.php | 23 + .../src/Entity/Network.php | 47 + .../src/Entity/NextBackupWindow.php | 42 + .../src/Entity/RateLimit.php | 33 + .../src/Entity/Region.php | 43 + .../digital-ocean-master/src/Entity/Size.php | 63 + .../src/Entity/Upgrade.php | 41 + .../src/Exception/ExceptionInterface.php | 29 + .../src/Exception/ExceptionReader.php | 89 + .../src/Exception/ResponseException.php | 59 + .../digital-ocean-master/src/functions.php | 54 + .../digital-ocean-master/vendor/autoload.php | 7 + .../digital-ocean-master/vendor/bin/phpspec | 1 + .../vendor/composer/ClassLoader.php | 413 ++ .../vendor/composer/autoload_classmap.php | 36 + .../vendor/composer/autoload_files.php | 11 + .../vendor/composer/autoload_namespaces.php | 16 + .../vendor/composer/autoload_psr4.php | 20 + .../vendor/composer/autoload_real.php | 55 + .../vendor/composer/installed.json | 1160 +++++ .../vendor/doctrine/instantiator/.gitignore | 5 + .../doctrine/instantiator/.scrutinizer.yml | 46 + .../doctrine/instantiator/.travis.install.sh | 14 + .../vendor/doctrine/instantiator/.travis.yml | 22 + .../doctrine/instantiator/CONTRIBUTING.md | 35 + .../vendor/doctrine/instantiator/LICENSE | 19 + .../vendor/doctrine/instantiator/README.md | 40 + .../doctrine/instantiator/composer.json | 45 + .../doctrine/instantiator/phpmd.xml.dist | 27 + .../doctrine/instantiator/phpunit.xml.dist | 22 + .../Exception/ExceptionInterface.php | 29 + .../Exception/InvalidArgumentException.php | 62 + .../Exception/UnexpectedValueException.php | 79 + .../Doctrine/Instantiator/Instantiator.php | 273 ++ .../Instantiator/InstantiatorInterface.php | 37 + .../InstantiatorPerformanceEvent.php | 96 + .../InvalidArgumentExceptionTest.php | 83 + .../UnexpectedValueExceptionTest.php | 69 + .../InstantiatorTest/InstantiatorTest.php | 219 + .../AbstractClassAsset.php | 29 + .../ArrayObjectAsset.php | 41 + .../InstantiatorTestAsset/ExceptionAsset.php | 41 + .../FinalExceptionAsset.php | 41 + .../InstantiatorTestAsset/PharAsset.php | 41 + .../PharExceptionAsset.php | 44 + .../SerializableArrayObjectAsset.php | 62 + .../SimpleSerializableAsset.php | 61 + .../SimpleTraitAsset.php | 29 + .../UnCloneableAsset.php | 50 + .../UnserializeExceptionArrayObjectAsset.php | 39 + .../WakeUpNoticesAsset.php | 38 + .../InstantiatorTestAsset/XMLReaderAsset.php | 41 + .../vendor/guzzle/guzzle/.gitignore | 27 + .../vendor/guzzle/guzzle/.travis.yml | 17 + .../vendor/guzzle/guzzle/CHANGELOG.md | 751 ++++ .../vendor/guzzle/guzzle/LICENSE | 19 + .../vendor/guzzle/guzzle/README.md | 57 + .../vendor/guzzle/guzzle/UPGRADING.md | 537 +++ .../vendor/guzzle/guzzle/build.xml | 45 + .../vendor/guzzle/guzzle/composer.json | 82 + .../vendor/guzzle/guzzle/docs/Makefile | 153 + .../docs/_downloads/guzzle-schema-1.0.json | 176 + .../guzzle/docs/_static/guzzle-icon.png | Bin 0 -> 803 bytes .../guzzle/guzzle/docs/_static/homepage.css | 122 + .../guzzle/guzzle/docs/_static/logo.png | Bin 0 -> 247678 bytes .../guzzle/guzzle/docs/_static/prettify.css | 41 + .../guzzle/guzzle/docs/_static/prettify.js | 28 + .../guzzle/guzzle/docs/_templates/index.html | 106 + .../guzzle/docs/_templates/leftbar.html | 0 .../guzzle/docs/_templates/nav_links.html | 5 + .../guzzle/guzzle/docs/batching/batching.rst | 183 + .../vendor/guzzle/guzzle/docs/conf.py | 94 + .../vendor/guzzle/guzzle/docs/docs.rst | 73 + .../guzzle/docs/getting-started/faq.rst | 29 + .../docs/getting-started/installation.rst | 154 + .../guzzle/docs/getting-started/overview.rst | 85 + .../guzzle/guzzle/docs/http-client/client.rst | 569 +++ .../guzzle/docs/http-client/entity-bodies.rst | 151 + .../docs/http-client/http-redirects.rst | 99 + .../guzzle/docs/http-client/request.rst | 667 +++ .../guzzle/docs/http-client/response.rst | 141 + .../guzzle/docs/http-client/uri-templates.rst | 52 + .../vendor/guzzle/guzzle/docs/index.rst | 5 + .../docs/iterators/guzzle-iterators.rst | 97 + .../docs/iterators/resource-iterators.rst | 149 + .../guzzle/docs/plugins/async-plugin.rst | 18 + .../guzzle/docs/plugins/backoff-plugin.rst | 22 + .../guzzle/docs/plugins/cache-plugin.rst | 169 + .../guzzle/docs/plugins/cookie-plugin.rst | 33 + .../guzzle/docs/plugins/creating-plugins.rst | 93 + .../guzzle/docs/plugins/curl-auth-plugin.rst | 32 + .../guzzle/docs/plugins/history-plugin.rst | 24 + .../guzzle/guzzle/docs/plugins/log-plugin.rst | 69 + .../docs/plugins/md5-validator-plugin.rst | 29 + .../guzzle/docs/plugins/mock-plugin.rst | 27 + .../guzzle/docs/plugins/oauth-plugin.rst | 30 + .../guzzle/docs/plugins/plugins-list.rst.inc | 9 + .../guzzle/docs/plugins/plugins-overview.rst | 59 + .../guzzle/guzzle/docs/requirements.txt | 2 + .../guzzle/docs/testing/unit-testing.rst | 201 + .../guzzle-service-descriptions.rst | 619 +++ .../using-the-service-builder.rst | 316 ++ .../webservice-client/webservice-client.rst | 659 +++ .../vendor/guzzle/guzzle/phar-stub.php | 16 + .../guzzle/guzzle/phing/build.properties.dist | 16 + .../guzzle/phing/imports/dependencies.xml | 33 + .../guzzle/guzzle/phing/imports/deploy.xml | 142 + .../guzzle/phing/tasks/ComposerLintTask.php | 152 + .../phing/tasks/GuzzlePearPharPackageTask.php | 338 ++ .../guzzle/phing/tasks/GuzzleSubSplitTask.php | 385 ++ .../vendor/guzzle/guzzle/phpunit.xml.dist | 48 + .../Guzzle/Batch/AbstractBatchDecorator.php | 66 + .../guzzle/guzzle/src/Guzzle/Batch/Batch.php | 92 + .../guzzle/src/Guzzle/Batch/BatchBuilder.php | 199 + .../src/Guzzle/Batch/BatchClosureDivisor.php | 39 + .../src/Guzzle/Batch/BatchClosureTransfer.php | 40 + .../src/Guzzle/Batch/BatchCommandTransfer.php | 75 + .../Guzzle/Batch/BatchDivisorInterface.php | 18 + .../src/Guzzle/Batch/BatchInterface.php | 32 + .../src/Guzzle/Batch/BatchRequestTransfer.php | 65 + .../src/Guzzle/Batch/BatchSizeDivisor.php | 47 + .../Guzzle/Batch/BatchTransferInterface.php | 16 + .../Exception/BatchTransferException.php | 90 + .../Guzzle/Batch/ExceptionBufferingBatch.php | 50 + .../guzzle/src/Guzzle/Batch/FlushingBatch.php | 60 + .../guzzle/src/Guzzle/Batch/HistoryBatch.php | 39 + .../src/Guzzle/Batch/NotifyingBatch.php | 38 + .../guzzle/src/Guzzle/Batch/composer.json | 31 + .../src/Guzzle/Cache/AbstractCacheAdapter.php | 21 + .../src/Guzzle/Cache/CacheAdapterFactory.php | 117 + .../Guzzle/Cache/CacheAdapterInterface.php | 55 + .../src/Guzzle/Cache/ClosureCacheAdapter.php | 57 + .../src/Guzzle/Cache/DoctrineCacheAdapter.php | 41 + .../src/Guzzle/Cache/NullCacheAdapter.php | 31 + .../src/Guzzle/Cache/Zf1CacheAdapter.php | 44 + .../src/Guzzle/Cache/Zf2CacheAdapter.php | 41 + .../guzzle/src/Guzzle/Cache/composer.json | 27 + .../Guzzle/Common/AbstractHasDispatcher.php | 49 + .../guzzle/src/Guzzle/Common/Collection.php | 403 ++ .../guzzle/guzzle/src/Guzzle/Common/Event.php | 52 + .../Exception/BadMethodCallException.php | 5 + .../Common/Exception/ExceptionCollection.php | 108 + .../Common/Exception/GuzzleException.php | 8 + .../Exception/InvalidArgumentException.php | 5 + .../Common/Exception/RuntimeException.php | 5 + .../Exception/UnexpectedValueException.php | 5 + .../src/Guzzle/Common/FromConfigInterface.php | 18 + .../Guzzle/Common/HasDispatcherInterface.php | 54 + .../src/Guzzle/Common/ToArrayInterface.php | 16 + .../guzzle/src/Guzzle/Common/Version.php | 29 + .../guzzle/src/Guzzle/Common/composer.json | 20 + .../Http/AbstractEntityBodyDecorator.php | 221 + .../src/Guzzle/Http/CachingEntityBody.php | 229 + .../guzzle/guzzle/src/Guzzle/Http/Client.php | 524 +++ .../src/Guzzle/Http/ClientInterface.php | 223 + .../src/Guzzle/Http/Curl/CurlHandle.php | 464 ++ .../guzzle/src/Guzzle/Http/Curl/CurlMulti.php | 423 ++ .../Guzzle/Http/Curl/CurlMultiInterface.php | 58 + .../src/Guzzle/Http/Curl/CurlMultiProxy.php | 150 + .../src/Guzzle/Http/Curl/CurlVersion.php | 66 + .../src/Guzzle/Http/Curl/RequestMediator.php | 147 + .../guzzle/src/Guzzle/Http/EntityBody.php | 201 + .../src/Guzzle/Http/EntityBodyInterface.php | 73 + .../Http/Exception/BadResponseException.php | 69 + .../ClientErrorResponseException.php | 8 + .../CouldNotRewindStreamException.php | 7 + .../Guzzle/Http/Exception/CurlException.php | 101 + .../Guzzle/Http/Exception/HttpException.php | 10 + .../Http/Exception/MultiTransferException.php | 145 + .../Http/Exception/RequestException.php | 39 + .../ServerErrorResponseException.php | 8 + .../Exception/TooManyRedirectsException.php | 5 + .../src/Guzzle/Http/IoEmittingEntityBody.php | 83 + .../Guzzle/Http/Message/AbstractMessage.php | 220 + .../Http/Message/EntityEnclosingRequest.php | 247 ++ .../EntityEnclosingRequestInterface.php | 137 + .../guzzle/src/Guzzle/Http/Message/Header.php | 182 + .../Http/Message/Header/CacheControl.php | 121 + .../Http/Message/Header/HeaderCollection.php | 108 + .../Http/Message/Header/HeaderFactory.php | 26 + .../Message/Header/HeaderFactoryInterface.php | 19 + .../Http/Message/Header/HeaderInterface.php | 83 + .../src/Guzzle/Http/Message/Header/Link.php | 93 + .../Guzzle/Http/Message/MessageInterface.php | 102 + .../src/Guzzle/Http/Message/PostFile.php | 124 + .../Guzzle/Http/Message/PostFileInterface.php | 83 + .../src/Guzzle/Http/Message/Request.php | 638 +++ .../Guzzle/Http/Message/RequestFactory.php | 359 ++ .../Http/Message/RequestFactoryInterface.php | 105 + .../Guzzle/Http/Message/RequestInterface.php | 318 ++ .../src/Guzzle/Http/Message/Response.php | 968 +++++ .../guzzle/src/Guzzle/Http/Mimetypes.php | 962 ++++ .../Http/QueryAggregator/CommaAggregator.php | 20 + .../QueryAggregator/DuplicateAggregator.php | 22 + .../Http/QueryAggregator/PhpAggregator.php | 27 + .../QueryAggregatorInterface.php | 22 + .../guzzle/src/Guzzle/Http/QueryString.php | 297 ++ .../src/Guzzle/Http/ReadLimitEntityBody.php | 122 + .../guzzle/src/Guzzle/Http/RedirectPlugin.php | 250 ++ .../src/Guzzle/Http/Resources/cacert.pem | 3870 +++++++++++++++++ .../guzzle/src/Guzzle/Http/StaticClient.php | 157 + .../guzzle/guzzle/src/Guzzle/Http/Url.php | 554 +++ .../guzzle/src/Guzzle/Http/composer.json | 32 + .../src/Guzzle/Inflection/Inflector.php | 38 + .../Guzzle/Inflection/InflectorInterface.php | 27 + .../Guzzle/Inflection/MemoizingInflector.php | 70 + .../Inflection/PreComputedInflector.php | 59 + .../src/Guzzle/Inflection/composer.json | 26 + .../src/Guzzle/Iterator/AppendIterator.php | 19 + .../src/Guzzle/Iterator/ChunkedIterator.php | 56 + .../src/Guzzle/Iterator/FilterIterator.php | 36 + .../src/Guzzle/Iterator/MapIterator.php | 34 + .../Guzzle/Iterator/MethodProxyIterator.php | 27 + .../guzzle/src/Guzzle/Iterator/README.md | 25 + .../guzzle/src/Guzzle/Iterator/composer.json | 27 + .../src/Guzzle/Log/AbstractLogAdapter.php | 16 + .../guzzle/src/Guzzle/Log/ArrayLogAdapter.php | 34 + .../src/Guzzle/Log/ClosureLogAdapter.php | 23 + .../src/Guzzle/Log/LogAdapterInterface.php | 18 + .../src/Guzzle/Log/MessageFormatter.php | 179 + .../src/Guzzle/Log/MonologLogAdapter.php | 34 + .../guzzle/src/Guzzle/Log/PsrLogAdapter.php | 36 + .../guzzle/src/Guzzle/Log/Zf1LogAdapter.php | 24 + .../guzzle/src/Guzzle/Log/Zf2LogAdapter.php | 21 + .../guzzle/src/Guzzle/Log/composer.json | 29 + .../src/Guzzle/Parser/Cookie/CookieParser.php | 131 + .../Parser/Cookie/CookieParserInterface.php | 33 + .../Parser/Message/AbstractMessageParser.php | 58 + .../Guzzle/Parser/Message/MessageParser.php | 110 + .../Parser/Message/MessageParserInterface.php | 27 + .../Parser/Message/PeclHttpMessageParser.php | 48 + .../src/Guzzle/Parser/ParserRegistry.php | 75 + .../Parser/UriTemplate/PeclUriTemplate.php | 26 + .../Guzzle/Parser/UriTemplate/UriTemplate.php | 254 ++ .../UriTemplate/UriTemplateInterface.php | 21 + .../src/Guzzle/Parser/Url/UrlParser.php | 48 + .../Guzzle/Parser/Url/UrlParserInterface.php | 19 + .../guzzle/src/Guzzle/Parser/composer.json | 19 + .../src/Guzzle/Plugin/Async/AsyncPlugin.php | 84 + .../src/Guzzle/Plugin/Async/composer.json | 27 + .../Backoff/AbstractBackoffStrategy.php | 91 + .../AbstractErrorCodeBackoffStrategy.php | 40 + .../Guzzle/Plugin/Backoff/BackoffLogger.php | 76 + .../Guzzle/Plugin/Backoff/BackoffPlugin.php | 126 + .../Backoff/BackoffStrategyInterface.php | 30 + .../Backoff/CallbackBackoffStrategy.php | 47 + .../Backoff/ConstantBackoffStrategy.php | 34 + .../Plugin/Backoff/CurlBackoffStrategy.php | 28 + .../Backoff/ExponentialBackoffStrategy.php | 25 + .../Plugin/Backoff/HttpBackoffStrategy.php | 30 + .../Plugin/Backoff/LinearBackoffStrategy.php | 36 + .../Backoff/ReasonPhraseBackoffStrategy.php | 25 + .../Backoff/TruncatedBackoffStrategy.php | 36 + .../src/Guzzle/Plugin/Backoff/composer.json | 28 + .../Cache/CacheKeyProviderInterface.php | 11 + .../src/Guzzle/Plugin/Cache/CachePlugin.php | 353 ++ .../Plugin/Cache/CacheStorageInterface.php | 43 + .../Plugin/Cache/CallbackCanCacheStrategy.php | 53 + .../Cache/CanCacheStrategyInterface.php | 30 + .../Plugin/Cache/DefaultCacheKeyProvider.php | 46 + .../Plugin/Cache/DefaultCacheStorage.php | 266 ++ .../Plugin/Cache/DefaultCanCacheStrategy.php | 32 + .../Plugin/Cache/DefaultRevalidation.php | 174 + .../Guzzle/Plugin/Cache/DenyRevalidation.php | 19 + .../Plugin/Cache/RevalidationInterface.php | 32 + .../Guzzle/Plugin/Cache/SkipRevalidation.php | 19 + .../src/Guzzle/Plugin/Cache/composer.json | 28 + .../src/Guzzle/Plugin/Cookie/Cookie.php | 538 +++ .../Cookie/CookieJar/ArrayCookieJar.php | 237 + .../Cookie/CookieJar/CookieJarInterface.php | 85 + .../Plugin/Cookie/CookieJar/FileCookieJar.php | 65 + .../src/Guzzle/Plugin/Cookie/CookiePlugin.php | 70 + .../Exception/InvalidCookieException.php | 7 + .../src/Guzzle/Plugin/Cookie/composer.json | 27 + .../Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php | 46 + .../src/Guzzle/Plugin/CurlAuth/composer.json | 27 + .../ErrorResponseExceptionInterface.php | 22 + .../ErrorResponse/ErrorResponsePlugin.php | 72 + .../Exception/ErrorResponseException.php | 7 + .../Guzzle/Plugin/ErrorResponse/composer.json | 27 + .../Guzzle/Plugin/History/HistoryPlugin.php | 163 + .../src/Guzzle/Plugin/History/composer.json | 27 + .../src/Guzzle/Plugin/Log/LogPlugin.php | 161 + .../src/Guzzle/Plugin/Log/composer.json | 28 + .../Plugin/Md5/CommandContentMd5Plugin.php | 57 + .../Guzzle/Plugin/Md5/Md5ValidatorPlugin.php | 88 + .../src/Guzzle/Plugin/Md5/composer.json | 27 + .../src/Guzzle/Plugin/Mock/MockPlugin.php | 245 ++ .../src/Guzzle/Plugin/Mock/composer.json | 27 + .../src/Guzzle/Plugin/Oauth/OauthPlugin.php | 306 ++ .../src/Guzzle/Plugin/Oauth/composer.json | 27 + .../guzzle/src/Guzzle/Plugin/composer.json | 44 + .../Guzzle/Service/AbstractConfigLoader.php | 177 + .../Guzzle/Service/Builder/ServiceBuilder.php | 189 + .../Builder/ServiceBuilderInterface.php | 40 + .../Service/Builder/ServiceBuilderLoader.php | 89 + .../Guzzle/Service/CachingConfigLoader.php | 46 + .../guzzle/src/Guzzle/Service/Client.php | 297 ++ .../src/Guzzle/Service/ClientInterface.php | 68 + .../Service/Command/AbstractCommand.php | 390 ++ .../Guzzle/Service/Command/ClosureCommand.php | 41 + .../Service/Command/CommandInterface.php | 128 + .../Command/CreateResponseClassEvent.php | 32 + .../Command/DefaultRequestSerializer.php | 169 + .../Service/Command/DefaultResponseParser.php | 55 + .../Service/Command/Factory/AliasFactory.php | 39 + .../Command/Factory/CompositeFactory.php | 154 + .../Command/Factory/ConcreteClassFactory.php | 47 + .../Command/Factory/FactoryInterface.php | 21 + .../Service/Command/Factory/MapFactory.php | 27 + .../Factory/ServiceDescriptionFactory.php | 71 + .../Request/AbstractRequestVisitor.php | 69 + .../LocationVisitor/Request/BodyVisitor.php | 58 + .../LocationVisitor/Request/HeaderVisitor.php | 44 + .../LocationVisitor/Request/JsonVisitor.php | 63 + .../Request/PostFieldVisitor.php | 18 + .../Request/PostFileVisitor.php | 24 + .../LocationVisitor/Request/QueryVisitor.php | 18 + .../Request/RequestVisitorInterface.php | 31 + .../Request/ResponseBodyVisitor.php | 18 + .../LocationVisitor/Request/XmlVisitor.php | 252 ++ .../Response/AbstractResponseVisitor.php | 26 + .../LocationVisitor/Response/BodyVisitor.php | 23 + .../Response/HeaderVisitor.php | 50 + .../LocationVisitor/Response/JsonVisitor.php | 93 + .../Response/ReasonPhraseVisitor.php | 23 + .../Response/ResponseVisitorInterface.php | 46 + .../Response/StatusCodeVisitor.php | 23 + .../LocationVisitor/Response/XmlVisitor.php | 151 + .../LocationVisitor/VisitorFlyweight.php | 138 + .../Service/Command/OperationCommand.php | 89 + .../Command/OperationResponseParser.php | 195 + .../Command/RequestSerializerInterface.php | 21 + .../Command/ResponseClassInterface.php | 18 + .../Command/ResponseParserInterface.php | 18 + .../Guzzle/Service/ConfigLoaderInterface.php | 22 + .../Guzzle/Service/Description/Operation.php | 547 +++ .../Description/OperationInterface.php | 159 + .../Guzzle/Service/Description/Parameter.php | 925 ++++ .../Service/Description/SchemaFormatter.php | 156 + .../Service/Description/SchemaValidator.php | 291 ++ .../Description/ServiceDescription.php | 271 ++ .../ServiceDescriptionInterface.php | 106 + .../Description/ServiceDescriptionLoader.php | 64 + .../Description/ValidatorInterface.php | 28 + .../Service/Exception/CommandException.php | 7 + .../Exception/CommandTransferException.php | 119 + .../Exception/DescriptionBuilderException.php | 7 + .../InconsistentClientTransferException.php | 38 + .../Exception/ResponseClassException.php | 9 + .../Exception/ServiceBuilderException.php | 7 + .../Exception/ServiceNotFoundException.php | 5 + .../Service/Exception/ValidationException.php | 30 + .../AbstractResourceIteratorFactory.php | 37 + .../CompositeResourceIteratorFactory.php | 67 + .../Resource/MapResourceIteratorFactory.php | 34 + .../src/Guzzle/Service/Resource/Model.php | 64 + .../Service/Resource/ResourceIterator.php | 254 ++ .../Resource/ResourceIteratorApplyBatched.php | 111 + .../Resource/ResourceIteratorClassFactory.php | 60 + .../ResourceIteratorFactoryInterface.php | 30 + .../Resource/ResourceIteratorInterface.php | 61 + .../guzzle/src/Guzzle/Service/composer.json | 29 + .../Guzzle/Stream/PhpStreamRequestFactory.php | 284 ++ .../guzzle/src/Guzzle/Stream/Stream.php | 289 ++ .../src/Guzzle/Stream/StreamInterface.php | 218 + .../Stream/StreamRequestFactoryInterface.php | 24 + .../guzzle/src/Guzzle/Stream/composer.json | 30 + .../Batch/AbstractBatchDecoratorTest.php | 33 + .../Guzzle/Tests/Batch/BatchBuilderTest.php | 86 + .../Tests/Batch/BatchClosureDivisorTest.php | 36 + .../Tests/Batch/BatchClosureTransferTest.php | 52 + .../Tests/Batch/BatchCommandTransferTest.php | 83 + .../Tests/Batch/BatchRequestTransferTest.php | 80 + .../Tests/Batch/BatchSizeDivisorTest.php | 24 + .../tests/Guzzle/Tests/Batch/BatchTest.php | 91 + .../Batch/ExceptionBufferingBatchTest.php | 45 + .../Guzzle/Tests/Batch/FlushingBatchTest.php | 40 + .../Guzzle/Tests/Batch/HistoryBatchTest.php | 26 + .../Guzzle/Tests/Batch/NotifyingBatchTest.php | 45 + .../Tests/Cache/CacheAdapterFactoryTest.php | 64 + .../Guzzle/Tests/Cache/CacheAdapterTest.php | 68 + .../Tests/Cache/ClosureCacheAdapterTest.php | 94 + .../Tests/Cache/NullCacheAdapterTest.php | 20 + .../Tests/Cache/Zf2CacheAdapterTest.php | 58 + .../Common/AbstractHasDispatcherTest.php | 63 + .../Guzzle/Tests/Common/CollectionTest.php | 529 +++ .../tests/Guzzle/Tests/Common/EventTest.php | 62 + .../Exception/BatchTransferExceptionTest.php | 21 + .../Exception/ExceptionCollectionTest.php | 66 + .../tests/Guzzle/Tests/Common/VersionTest.php | 27 + .../tests/Guzzle/Tests/GuzzleTestCase.php | 235 + .../Http/AbstractEntityBodyDecoratorTest.php | 34 + .../Tests/Http/CachingEntityBodyTest.php | 249 ++ .../tests/Guzzle/Tests/Http/ClientTest.php | 601 +++ .../Guzzle/Tests/Http/Curl/CurlHandleTest.php | 947 ++++ .../Tests/Http/Curl/CurlMultiProxyTest.php | 110 + .../Guzzle/Tests/Http/Curl/CurlMultiTest.php | 455 ++ .../Tests/Http/Curl/CurlVersionTest.php | 39 + .../Tests/Http/Curl/RequestMediatorTest.php | 67 + .../Guzzle/Tests/Http/EntityBodyTest.php | 182 + .../Http/Exception/CurlExceptionTest.php | 27 + .../Tests/Http/Exception/ExceptionTest.php | 66 + .../Exception/MultiTransferExceptionTest.php | 51 + .../Tests/Http/IoEmittingEntityBodyTest.php | 47 + .../Http/Message/AbstractMessageTest.php | 136 + .../Message/EntityEnclosingRequestTest.php | 434 ++ .../Http/Message/Header/HeaderFactoryTest.php | 29 + .../Tests/Http/Message/Header/LinkTest.php | 63 + .../Tests/Http/Message/HeaderComparison.php | 135 + .../Http/Message/HeaderComparisonTest.php | 115 + .../Guzzle/Tests/Http/Message/HeaderTest.php | 162 + .../Tests/Http/Message/PostFileTest.php | 88 + .../Tests/Http/Message/RequestFactoryTest.php | 616 +++ .../Guzzle/Tests/Http/Message/RequestTest.php | 639 +++ .../Tests/Http/Message/ResponseTest.php | 677 +++ .../tests/Guzzle/Tests/Http/MimetypesTest.php | 31 + .../QueryAggregator/CommaAggregatorTest.php | 30 + .../DuplicateAggregatorTest.php | 30 + .../QueryAggregator/PhpAggregatorTest.php | 32 + .../Guzzle/Tests/Http/QueryStringTest.php | 233 + .../Tests/Http/ReadLimitEntityBodyTest.php | 81 + .../Guzzle/Tests/Http/RedirectPluginTest.php | 277 ++ .../guzzle/tests/Guzzle/Tests/Http/Server.php | 191 + .../Guzzle/Tests/Http/StaticClientTest.php | 67 + .../tests/Guzzle/Tests/Http/UrlTest.php | 303 ++ .../guzzle/tests/Guzzle/Tests/Http/server.js | 146 + .../Guzzle/Tests/Inflection/InflectorTest.php | 37 + .../Inflection/MemoizingInflectorTest.php | 46 + .../Inflection/PreComputedInflectorTest.php | 45 + .../Tests/Iterator/AppendIteratorTest.php | 29 + .../Tests/Iterator/ChunkedIteratorTest.php | 52 + .../Tests/Iterator/FilterIteratorTest.php | 28 + .../Guzzle/Tests/Iterator/MapIteratorTest.php | 28 + .../Iterator/MethodProxyIteratorTest.php | 28 + .../Guzzle/Tests/Log/ArrayLogAdapterTest.php | 23 + .../Tests/Log/ClosureLogAdapterTest.php | 30 + .../Guzzle/Tests/Log/MessageFormatterTest.php | 143 + .../Guzzle/Tests/Log/PsrLogAdapterTest.php | 25 + .../Guzzle/Tests/Log/Zf2LogAdapterTest.php | 51 + .../Guzzle/Tests/Mock/CustomResponseModel.php | 21 + .../Guzzle/Tests/Mock/ErrorResponseMock.php | 25 + .../tests/Guzzle/Tests/Mock/ExceptionMock.php | 11 + .../tests/Guzzle/Tests/Mock/MockMulti.php | 11 + .../tests/Guzzle/Tests/Mock/MockObserver.php | 65 + .../tests/Guzzle/Tests/Mock/MockSubject.php | 7 + .../Parser/Cookie/CookieParserProvider.php | 381 ++ .../Tests/Parser/Cookie/CookieParserTest.php | 22 + .../Parser/Message/MessageParserProvider.php | 225 + .../Parser/Message/MessageParserTest.php | 58 + .../Message/PeclHttpMessageParserTest.php | 36 + .../Tests/Parser/ParserRegistryTest.php | 33 + .../UriTemplate/AbstractUriTemplateTest.php | 113 + .../UriTemplate/PeclUriTemplateTest.php | 27 + .../Parser/UriTemplate/UriTemplateTest.php | 106 + .../Tests/Plugin/Async/AsyncPluginTest.php | 93 + .../Backoff/AbstractBackoffStrategyTest.php | 86 + .../Plugin/Backoff/BackoffLoggerTest.php | 110 + .../Plugin/Backoff/BackoffPluginTest.php | 297 ++ .../Backoff/CallbackBackoffStrategyTest.php | 31 + .../Backoff/ConstantBackoffStrategyTest.php | 20 + .../Backoff/CurlBackoffStrategyTest.php | 36 + .../ExponentialBackoffStrategyTest.php | 23 + .../Backoff/HttpBackoffStrategyTest.php | 47 + .../Backoff/LinearBackoffStrategyTest.php | 21 + .../ReasonPhraseBackoffStrategyTest.php | 32 + .../Backoff/TruncatedBackoffStrategyTest.php | 30 + .../Tests/Plugin/Cache/CachePluginTest.php | 441 ++ .../Cache/CallbackCanCacheStrategyTest.php | 72 + .../Plugin/Cache/DefaultCacheStorageTest.php | 193 + .../Cache/DefaultCanCacheStrategyTest.php | 40 + .../Plugin/Cache/DefaultRevalidationTest.php | 248 ++ .../Plugin/Cache/DenyRevalidationTest.php | 19 + .../Plugin/Cache/SkipRevalidationTest.php | 19 + .../Cookie/CookieJar/ArrayCookieJarTest.php | 385 ++ .../Cookie/CookieJar/FileCookieJarTest.php | 63 + .../Tests/Plugin/Cookie/CookiePluginTest.php | 134 + .../Guzzle/Tests/Plugin/Cookie/CookieTest.php | 223 + .../Plugin/CurlAuth/CurlAuthPluginTest.php | 39 + .../ErrorResponse/ErrorResponsePluginTest.php | 137 + .../Plugin/History/HistoryPluginTest.php | 140 + .../Guzzle/Tests/Plugin/Log/LogPluginTest.php | 95 + .../Md5/CommandContentMd5PluginTest.php | 97 + .../Plugin/Md5/Md5ValidatorPluginTest.php | 120 + .../Tests/Plugin/Mock/MockPluginTest.php | 199 + .../Tests/Plugin/Oauth/OauthPluginTest.php | 345 ++ .../Service/AbstractConfigLoaderTest.php | 149 + .../Builder/ServiceBuilderLoaderTest.php | 177 + .../Service/Builder/ServiceBuilderTest.php | 317 ++ .../Tests/Service/CachingConfigLoaderTest.php | 43 + .../tests/Guzzle/Tests/Service/ClientTest.php | 320 ++ .../Service/Command/AbstractCommandTest.php | 16 + .../Service/Command/ClosureCommandTest.php | 54 + .../Tests/Service/Command/CommandTest.php | 445 ++ .../Command/DefaultRequestSerializerTest.php | 122 + .../Command/DefaultResponseParserTest.php | 59 + .../Command/Factory/AliasFactoryTest.php | 76 + .../Command/Factory/CompositeFactoryTest.php | 124 + .../Factory/ConcreteClassFactoryTest.php | 49 + .../Command/Factory/MapFactoryTest.php | 37 + .../Factory/ServiceDescriptionFactoryTest.php | 68 + .../Request/AbstractVisitorTestCase.php | 110 + .../Request/BodyVisitorTest.php | 63 + .../Request/HeaderVisitorTest.php | 48 + .../Request/JsonVisitorTest.php | 60 + .../Request/PostFieldVisitorTest.php | 33 + .../Request/PostFileVisitorTest.php | 54 + .../Request/QueryVisitorTest.php | 48 + .../Request/ResponseBodyVisitorTest.php | 20 + .../Request/XmlVisitorTest.php | 558 +++ .../Response/AbstractResponseVisitorTest.php | 29 + .../Response/BodyVisitorTest.php | 21 + .../Response/HeaderVisitorTest.php | 98 + .../Response/JsonVisitorTest.php | 157 + .../Response/ReasonPhraseVisitorTest.php | 21 + .../Response/StatusCodeVisitorTest.php | 21 + .../Response/XmlVisitorTest.php | 431 ++ .../LocationVisitor/VisitorFlyweightTest.php | 53 + .../Service/Command/OperationCommandTest.php | 102 + .../Command/OperationResponseParserTest.php | 335 ++ .../Service/Description/OperationTest.php | 308 ++ .../Service/Description/ParameterTest.php | 411 ++ .../Description/SchemaFormatterTest.php | 61 + .../Description/SchemaValidatorTest.php | 326 ++ .../ServiceDescriptionLoaderTest.php | 177 + .../Description/ServiceDescriptionTest.php | 240 + .../CommandTransferExceptionTest.php | 66 + ...nconsistentClientTransferExceptionTest.php | 15 + .../Exception/ValidationExceptionTest.php | 17 + .../Service/Mock/Command/IterableCommand.php | 31 + .../Service/Mock/Command/MockCommand.php | 32 + .../Service/Mock/Command/OtherCommand.php | 30 + .../Tests/Service/Mock/Command/Sub/Sub.php | 7 + .../Guzzle/Tests/Service/Mock/MockClient.php | 36 + .../Mock/Model/MockCommandIterator.php | 42 + .../CompositeResourceIteratorFactoryTest.php | 37 + .../MapResourceIteratorFactoryTest.php | 40 + .../Tests/Service/Resource/ModelTest.php | 65 + .../ResourceIteratorClassFactoryTest.php | 41 + .../Service/Resource/ResourceIteratorTest.php | 184 + .../Stream/PhpStreamRequestFactoryTest.php | 172 + .../tests/Guzzle/Tests/Stream/StreamTest.php | 189 + .../tests/Guzzle/Tests/TestData/FileBody.txt | 0 .../Tests/TestData/description/bar.json | 3 + .../Tests/TestData/description/baz.json | 3 + .../Tests/TestData/description/foo.json | 8 + .../Tests/TestData/description/recursive.json | 3 + .../tests/Guzzle/Tests/TestData/mock_response | 3 + .../Guzzle/Tests/TestData/services/json1.json | 18 + .../Guzzle/Tests/TestData/services/json2.json | 11 + .../Tests/TestData/services/services.json | 71 + .../Guzzle/Tests/TestData/test_service.json | 40 + .../Guzzle/Tests/TestData/test_service2.json | 7 + .../Guzzle/Tests/TestData/test_service_3.json | 40 + .../vendor/guzzle/guzzle/tests/bootstrap.php | 10 + .../vendor/guzzlehttp/guzzle/.editorconfig | 11 + .../vendor/guzzlehttp/guzzle/.gitignore | 11 + .../vendor/guzzlehttp/guzzle/.travis.yml | 41 + .../vendor/guzzlehttp/guzzle/CHANGELOG.md | 1053 +++++ .../vendor/guzzlehttp/guzzle/LICENSE | 19 + .../vendor/guzzlehttp/guzzle/Makefile | 50 + .../vendor/guzzlehttp/guzzle/README.md | 70 + .../vendor/guzzlehttp/guzzle/UPGRADING.md | 1050 +++++ .../guzzlehttp/guzzle/build/packager.php | 21 + .../vendor/guzzlehttp/guzzle/composer.json | 39 + .../vendor/guzzlehttp/guzzle/docs/Makefile | 153 + .../guzzle/docs/_static/guzzle-icon.png | Bin 0 -> 803 bytes .../guzzlehttp/guzzle/docs/_static/logo.png | Bin 0 -> 247678 bytes .../guzzle/docs/_templates/nav_links.html | 3 + .../vendor/guzzlehttp/guzzle/docs/clients.rst | 1326 ++++++ .../vendor/guzzlehttp/guzzle/docs/conf.py | 28 + .../vendor/guzzlehttp/guzzle/docs/events.rst | 516 +++ .../vendor/guzzlehttp/guzzle/docs/faq.rst | 199 + .../guzzlehttp/guzzle/docs/handlers.rst | 43 + .../guzzlehttp/guzzle/docs/http-messages.rst | 483 ++ .../vendor/guzzlehttp/guzzle/docs/index.rst | 98 + .../guzzlehttp/guzzle/docs/overview.rst | 150 + .../guzzlehttp/guzzle/docs/quickstart.rst | 448 ++ .../guzzlehttp/guzzle/docs/requirements.txt | 2 + .../vendor/guzzlehttp/guzzle/docs/streams.rst | 213 + .../vendor/guzzlehttp/guzzle/docs/testing.rst | 232 + .../vendor/guzzlehttp/guzzle/phpunit.xml.dist | 17 + .../guzzlehttp/guzzle/src/BatchResults.php | 148 + .../vendor/guzzlehttp/guzzle/src/Client.php | 352 ++ .../guzzlehttp/guzzle/src/ClientInterface.php | 150 + .../guzzlehttp/guzzle/src/Collection.php | 236 + .../guzzle/src/Cookie/CookieJar.php | 248 ++ .../guzzle/src/Cookie/CookieJarInterface.php | 75 + .../guzzle/src/Cookie/FileCookieJar.php | 86 + .../guzzle/src/Cookie/SessionCookieJar.php | 66 + .../guzzle/src/Cookie/SetCookie.php | 373 ++ .../guzzle/src/Event/AbstractEvent.php | 20 + .../guzzle/src/Event/AbstractRequestEvent.php | 61 + .../src/Event/AbstractRetryableEvent.php | 40 + .../src/Event/AbstractTransferEvent.php | 63 + .../guzzle/src/Event/BeforeEvent.php | 26 + .../guzzle/src/Event/CompleteEvent.php | 14 + .../guzzlehttp/guzzle/src/Event/Emitter.php | 146 + .../guzzle/src/Event/EmitterInterface.php | 96 + .../guzzlehttp/guzzle/src/Event/EndEvent.php | 28 + .../guzzle/src/Event/ErrorEvent.php | 27 + .../guzzle/src/Event/EventInterface.php | 23 + .../guzzle/src/Event/HasEmitterInterface.php | 15 + .../guzzle/src/Event/HasEmitterTrait.php | 20 + .../src/Event/ListenerAttacherTrait.php | 88 + .../guzzle/src/Event/ProgressEvent.php | 51 + .../guzzle/src/Event/RequestEvents.php | 56 + .../guzzle/src/Event/SubscriberInterface.php | 34 + .../src/Exception/BadResponseException.php | 7 + .../guzzle/src/Exception/ClientException.php | 7 + .../guzzle/src/Exception/ConnectException.php | 4 + .../CouldNotRewindStreamException.php | 4 + .../guzzle/src/Exception/ParseException.php | 31 + .../guzzle/src/Exception/RequestException.php | 121 + .../guzzle/src/Exception/ServerException.php | 7 + .../guzzle/src/Exception/StateException.php | 4 + .../Exception/TooManyRedirectsException.php | 4 + .../src/Exception/TransferException.php | 4 + .../src/Exception/XmlParseException.php | 34 + .../guzzlehttp/guzzle/src/HasDataTrait.php | 75 + .../guzzle/src/Message/AbstractMessage.php | 253 ++ .../src/Message/AppliesHeadersInterface.php | 24 + .../guzzle/src/Message/FutureResponse.php | 158 + .../guzzle/src/Message/MessageFactory.php | 364 ++ .../src/Message/MessageFactoryInterface.php | 71 + .../guzzle/src/Message/MessageInterface.php | 136 + .../guzzle/src/Message/MessageParser.php | 171 + .../guzzlehttp/guzzle/src/Message/Request.php | 195 + .../guzzle/src/Message/RequestInterface.php | 136 + .../guzzle/src/Message/Response.php | 208 + .../guzzle/src/Message/ResponseInterface.php | 111 + .../guzzlehttp/guzzle/src/Mimetypes.php | 963 ++++ .../vendor/guzzlehttp/guzzle/src/Pool.php | 333 ++ .../guzzle/src/Post/MultipartBody.php | 109 + .../guzzlehttp/guzzle/src/Post/PostBody.php | 287 ++ .../guzzle/src/Post/PostBodyInterface.php | 109 + .../guzzlehttp/guzzle/src/Post/PostFile.php | 135 + .../guzzle/src/Post/PostFileInterface.php | 41 + .../vendor/guzzlehttp/guzzle/src/Query.php | 204 + .../guzzlehttp/guzzle/src/QueryParser.php | 163 + .../guzzlehttp/guzzle/src/RequestFsm.php | 153 + .../guzzlehttp/guzzle/src/RingBridge.php | 165 + .../guzzle/src/Subscriber/Cookie.php | 58 + .../guzzle/src/Subscriber/History.php | 172 + .../guzzle/src/Subscriber/HttpError.php | 36 + .../guzzlehttp/guzzle/src/Subscriber/Mock.php | 147 + .../guzzle/src/Subscriber/Prepare.php | 130 + .../guzzle/src/Subscriber/Redirect.php | 176 + .../guzzle/src/ToArrayInterface.php | 15 + .../guzzlehttp/guzzle/src/Transaction.php | 103 + .../guzzlehttp/guzzle/src/UriTemplate.php | 241 + .../vendor/guzzlehttp/guzzle/src/Url.php | 595 +++ .../vendor/guzzlehttp/guzzle/src/Utils.php | 211 + .../guzzle/tests/BatchResultsTest.php | 58 + .../guzzlehttp/guzzle/tests/ClientTest.php | 647 +++ .../guzzle/tests/CollectionTest.php | 416 ++ .../guzzle/tests/Cookie/CookieJarTest.php | 339 ++ .../guzzle/tests/Cookie/FileCookieJarTest.php | 71 + .../tests/Cookie/SessionCookieJarTest.php | 76 + .../guzzle/tests/Cookie/SetCookieTest.php | 364 ++ .../guzzle/tests/Event/AbstractEventTest.php | 14 + .../tests/Event/AbstractRequestEventTest.php | 33 + .../Event/AbstractRetryableEventTest.php | 37 + .../tests/Event/AbstractTransferEventTest.php | 59 + .../guzzle/tests/Event/BeforeEventTest.php | 26 + .../guzzle/tests/Event/EmitterTest.php | 363 ++ .../guzzle/tests/Event/ErrorEventTest.php | 23 + .../tests/Event/HasEmitterTraitTest.php | 27 + .../tests/Event/ListenerAttacherTraitTest.php | 92 + .../guzzle/tests/Event/ProgressEventTest.php | 25 + .../guzzle/tests/Event/RequestEventsTest.php | 74 + .../tests/Exception/ParseExceptionTest.php | 20 + .../tests/Exception/RequestExceptionTest.php | 83 + .../tests/Exception/XmlParseExceptionTest.php | 19 + .../guzzle/tests/IntegrationTest.php | 123 + .../tests/Message/AbstractMessageTest.php | 269 ++ .../tests/Message/FutureResponseTest.php | 160 + .../tests/Message/MessageFactoryTest.php | 601 +++ .../tests/Message/MessageParserTest.php | 276 ++ .../guzzle/tests/Message/RequestTest.php | 144 + .../guzzle/tests/Message/ResponseTest.php | 120 + .../guzzlehttp/guzzle/tests/MimetypesTest.php | 31 + .../guzzlehttp/guzzle/tests/PoolTest.php | 319 ++ .../guzzle/tests/Post/MultipartBodyTest.php | 120 + .../guzzle/tests/Post/PostBodyTest.php | 255 ++ .../guzzle/tests/Post/PostFileTest.php | 61 + .../guzzle/tests/QueryParserTest.php | 80 + .../guzzlehttp/guzzle/tests/QueryTest.php | 171 + .../guzzle/tests/RequestFsmTest.php | 187 + .../guzzle/tests/RingBridgeTest.php | 195 + .../vendor/guzzlehttp/guzzle/tests/Server.php | 107 + .../guzzle/tests/Subscriber/CookieTest.php | 74 + .../guzzle/tests/Subscriber/HistoryTest.php | 140 + .../guzzle/tests/Subscriber/HttpErrorTest.php | 60 + .../guzzle/tests/Subscriber/MockTest.php | 225 + .../guzzle/tests/Subscriber/PrepareTest.php | 213 + .../guzzle/tests/Subscriber/RedirectTest.php | 302 ++ .../guzzle/tests/TransactionTest.php | 22 + .../guzzle/tests/UriTemplateTest.php | 202 + .../guzzlehttp/guzzle/tests/UrlTest.php | 364 ++ .../guzzlehttp/guzzle/tests/UtilsTest.php | 40 + .../guzzlehttp/guzzle/tests/bootstrap.php | 11 + .../vendor/guzzlehttp/guzzle/tests/perf.php | 61 + .../vendor/guzzlehttp/ringphp/.gitignore | 4 + .../vendor/guzzlehttp/ringphp/.travis.yml | 22 + .../vendor/guzzlehttp/ringphp/CHANGELOG.md | 54 + .../vendor/guzzlehttp/ringphp/LICENSE | 19 + .../vendor/guzzlehttp/ringphp/Makefile | 46 + .../vendor/guzzlehttp/ringphp/README.rst | 46 + .../vendor/guzzlehttp/ringphp/composer.json | 39 + .../vendor/guzzlehttp/ringphp/docs/Makefile | 153 + .../ringphp/docs/client_handlers.rst | 173 + .../ringphp/docs/client_middleware.rst | 165 + .../vendor/guzzlehttp/ringphp/docs/conf.py | 23 + .../guzzlehttp/ringphp/docs/futures.rst | 164 + .../vendor/guzzlehttp/ringphp/docs/index.rst | 50 + .../guzzlehttp/ringphp/docs/requirements.txt | 1 + .../vendor/guzzlehttp/ringphp/docs/spec.rst | 311 ++ .../guzzlehttp/ringphp/docs/testing.rst | 74 + .../guzzlehttp/ringphp/phpunit.xml.dist | 14 + .../ringphp/src/Client/ClientUtils.php | 74 + .../ringphp/src/Client/CurlFactory.php | 560 +++ .../ringphp/src/Client/CurlHandler.php | 135 + .../ringphp/src/Client/CurlMultiHandler.php | 250 ++ .../ringphp/src/Client/Middleware.php | 58 + .../ringphp/src/Client/MockHandler.php | 52 + .../ringphp/src/Client/StreamHandler.php | 414 ++ .../vendor/guzzlehttp/ringphp/src/Core.php | 364 ++ .../src/Exception/CancelledException.php | 7 + .../CancelledFutureAccessException.php | 4 + .../src/Exception/ConnectException.php | 7 + .../ringphp/src/Exception/RingException.php | 4 + .../ringphp/src/Future/BaseFutureTrait.php | 125 + .../src/Future/CompletedFutureArray.php | 43 + .../src/Future/CompletedFutureValue.php | 57 + .../ringphp/src/Future/FutureArray.php | 40 + .../src/Future/FutureArrayInterface.php | 11 + .../ringphp/src/Future/FutureInterface.php | 40 + .../ringphp/src/Future/FutureValue.php | 12 + .../ringphp/src/Future/MagicFutureTrait.php | 32 + .../ringphp/tests/Client/CurlFactoryTest.php | 821 ++++ .../ringphp/tests/Client/CurlHandlerTest.php | 96 + .../tests/Client/CurlMultiHandlerTest.php | 165 + .../ringphp/tests/Client/MiddlewareTest.php | 65 + .../ringphp/tests/Client/MockHandlerTest.php | 86 + .../ringphp/tests/Client/Server.php | 183 + .../tests/Client/StreamHandlerTest.php | 480 ++ .../guzzlehttp/ringphp/tests/Client/server.js | 241 + .../guzzlehttp/ringphp/tests/CoreTest.php | 336 ++ .../tests/Future/CompletedFutureArrayTest.php | 21 + .../tests/Future/CompletedFutureValueTest.php | 46 + .../ringphp/tests/Future/FutureArrayTest.php | 56 + .../ringphp/tests/Future/FutureValueTest.php | 109 + .../guzzlehttp/ringphp/tests/bootstrap.php | 11 + .../vendor/guzzlehttp/streams/.gitignore | 6 + .../vendor/guzzlehttp/streams/.travis.yml | 17 + .../vendor/guzzlehttp/streams/CHANGELOG.rst | 94 + .../vendor/guzzlehttp/streams/LICENSE | 19 + .../vendor/guzzlehttp/streams/Makefile | 19 + .../vendor/guzzlehttp/streams/README.rst | 36 + .../vendor/guzzlehttp/streams/composer.json | 28 + .../guzzlehttp/streams/phpunit.xml.dist | 17 + .../guzzlehttp/streams/src/AppendStream.php | 220 + .../streams/src/AsyncReadStream.php | 207 + .../guzzlehttp/streams/src/BufferStream.php | 138 + .../guzzlehttp/streams/src/CachingStream.php | 122 + .../guzzlehttp/streams/src/DroppingStream.php | 42 + .../src/Exception/CannotAttachException.php | 4 + .../streams/src/Exception/SeekException.php | 27 + .../guzzlehttp/streams/src/FnStream.php | 147 + .../streams/src/GuzzleStreamWrapper.php | 117 + .../guzzlehttp/streams/src/InflateStream.php | 27 + .../guzzlehttp/streams/src/LazyOpenStream.php | 37 + .../guzzlehttp/streams/src/LimitStream.php | 161 + .../streams/src/MetadataStreamInterface.php | 11 + .../guzzlehttp/streams/src/NoSeekStream.php | 25 + .../guzzlehttp/streams/src/NullStream.php | 78 + .../guzzlehttp/streams/src/PumpStream.php | 161 + .../vendor/guzzlehttp/streams/src/Stream.php | 261 ++ .../streams/src/StreamDecoratorTrait.php | 143 + .../streams/src/StreamInterface.php | 159 + .../vendor/guzzlehttp/streams/src/Utils.php | 196 + .../streams/tests/AppendStreamTest.php | 178 + .../streams/tests/AsyncReadStreamTest.php | 186 + .../streams/tests/BufferStreamTest.php | 69 + .../streams/tests/CachingStreamTest.php | 136 + .../streams/tests/DroppingStreamTest.php | 26 + .../tests/Exception/SeekExceptionTest.php | 16 + .../guzzlehttp/streams/tests/FnStreamTest.php | 89 + .../streams/tests/GuzzleStreamWrapperTest.php | 99 + .../streams/tests/InflateStreamTest.php | 16 + .../streams/tests/LazyOpenStreamTest.php | 64 + .../streams/tests/LimitStreamTest.php | 133 + .../streams/tests/NoSeekStreamTest.php | 41 + .../streams/tests/NullStreamTest.php | 39 + .../streams/tests/PumpStreamTest.php | 77 + .../tests/StreamDecoratorTraitTest.php | 147 + .../guzzlehttp/streams/tests/StreamTest.php | 252 ++ .../guzzlehttp/streams/tests/UtilsTest.php | 155 + .../vendor/kriswallsmith/buzz/.gitignore | 5 + .../vendor/kriswallsmith/buzz/.travis.yml | 25 + .../vendor/kriswallsmith/buzz/CHANGELOG.md | 4 + .../vendor/kriswallsmith/buzz/LICENSE | 19 + .../vendor/kriswallsmith/buzz/README.md | 28 + .../vendor/kriswallsmith/buzz/composer.json | 32 + .../kriswallsmith/buzz/lib/Buzz/Browser.php | 195 + .../buzz/lib/Buzz/Client/AbstractClient.php | 73 + .../buzz/lib/Buzz/Client/AbstractCurl.php | 232 + .../buzz/lib/Buzz/Client/AbstractStream.php | 45 + .../lib/Buzz/Client/BatchClientInterface.php | 27 + .../buzz/lib/Buzz/Client/ClientInterface.php | 20 + .../buzz/lib/Buzz/Client/Curl.php | 60 + .../buzz/lib/Buzz/Client/FileGetContents.php | 91 + .../buzz/lib/Buzz/Client/MultiCurl.php | 114 + .../lib/Buzz/Exception/ClientException.php | 10 + .../lib/Buzz/Exception/ExceptionInterface.php | 10 + .../Exception/InvalidArgumentException.php | 10 + .../lib/Buzz/Exception/LogicException.php | 10 + .../lib/Buzz/Exception/RequestException.php | 32 + .../lib/Buzz/Exception/RuntimeException.php | 7 + .../lib/Buzz/Listener/BasicAuthListener.php | 27 + .../lib/Buzz/Listener/CallbackListener.php | 49 + .../buzz/lib/Buzz/Listener/History/Entry.php | 42 + .../lib/Buzz/Listener/History/Journal.php | 76 + .../lib/Buzz/Listener/HistoryListener.php | 33 + .../buzz/lib/Buzz/Listener/ListenerChain.php | 40 + .../lib/Buzz/Listener/ListenerInterface.php | 12 + .../buzz/lib/Buzz/Listener/LoggerListener.php | 36 + .../buzz/lib/Buzz/Message/AbstractMessage.php | 154 + .../buzz/lib/Buzz/Message/Factory/Factory.php | 26 + .../Buzz/Message/Factory/FactoryInterface.php | 12 + .../lib/Buzz/Message/Form/FormRequest.php | 187 + .../Message/Form/FormRequestInterface.php | 27 + .../buzz/lib/Buzz/Message/Form/FormUpload.php | 118 + .../Buzz/Message/Form/FormUploadInterface.php | 13 + .../lib/Buzz/Message/MessageInterface.php | 74 + .../buzz/lib/Buzz/Message/Request.php | 174 + .../lib/Buzz/Message/RequestInterface.php | 75 + .../buzz/lib/Buzz/Message/Response.php | 193 + .../buzz/lib/Buzz/Util/Cookie.php | 216 + .../buzz/lib/Buzz/Util/CookieJar.php | 79 + .../kriswallsmith/buzz/lib/Buzz/Util/Url.php | 190 + .../kriswallsmith/buzz/phpunit.xml.dist | 20 + .../buzz/test/Buzz/Test/BrowserTest.php | 182 + .../Buzz/Test/Client/AbstractStreamTest.php | 58 + .../buzz/test/Buzz/Test/Client/ClientTest.php | 36 + .../test/Buzz/Test/Client/FunctionalTest.php | 287 ++ .../Test/Listener/BasicAuthListenerTest.php | 20 + .../Test/Listener/CallbackListenerTest.php | 34 + .../Buzz/Test/Listener/History/EntryTest.php | 15 + .../Test/Listener/History/JournalTest.php | 119 + .../Test/Listener/HistoryListenerTest.php | 39 + .../Buzz/Test/Listener/ListenerChainTest.php | 36 + .../Buzz/Test/Listener/LoggerListenerTest.php | 31 + .../Buzz/Test/Message/AbstractMessageTest.php | 129 + .../test/Buzz/Test/Message/FactoryTest.php | 61 + .../Buzz/Test/Message/Fixtures/google.png | Bin 0 -> 7007 bytes .../Buzz/Test/Message/FormRequestTest.php | 137 + .../test/Buzz/Test/Message/FormUploadTest.php | 44 + .../test/Buzz/Test/Message/RequestTest.php | 141 + .../test/Buzz/Test/Message/ResponseTest.php | 114 + .../test/Buzz/Test/Util/CookieJarTest.php | 73 + .../buzz/test/Buzz/Test/Util/CookieTest.php | 151 + .../buzz/test/Buzz/Test/Util/UrlTest.php | 57 + .../kriswallsmith/buzz/test/etc/nginx.conf | 49 + .../kriswallsmith/buzz/test/etc/squid.conf | 2 + .../vendor/kriswallsmith/buzz/test/server.php | 18 + .../reflection-docblock/.gitignore | 2 + .../reflection-docblock/.travis.yml | 32 + .../phpdocumentor/reflection-docblock/LICENSE | 21 + .../reflection-docblock/README.md | 57 + .../reflection-docblock/composer.json | 26 + .../reflection-docblock/composer.lock | 827 ++++ .../reflection-docblock/phpunit.xml.dist | 14 + .../src/phpDocumentor/Reflection/DocBlock.php | 468 ++ .../Reflection/DocBlock/Context.php | 154 + .../Reflection/DocBlock/Description.php | 223 + .../Reflection/DocBlock/Location.php | 76 + .../Reflection/DocBlock/Serializer.php | 198 + .../phpDocumentor/Reflection/DocBlock/Tag.php | 377 ++ .../Reflection/DocBlock/Tag/AuthorTag.php | 131 + .../Reflection/DocBlock/Tag/CoversTag.php | 24 + .../Reflection/DocBlock/Tag/DeprecatedTag.php | 26 + .../Reflection/DocBlock/Tag/ExampleTag.php | 156 + .../Reflection/DocBlock/Tag/LinkTag.php | 81 + .../Reflection/DocBlock/Tag/MethodTag.php | 209 + .../Reflection/DocBlock/Tag/ParamTag.php | 119 + .../DocBlock/Tag/PropertyReadTag.php | 24 + .../Reflection/DocBlock/Tag/PropertyTag.php | 24 + .../DocBlock/Tag/PropertyWriteTag.php | 24 + .../Reflection/DocBlock/Tag/ReturnTag.php | 99 + .../Reflection/DocBlock/Tag/SeeTag.php | 81 + .../Reflection/DocBlock/Tag/SinceTag.php | 26 + .../Reflection/DocBlock/Tag/SourceTag.php | 137 + .../Reflection/DocBlock/Tag/ThrowsTag.php | 24 + .../Reflection/DocBlock/Tag/UsesTag.php | 24 + .../Reflection/DocBlock/Tag/VarTag.php | 24 + .../Reflection/DocBlock/Tag/VersionTag.php | 108 + .../Reflection/DocBlock/Type/Collection.php | 221 + .../Reflection/DocBlock/DescriptionTest.php | 245 ++ .../Reflection/DocBlock/Tag/CoversTagTest.php | 86 + .../DocBlock/Tag/DeprecatedTagTest.php | 115 + .../DocBlock/Tag/ExampleTagTest.php | 203 + .../Reflection/DocBlock/Tag/LinkTagTest.php | 87 + .../Reflection/DocBlock/Tag/MethodTagTest.php | 146 + .../Reflection/DocBlock/Tag/ParamTagTest.php | 118 + .../Reflection/DocBlock/Tag/ReturnTagTest.php | 102 + .../Reflection/DocBlock/Tag/SeeTagTest.php | 86 + .../Reflection/DocBlock/Tag/SinceTagTest.php | 115 + .../Reflection/DocBlock/Tag/SourceTagTest.php | 116 + .../Reflection/DocBlock/Tag/ThrowsTagTest.php | 102 + .../Reflection/DocBlock/Tag/UsesTagTest.php | 86 + .../Reflection/DocBlock/Tag/VarTagTest.php | 94 + .../DocBlock/Tag/VersionTagTest.php | 115 + .../Reflection/DocBlock/TagTest.php | 313 ++ .../DocBlock/Type/CollectionTest.php | 195 + .../phpDocumentor/Reflection/DocBlockTest.php | 337 ++ .../vendor/phpspec/php-diff/README | 58 + .../vendor/phpspec/php-diff/composer.json | 19 + .../vendor/phpspec/php-diff/example/a.txt | 13 + .../vendor/phpspec/php-diff/example/b.txt | 14 + .../phpspec/php-diff/example/example.php | 69 + .../phpspec/php-diff/example/styles.css | 93 + .../vendor/phpspec/php-diff/lib/Diff.php | 177 + .../php-diff/lib/Diff/Renderer/Abstract.php | 82 + .../php-diff/lib/Diff/Renderer/Html/Array.php | 225 + .../lib/Diff/Renderer/Html/Inline.php | 143 + .../lib/Diff/Renderer/Html/SideBySide.php | 163 + .../lib/Diff/Renderer/Text/Context.php | 128 + .../lib/Diff/Renderer/Text/Unified.php | 87 + .../php-diff/lib/Diff/SequenceMatcher.php | 748 ++++ .../vendor/phpspec/phpspec/.gitattributes | 4 + .../vendor/phpspec/phpspec/.gitignore | 6 + .../vendor/phpspec/phpspec/.scrutinizer.yml | 26 + .../vendor/phpspec/phpspec/.travis.yml | 49 + .../vendor/phpspec/phpspec/CHANGES.md | 169 + .../vendor/phpspec/phpspec/CONTRIBUTING.md | 25 + .../vendor/phpspec/phpspec/LICENSE | 23 + .../vendor/phpspec/phpspec/Makefile | 13 + .../vendor/phpspec/phpspec/README.rst | 29 + .../vendor/phpspec/phpspec/behat.yml.dist | 13 + .../vendor/phpspec/phpspec/bin/phpspec | 26 + .../vendor/phpspec/phpspec/box.json | 19 + .../vendor/phpspec/phpspec/composer.json | 64 + .../features/bootstrap/ApplicationContext.php | 244 ++ .../features/bootstrap/Fake/Prompter.php | 34 + .../features/bootstrap/Fake/ReRunner.php | 28 + .../features/bootstrap/FilesystemContext.php | 107 + .../bootstrap/IsolatedProcessContext.php | 76 + .../Matcher/ApplicationOutputMatcher.php | 74 + .../bootstrap/Matcher/FileExistsMatcher.php | 67 + .../Matcher/FileHasContentsMatcher.php | 66 + .../Matcher/ValidJUnitXmlMatcher.php | 66 + .../developer_generates_class.feature | 133 + .../developer_generates_collaborator.feature | 121 + ...oper_generates_collaborator_method.feature | 266 ++ .../developer_generates_method.feature | 106 + ...eloper_generates_named_constructor.feature | 285 ++ ...eveloper_generates_return_constant.feature | 324 ++ .../developer_generates_spec.feature | 97 + .../developer_reruns_features.feature | 62 + ...oper_specifies_object_construction.feature | 436 ++ .../developer_specifies_exceptions.feature | 48 + .../developer_is_shown_diffs.feature | 466 ++ .../formatter/use_the_junit_formatter.feature | 68 + .../formatter/use_the_tap_formatter.feature | 88 + ...oper_users_array_key_value_matcher.feature | 41 + ...veloper_uses_array_contain_matcher.feature | 41 + .../developer_uses_array_key_matcher.feature | 44 + .../developer_uses_comparison_matcher.feature | 41 + .../developer_uses_count_matcher.feature | 41 + .../developer_uses_identity_matcher.feature | 448 ++ .../developer_uses_inline_matcher.feature | 111 + ...eveloper_uses_object_state_matcher.feature | 78 + .../developer_uses_scalar_matcher.feature | 53 + .../developer_uses_string_end_matcher.feature | 41 + ...eveloper_uses_string_regex_matcher.feature | 41 + ...eveloper_uses_string_start_matcher.feature | 41 + .../developer_uses_throw_matcher.feature | 165 + .../developer_uses_type_matcher.feature | 145 + ...veloper_chooses_no_code_generation.feature | 81 + .../developer_chooses_stop_on_failure.feature | 144 + ...eloper_is_told_about_pending_specs.feature | 125 + .../runner/developer_runs_specs.feature | 132 + ...eveloper_runs_specs_with_bootstrap.feature | 31 + .../runner/developer_skips_specs.feature | 42 + .../PhpSpec/Console/Prompter/DialogTest.php | 59 + .../PhpSpec/Console/Prompter/FactoryTest.php | 108 + .../PhpSpec/Console/Prompter/QuestionTest.php | 69 + .../vendor/phpspec/phpspec/phpspec.yml | 1 + .../vendor/phpspec/phpspec/phpunit.xml | 7 + .../Generator/ClassGeneratorSpec.php | 120 + .../Generator/MethodGeneratorSpec.php | 79 + .../NamedConstructorGeneratorSpec.php | 82 + .../Generator/ReturnConstantGeneratorSpec.php | 38 + .../Generator/SpecificationGeneratorSpec.php | 120 + .../CodeGenerator/GeneratorManagerSpec.php | 46 + .../CodeGenerator/TemplateRendererSpec.php | 98 + .../spec/PhpSpec/Config/OptionsConfigSpec.php | 51 + .../spec/PhpSpec/Console/ApplicationSpec.php | 19 + .../phpspec/spec/PhpSpec/Console/IOSpec.php | 183 + .../PhpSpec/Console/ResultConverterSpec.php | 35 + .../spec/PhpSpec/Event/ExampleEventSpec.php | 58 + .../PhpSpec/Event/ExpectationEventSpec.php | 77 + .../PhpSpec/Event/MethodCallEventSpec.php | 66 + .../PhpSpec/Event/SpecificationEventSpec.php | 45 + .../spec/PhpSpec/Event/SuiteEventSpec.php | 48 + .../Example/NotEqualExceptionSpec.php | 28 + .../Example/StopOnFailureExceptionSpec.php | 25 + .../Exception/ExceptionFactorySpec.php | 173 + .../spec/PhpSpec/Exception/ExceptionSpec.php | 21 + .../Fracture/ClassNotFoundExceptionSpec.php | 23 + .../InterfaceNotImplementedExceptionSpec.php | 28 + .../Fracture/MethodNotFoundExceptionSpec.php | 33 + .../MethodNotVisibleExceptionSpec.php | 34 + .../NamedConstructorNotFoundExceptionSpec.php | 34 + .../PropertyNotFoundExceptionSpec.php | 28 + .../PhpSpec/Formatter/BasicFormatterSpec.php | 43 + .../PhpSpec/Formatter/DotFormatterSpec.php | 171 + .../Formatter/Html/HtmlPresenterSpec.php | 29 + .../spec/PhpSpec/Formatter/Html/IOSpec.php | 16 + .../Formatter/Html/ReportFailedItemSpec.php | 44 + .../Formatter/Html/ReportItemFactorySpec.php | 42 + .../Formatter/Html/ReportPassedItemSpec.php | 28 + .../Formatter/Html/ReportPendingItemSpec.php | 29 + .../PhpSpec/Formatter/Html/TemplateSpec.php | 57 + .../PhpSpec/Formatter/HtmlFormatterSpec.php | 37 + .../PhpSpec/Formatter/JUnitFormatterSpec.php | 226 + .../Presenter/Differ/ArrayEngineSpec.php | 24 + .../Formatter/Presenter/Differ/DifferSpec.php | 41 + .../Presenter/Differ/ObjectEngineSpec.php | 43 + .../Presenter/Differ/StringEngineSpec.php | 31 + .../Presenter/StringPresenterSpec.php | 151 + .../Presenter/TaggedPresenterSpec.php | 24 + .../Formatter/ProgressFormatterSpec.php | 99 + .../PhpSpec/Formatter/TapFormatterSpec.php | 148 + .../Listener/ClassNotFoundListenerSpec.php | 81 + ...CollaboratorMethodNotFoundListenerSpec.php | 162 + .../CollaboratorNotFoundListenerSpec.php | 134 + .../Listener/MethodNotFoundListenerSpec.php | 68 + .../MethodReturnedNullListenerSpec.php | 209 + .../NamedConstructorNotFoundListenerSpec.php | 67 + .../PhpSpec/Listener/RerunListenerSpec.php | 40 + .../Listener/StatisticsCollectorSpec.php | 113 + .../Listener/StopOnFailureListenerSpec.php | 67 + .../PhpSpec/Loader/Node/ExampleNodeSpec.php | 57 + .../Loader/Node/SpecificationNodeSpec.php | 64 + .../phpspec/spec/PhpSpec/Loader/SuiteSpec.php | 36 + .../PhpSpec/Locator/PSR0/PSR0LocatorSpec.php | 502 +++ .../PhpSpec/Locator/PSR0/PSR0ResourceSpec.php | 95 + .../PhpSpec/Locator/ResourceManagerSpec.php | 117 + .../Matcher/ArrayContainMatcherSpec.php | 46 + .../PhpSpec/Matcher/ArrayCountMatcherSpec.php | 70 + .../PhpSpec/Matcher/ArrayKeyMatcherSpec.php | 72 + .../Matcher/ArrayKeyValueMatcherSpec.php | 112 + .../PhpSpec/Matcher/CallbackMatcherSpec.php | 48 + .../PhpSpec/Matcher/ComparisonMatcherSpec.php | 106 + .../PhpSpec/Matcher/IdentityMatcherSpec.php | 121 + .../Matcher/ObjectStateMatcherSpec.php | 93 + .../PhpSpec/Matcher/StringEndMatcherSpec.php | 53 + .../Matcher/StringRegexMatcherSpec.php | 53 + .../Matcher/StringStartMatcherSpec.php | 53 + .../spec/PhpSpec/Matcher/ThrowMatcherSpec.php | 39 + .../spec/PhpSpec/Matcher/TypeMatcherSpec.php | 79 + .../ReRunner/CompositeReRunnerSpec.php | 54 + .../Process/ReRunner/OptionalReRunnerSpec.php | 34 + .../Process/ReRunner/PassthruRerunnerSpec.php | 38 + .../Process/ReRunner/PcntlReRunnerSpec.php | 27 + .../Runner/CollaboratorManagerSpec.php | 67 + .../spec/PhpSpec/Runner/ExampleRunnerSpec.php | 134 + .../Maintainer/MatchersMaintainerSpec.php | 25 + .../PhpSpec/Runner/MatcherManagerSpec.php | 46 + .../Runner/SpecificationRunnerSpec.php | 67 + .../spec/PhpSpec/Runner/SuiteRunnerSpec.php | 99 + .../spec/PhpSpec/ServiceContainerSpec.php | 84 + .../PhpSpec/Util/ExampleObjectUsingTrait.php | 8 + .../spec/PhpSpec/Util/ExampleTrait.php | 18 + .../spec/PhpSpec/Util/InstantiatorSpec.php | 53 + .../spec/PhpSpec/Util/MethodAnalyserSpec.php | 99 + .../PhpSpec/Wrapper/Subject/CallerSpec.php | 228 + .../Expectation/ConstructorDecoratorSpec.php | 37 + .../Subject/Expectation/DecoratorSpec.php | 39 + .../Expectation/DispatcherDecoratorSpec.php | 65 + .../Subject/Expectation/NegativeSpec.php | 27 + .../Subject/Expectation/PositiveSpec.php | 25 + .../Subject/ExpectationFactorySpec.php | 62 + .../Wrapper/Subject/WrappedObjectSpec.php | 101 + .../spec/PhpSpec/Wrapper/SubjectSpec.php | 55 + .../Generator/ClassGenerator.php | 103 + .../Generator/CreateObjectTemplate.php | 73 + .../Generator/ExistingConstructorTemplate.php | 143 + .../Generator/GeneratorInterface.php | 42 + .../Generator/InterfaceGenerator.php | 101 + .../Generator/MethodGenerator.php | 114 + .../Generator/MethodSignatureGenerator.php | 130 + .../Generator/NamedConstructorGenerator.php | 118 + .../Generator/PromptingGenerator.php | 148 + .../Generator/ReturnConstantGenerator.php | 108 + .../Generator/SpecificationGenerator.php | 97 + .../Generator/templates/class.template | 5 + .../Generator/templates/interface.template | 5 + .../interface_method_signature.template | 3 + .../Generator/templates/method.template | 5 + .../named_constructor_create_object.template | 9 + .../named_constructor_exception.template | 5 + .../templates/returnconstant.template | 4 + .../templates/specification.template | 14 + .../CodeGenerator/GeneratorManager.php | 61 + .../CodeGenerator/TemplateRenderer.php | 119 + .../src/PhpSpec/Config/OptionsConfig.php | 93 + .../src/PhpSpec/Console/Application.php | 186 + .../Console/Command/DescribeCommand.php | 70 + .../PhpSpec/Console/Command/RunCommand.php | 152 + .../PhpSpec/Console/ContainerAssembler.php | 612 +++ .../phpspec/src/PhpSpec/Console/Formatter.php | 64 + .../phpspec/src/PhpSpec/Console/IO.php | 348 ++ .../phpspec/src/PhpSpec/Console/Prompter.php | 24 + .../src/PhpSpec/Console/Prompter/Dialog.php | 51 + .../src/PhpSpec/Console/Prompter/Factory.php | 60 + .../src/PhpSpec/Console/Prompter/Question.php | 60 + .../src/PhpSpec/Console/ResultConverter.php | 40 + .../src/PhpSpec/Event/EventInterface.php | 21 + .../src/PhpSpec/Event/ExampleEvent.php | 158 + .../src/PhpSpec/Event/ExpectationEvent.php | 173 + .../src/PhpSpec/Event/MethodCallEvent.php | 120 + .../src/PhpSpec/Event/SpecificationEvent.php | 90 + .../phpspec/src/PhpSpec/Event/SuiteEvent.php | 92 + .../Exception/Example/ErrorException.php | 52 + .../Exception/Example/ExampleException.php | 23 + .../Exception/Example/FailureException.php | 21 + .../Exception/Example/MatcherException.php | 21 + .../Exception/Example/NotEqualException.php | 67 + .../Exception/Example/PendingException.php | 28 + .../Exception/Example/SkippingException.php | 25 + .../Example/StopOnFailureException.php | 38 + .../src/PhpSpec/Exception/Exception.php | 43 + .../PhpSpec/Exception/ExceptionFactory.php | 165 + .../Fracture/ClassNotFoundException.php | 44 + .../CollaboratorNotFoundException.php | 64 + .../FactoryDoesNotReturnObjectException.php | 18 + .../Exception/Fracture/FractureException.php | 23 + .../InterfaceNotImplementedException.php | 60 + .../Fracture/MethodInvocationException.php | 75 + .../Fracture/MethodNotFoundException.php | 22 + .../Fracture/MethodNotVisibleException.php | 22 + .../NamedConstructorNotFoundException.php | 22 + .../Fracture/PropertyNotFoundException.php | 60 + .../Locator/ResourceCreationException.php | 18 + .../Wrapper/CollaboratorException.php | 23 + .../Wrapper/MatcherNotFoundException.php | 77 + .../Exception/Wrapper/SubjectException.php | 23 + .../PhpSpec/Extension/ExtensionInterface.php | 28 + .../src/PhpSpec/Factory/ReflectionFactory.php | 30 + .../src/PhpSpec/Formatter/BasicFormatter.php | 127 + .../PhpSpec/Formatter/ConsoleFormatter.php | 94 + .../src/PhpSpec/Formatter/DotFormatter.php | 140 + .../PhpSpec/Formatter/Html/HtmlPresenter.php | 67 + .../phpspec/src/PhpSpec/Formatter/Html/IO.php | 35 + .../Html/InvalidExampleResultException.php | 20 + .../Formatter/Html/ReportFailedItem.php | 86 + .../src/PhpSpec/Formatter/Html/ReportItem.php | 22 + .../Formatter/Html/ReportItemFactory.php | 69 + .../Formatter/Html/ReportPassedItem.php | 49 + .../Formatter/Html/ReportPendingItem.php | 55 + .../Formatter/Html/ReportSkippedItem.php | 50 + .../src/PhpSpec/Formatter/Html/Template.php | 61 + .../Formatter/Html/Template/ReportFailed.html | 11 + .../Formatter/Html/Template/ReportFooter.html | 2 + .../Formatter/Html/Template/ReportHeader.html | 196 + .../Formatter/Html/Template/ReportLine.html | 0 .../Formatter/Html/Template/ReportPass.html | 1 + .../Html/Template/ReportPending.html | 6 + .../Formatter/Html/Template/ReportRed.html | 11 + .../Html/Template/ReportSkipped.html | 6 + .../Template/ReportSpecificationEnds.html | 3 + .../Template/ReportSpecificationStarts.html | 5 + .../Html/Template/ReportSummary.html | 3 + .../src/PhpSpec/Formatter/HtmlFormatter.php | 90 + .../src/PhpSpec/Formatter/JUnitFormatter.php | 222 + .../Presenter/Differ/ArrayEngine.php | 80 + .../Formatter/Presenter/Differ/Differ.php | 38 + .../Presenter/Differ/EngineInterface.php | 33 + .../Presenter/Differ/ObjectEngine.php | 63 + .../Presenter/Differ/StringEngine.php | 46 + .../Presenter/PresenterInterface.php | 39 + .../Formatter/Presenter/StringPresenter.php | 488 +++ .../Formatter/Presenter/TaggedPresenter.php | 62 + .../src/PhpSpec/Formatter/PrettyFormatter.php | 133 + .../PhpSpec/Formatter/ProgressFormatter.php | 183 + .../src/PhpSpec/Formatter/TapFormatter.php | 179 + .../src/PhpSpec/Formatter/Template.php | 23 + .../phpspec/src/PhpSpec/IO/IOInterface.php | 27 + .../PhpSpec/Listener/BootstrapListener.php | 35 + .../Listener/ClassNotFoundListener.php | 96 + .../CollaboratorMethodNotFoundListener.php | 170 + .../Listener/CollaboratorNotFoundListener.php | 115 + .../Listener/MethodNotFoundListener.php | 85 + .../Listener/MethodReturnedNullListener.php | 174 + .../NamedConstructorNotFoundListener.php | 85 + .../src/PhpSpec/Listener/RerunListener.php | 52 + .../PhpSpec/Listener/StatisticsCollector.php | 142 + .../Listener/StopOnFailureListener.php | 62 + .../src/PhpSpec/Loader/Node/ExampleNode.php | 94 + .../PhpSpec/Loader/Node/SpecificationNode.php | 119 + .../src/PhpSpec/Loader/ResourceLoader.php | 103 + .../phpspec/src/PhpSpec/Loader/Suite.php | 47 + .../src/PhpSpec/Locator/PSR0/PSR0Locator.php | 349 ++ .../src/PhpSpec/Locator/PSR0/PSR0Resource.php | 117 + .../src/PhpSpec/Locator/ResourceInterface.php | 57 + .../Locator/ResourceLocatorInterface.php | 55 + .../src/PhpSpec/Locator/ResourceManager.php | 101 + .../Locator/ResourceManagerInterface.php | 31 + .../PhpSpec/Matcher/ArrayContainMatcher.php | 91 + .../src/PhpSpec/Matcher/ArrayCountMatcher.php | 92 + .../src/PhpSpec/Matcher/ArrayKeyMatcher.php | 98 + .../PhpSpec/Matcher/ArrayKeyValueMatcher.php | 116 + .../src/PhpSpec/Matcher/BasicMatcher.php | 89 + .../src/PhpSpec/Matcher/CallbackMatcher.php | 104 + .../src/PhpSpec/Matcher/ComparisonMatcher.php | 90 + .../src/PhpSpec/Matcher/IdentityMatcher.php | 99 + .../src/PhpSpec/Matcher/MatcherInterface.php | 53 + .../Matcher/MatchersProviderInterface.php | 22 + .../PhpSpec/Matcher/ObjectStateMatcher.php | 129 + .../src/PhpSpec/Matcher/ScalarMatcher.php | 132 + .../src/PhpSpec/Matcher/StringEndMatcher.php | 91 + .../PhpSpec/Matcher/StringRegexMatcher.php | 91 + .../PhpSpec/Matcher/StringStartMatcher.php | 91 + .../src/PhpSpec/Matcher/ThrowMatcher.php | 275 ++ .../src/PhpSpec/Matcher/TypeMatcher.php | 99 + .../phpspec/src/PhpSpec/ObjectBehavior.php | 172 + .../phpspec/src/PhpSpec/Process/ReRunner.php | 19 + .../Process/ReRunner/CompositeReRunner.php | 42 + .../Process/ReRunner/OptionalReRunner.php | 45 + .../Process/ReRunner/PassthruReRunner.php | 35 + .../Process/ReRunner/PcntlReRunner.php | 37 + .../ReRunner/PhpExecutableReRunner.php | 50 + .../ReRunner/PlatformSpecificReRunner.php | 26 + .../src/PhpSpec/Resources/schema/junit.xsd | 90 + .../PhpSpec/Runner/CollaboratorManager.php | 95 + .../src/PhpSpec/Runner/ExampleRunner.php | 191 + .../Maintainer/CollaboratorsMaintainer.php | 152 + .../Runner/Maintainer/ErrorMaintainer.php | 127 + .../Maintainer/LetAndLetgoMaintainer.php | 82 + .../Runner/Maintainer/MaintainerInterface.php | 60 + .../Runner/Maintainer/MatchersMaintainer.php | 116 + .../Runner/Maintainer/SubjectMaintainer.php | 109 + .../src/PhpSpec/Runner/MatcherManager.php | 89 + .../PhpSpec/Runner/SpecificationRunner.php | 65 + .../src/PhpSpec/Runner/SuiteRunner.php | 71 + .../phpspec/src/PhpSpec/ServiceContainer.php | 248 ++ .../src/PhpSpec/SpecificationInterface.php | 23 + .../phpspec/src/PhpSpec/Util/Filesystem.php | 103 + .../phpspec/src/PhpSpec/Util/Instantiator.php | 35 + .../src/PhpSpec/Util/MethodAnalyser.php | 176 + .../src/PhpSpec/Wrapper/Collaborator.php | 98 + .../src/PhpSpec/Wrapper/DelayedCall.php | 41 + .../phpspec/src/PhpSpec/Wrapper/Subject.php | 267 ++ .../src/PhpSpec/Wrapper/Subject/Caller.php | 438 ++ .../Expectation/ConstructorDecorator.php | 62 + .../Wrapper/Subject/Expectation/Decorator.php | 59 + .../Expectation/DispatcherDecorator.php | 120 + .../Subject/Expectation/DuringCall.php | 131 + .../Expectation/ExpectationInterface.php | 26 + .../Wrapper/Subject/Expectation/Negative.php | 44 + .../Subject/Expectation/NegativeThrow.php | 30 + .../Wrapper/Subject/Expectation/Positive.php | 44 + .../Subject/Expectation/PositiveThrow.php | 30 + .../Subject/Expectation/ThrowExpectation.php | 25 + .../Subject/Expectation/UnwrapDecorator.php | 48 + .../Wrapper/Subject/ExpectationFactory.php | 154 + .../Subject/SubjectWithArrayAccess.php | 158 + .../PhpSpec/Wrapper/Subject/WrappedObject.php | 235 + .../Wrapper/SubjectContainerInterface.php | 22 + .../phpspec/src/PhpSpec/Wrapper/Unwrapper.php | 70 + .../phpspec/src/PhpSpec/Wrapper/Wrapper.php | 85 + .../src/PhpSpec/Wrapper/WrapperInterface.php | 22 + .../vendor/phpspec/prophecy/.gitignore | 5 + .../vendor/phpspec/prophecy/.travis.yml | 15 + .../vendor/phpspec/prophecy/CHANGES.md | 115 + .../vendor/phpspec/prophecy/CONTRIBUTING.md | 21 + .../vendor/phpspec/prophecy/LICENSE | 23 + .../vendor/phpspec/prophecy/README.md | 389 ++ .../vendor/phpspec/prophecy/composer.json | 40 + .../Argument/ArgumentsWildcardSpec.php | 143 + .../Argument/Token/AnyValueTokenSpec.php | 28 + .../Argument/Token/AnyValuesTokenSpec.php | 28 + .../Argument/Token/ArrayCountTokenSpec.php | 64 + .../Argument/Token/ArrayEntryTokenSpec.php | 229 + .../Token/ArrayEveryEntryTokenSpec.php | 109 + .../Argument/Token/CallbackTokenSpec.php | 42 + .../Argument/Token/ExactValueTokenSpec.php | 155 + .../Token/IdenticalValueTokenSpec.php | 152 + .../Argument/Token/LogicalAndTokenSpec.php | 78 + .../Argument/Token/LogicalNotTokenSpec.php | 65 + .../Argument/Token/ObjectStateTokenSpec.php | 101 + .../Token/StringContainsTokenSpec.php | 49 + .../Prophecy/Argument/Token/TypeTokenSpec.php | 59 + .../prophecy/spec/Prophecy/ArgumentSpec.php | 101 + .../spec/Prophecy/Call/CallCenterSpec.php | 188 + .../prophecy/spec/Prophecy/Call/CallSpec.php | 54 + .../Comparator/ClosureComparatorSpec.php | 39 + .../spec/Prophecy/Comparator/FactorySpec.php | 20 + .../DisableConstructorPatchSpec.php | 59 + .../ClassPatch/HhvmExceptionPatchSpec.php | 37 + .../Doubler/ClassPatch/KeywordPatchSpec.php | 44 + .../Doubler/ClassPatch/MagicCallPatchSpec.php | 76 + .../ClassPatch/ProphecySubjectPatchSpec.php | 83 + .../ReflectionClassNewInstancePatchSpec.php | 47 + .../ClassPatch/SplFileInfoPatchSpec.php | 91 + .../ClassPatch/TraversablePatchSpec.php | 61 + .../spec/Prophecy/Doubler/DoublerSpec.php | 122 + .../Generator/ClassCodeGeneratorSpec.php | 186 + .../Doubler/Generator/ClassCreatorSpec.php | 44 + .../Doubler/Generator/ClassMirrorSpec.php | 554 +++ .../Generator/Node/ArgumentNodeSpec.php | 62 + .../Doubler/Generator/Node/ClassNodeSpec.php | 154 + .../Doubler/Generator/Node/MethodNodeSpec.php | 123 + .../spec/Prophecy/Doubler/LazyDoubleSpec.php | 96 + .../Prophecy/Doubler/NameGeneratorSpec.php | 72 + .../Call/UnexpectedCallExceptionSpec.php | 32 + .../Doubler/ClassCreatorExceptionSpec.php | 28 + .../Doubler/ClassMirrorExceptionSpec.php | 27 + .../Doubler/ClassNotFoundExceptionSpec.php | 25 + .../Exception/Doubler/DoubleExceptionSpec.php | 14 + .../InterfaceNotFoundExceptionSpec.php | 24 + .../Doubler/MethodNotFoundExceptionSpec.php | 40 + .../Prediction/AggregateExceptionSpec.php | 57 + .../Prediction/NoCallsExceptionSpec.php | 29 + .../UnexpectedCallsCountExceptionSpec.php | 31 + .../UnexpectedCallsExceptionSpec.php | 36 + .../Prophecy/MethodProphecyExceptionSpec.php | 30 + .../Prophecy/ObjectProphecyExceptionSpec.php | 27 + .../Prediction/CallPredictionSpec.php | 42 + .../Prediction/CallTimesPredictionSpec.php | 54 + .../Prediction/CallbackPredictionSpec.php | 36 + .../Prediction/NoCallsPredictionSpec.php | 43 + .../Prophecy/Promise/CallbackPromiseSpec.php | 110 + .../Promise/ReturnArgumentPromiseSpec.php | 41 + .../Prophecy/Promise/ReturnPromiseSpec.php | 61 + .../Prophecy/Promise/ThrowPromiseSpec.php | 58 + .../Prophecy/Prophecy/MethodProphecySpec.php | 381 ++ .../Prophecy/Prophecy/ObjectProphecySpec.php | 319 ++ .../spec/Prophecy/Prophecy/RevealerSpec.php | 51 + .../prophecy/spec/Prophecy/ProphetSpec.php | 91 + .../spec/Prophecy/Util/StringUtilSpec.php | 97 + .../prophecy/src/Prophecy/Argument.php | 198 + .../Prophecy/Argument/ArgumentsWildcard.php | 101 + .../Prophecy/Argument/Token/AnyValueToken.php | 52 + .../Argument/Token/AnyValuesToken.php | 52 + .../Argument/Token/ArrayCountToken.php | 86 + .../Argument/Token/ArrayEntryToken.php | 143 + .../Argument/Token/ArrayEveryEntryToken.php | 82 + .../Prophecy/Argument/Token/CallbackToken.php | 75 + .../Argument/Token/ExactValueToken.php | 116 + .../Argument/Token/IdenticalValueToken.php | 74 + .../Argument/Token/LogicalAndToken.php | 80 + .../Argument/Token/LogicalNotToken.php | 73 + .../Argument/Token/ObjectStateToken.php | 104 + .../Argument/Token/StringContainsToken.php | 67 + .../Argument/Token/TokenInterface.php | 43 + .../src/Prophecy/Argument/Token/TypeToken.php | 76 + .../prophecy/src/Prophecy/Call/Call.php | 127 + .../prophecy/src/Prophecy/Call/CallCenter.php | 152 + .../Prophecy/Comparator/ClosureComparator.php | 42 + .../src/Prophecy/Comparator/Factory.php | 46 + .../src/Prophecy/Doubler/CachedDoubler.php | 68 + .../ClassPatch/ClassPatchInterface.php | 48 + .../ClassPatch/DisableConstructorPatch.php | 72 + .../Doubler/ClassPatch/HhvmExceptionPatch.php | 63 + .../Doubler/ClassPatch/KeywordPatch.php | 135 + .../Doubler/ClassPatch/MagicCallPatch.php | 73 + .../ClassPatch/ProphecySubjectPatch.php | 98 + .../ReflectionClassNewInstancePatch.php | 57 + .../Doubler/ClassPatch/SplFileInfoPatch.php | 85 + .../Doubler/ClassPatch/TraversablePatch.php | 83 + .../src/Prophecy/Doubler/DoubleInterface.php | 22 + .../prophecy/src/Prophecy/Doubler/Doubler.php | 146 + .../Doubler/Generator/ClassCodeGenerator.php | 91 + .../Doubler/Generator/ClassCreator.php | 67 + .../Doubler/Generator/ClassMirror.php | 202 + .../Doubler/Generator/Node/ArgumentNode.php | 75 + .../Doubler/Generator/Node/ClassNode.php | 130 + .../Doubler/Generator/Node/MethodNode.php | 129 + .../Doubler/Generator/ReflectionInterface.php | 22 + .../src/Prophecy/Doubler/LazyDouble.php | 127 + .../src/Prophecy/Doubler/NameGenerator.php | 52 + .../Call/UnexpectedCallException.php | 40 + .../Doubler/ClassCreatorException.php | 31 + .../Doubler/ClassMirrorException.php | 31 + .../Doubler/ClassNotFoundException.php | 33 + .../Exception/Doubler/DoubleException.php | 18 + .../Exception/Doubler/DoublerException.php | 18 + .../Doubler/InterfaceNotFoundException.php | 20 + .../Doubler/MethodNotFoundException.php | 60 + .../Doubler/ReturnByReferenceException.php | 41 + .../src/Prophecy/Exception/Exception.php | 26 + .../Exception/InvalidArgumentException.php | 16 + .../Prediction/AggregateException.php | 50 + .../Prediction/FailedPredictionException.php | 24 + .../Exception/Prediction/NoCallsException.php | 18 + .../Prediction/PredictionException.php | 18 + .../UnexpectedCallsCountException.php | 31 + .../Prediction/UnexpectedCallsException.php | 32 + .../Prophecy/MethodProphecyException.php | 34 + .../Prophecy/ObjectProphecyException.php | 34 + .../Exception/Prophecy/ProphecyException.php | 18 + .../Prophecy/Prediction/CallPrediction.php | 86 + .../Prediction/CallTimesPrediction.php | 107 + .../Prediction/CallbackPrediction.php | 65 + .../Prophecy/Prediction/NoCallsPrediction.php | 68 + .../Prediction/PredictionInterface.php | 37 + .../src/Prophecy/Promise/CallbackPromise.php | 66 + .../src/Prophecy/Promise/PromiseInterface.php | 35 + .../Promise/ReturnArgumentPromise.php | 61 + .../src/Prophecy/Promise/ReturnPromise.php | 55 + .../src/Prophecy/Promise/ThrowPromise.php | 91 + .../src/Prophecy/Prophecy/MethodProphecy.php | 409 ++ .../src/Prophecy/Prophecy/ObjectProphecy.php | 279 ++ .../Prophecy/Prophecy/ProphecyInterface.php | 27 + .../Prophecy/ProphecySubjectInterface.php | 34 + .../src/Prophecy/Prophecy/Revealer.php | 44 + .../Prophecy/Prophecy/RevealerInterface.php | 29 + .../phpspec/prophecy/src/Prophecy/Prophet.php | 134 + .../prophecy/src/Prophecy/Util/ExportUtil.php | 185 + .../prophecy/src/Prophecy/Util/StringUtil.php | 89 + .../vendor/react/promise/.gitignore | 4 + .../vendor/react/promise/.travis.yml | 13 + .../vendor/react/promise/CHANGELOG.md | 60 + .../vendor/react/promise/LICENSE | 22 + .../vendor/react/promise/README.md | 824 ++++ .../vendor/react/promise/composer.json | 22 + .../vendor/react/promise/phpunit.xml.dist | 25 + .../src/CancellablePromiseInterface.php | 11 + .../vendor/react/promise/src/Deferred.php | 60 + .../promise/src/ExtendedPromiseInterface.php | 26 + .../react/promise/src/FulfilledPromise.php | 68 + .../vendor/react/promise/src/LazyPromise.php | 58 + .../vendor/react/promise/src/Promise.php | 182 + .../react/promise/src/PromiseInterface.php | 11 + .../react/promise/src/PromisorInterface.php | 11 + .../react/promise/src/RejectedPromise.php | 74 + .../src/UnhandledRejectionException.php | 31 + .../vendor/react/promise/src/functions.php | 196 + .../react/promise/src/functions_include.php | 5 + .../react/promise/tests/DeferredTest.php | 42 + .../promise/tests/FulfilledPromiseTest.php | 50 + .../react/promise/tests/FunctionAllTest.php | 97 + .../react/promise/tests/FunctionAnyTest.php | 116 + .../tests/FunctionCheckTypehintTest.php | 118 + .../react/promise/tests/FunctionMapTest.php | 125 + .../react/promise/tests/FunctionRaceTest.php | 122 + .../promise/tests/FunctionReduceTest.php | 290 ++ .../promise/tests/FunctionRejectTest.php | 64 + .../promise/tests/FunctionResolveTest.php | 102 + .../react/promise/tests/FunctionSomeTest.php | 126 + .../react/promise/tests/LazyPromiseTest.php | 107 + .../PromiseAdapter/CallbackPromiseAdapter.php | 40 + .../PromiseAdapterInterface.php | 14 + .../react/promise/tests/PromiseTest.php | 116 + .../tests/PromiseTest/CancelTestTrait.php | 206 + .../tests/PromiseTest/FullTestTrait.php | 15 + .../tests/PromiseTest/NotifyTestTrait.php | 336 ++ .../PromiseTest/PromiseFulfilledTestTrait.php | 351 ++ .../PromiseTest/PromisePendingTestTrait.php | 68 + .../PromiseTest/PromiseRejectedTestTrait.php | 492 +++ .../PromiseTest/PromiseSettledTestTrait.php | 86 + .../tests/PromiseTest/RejectTestTrait.php | 363 ++ .../tests/PromiseTest/ResolveTestTrait.php | 258 ++ .../promise/tests/RejectedPromiseTest.php | 50 + .../react/promise/tests/Stub/CallableStub.php | 10 + .../vendor/react/promise/tests/TestCase.php | 41 + .../vendor/react/promise/tests/bootstrap.php | 7 + .../vendor/sebastian/comparator/.gitignore | 6 + .../vendor/sebastian/comparator/.travis.yml | 23 + .../vendor/sebastian/comparator/LICENSE | 33 + .../vendor/sebastian/comparator/README.md | 38 + .../vendor/sebastian/comparator/build.xml | 34 + .../sebastian/comparator/build/travis-ci.xml | 11 + .../vendor/sebastian/comparator/composer.json | 44 + .../sebastian/comparator/phpunit.xml.dist | 21 + .../comparator/src/ArrayComparator.php | 142 + .../sebastian/comparator/src/Comparator.php | 75 + .../comparator/src/ComparisonFailure.php | 136 + .../comparator/src/DOMNodeComparator.php | 116 + .../comparator/src/DateTimeComparator.php | 85 + .../comparator/src/DoubleComparator.php | 66 + .../comparator/src/ExceptionComparator.php | 57 + .../sebastian/comparator/src/Factory.php | 113 + .../comparator/src/MockObjectComparator.php | 51 + .../comparator/src/NumericComparator.php | 79 + .../comparator/src/ObjectComparator.php | 115 + .../comparator/src/ResourceComparator.php | 62 + .../comparator/src/ScalarComparator.php | 100 + .../src/SplObjectStorageComparator.php | 79 + .../comparator/src/TypeComparator.php | 69 + .../comparator/tests/ArrayComparatorTest.php | 168 + .../tests/DOMNodeComparatorTest.php | 167 + .../tests/DateTimeComparatorTest.php | 199 + .../comparator/tests/DoubleComparatorTest.php | 139 + .../tests/ExceptionComparatorTest.php | 141 + .../comparator/tests/FactoryTest.php | 120 + .../tests/MockObjectComparatorTest.php | 171 + .../tests/NumericComparatorTest.php | 127 + .../comparator/tests/ObjectComparatorTest.php | 155 + .../tests/ResourceComparatorTest.php | 125 + .../comparator/tests/ScalarComparatorTest.php | 163 + .../tests/SplObjectStorageComparatorTest.php | 142 + .../comparator/tests/TypeComparatorTest.php | 109 + .../comparator/tests/_files/Author.php | 33 + .../comparator/tests/_files/Book.php | 26 + .../tests/_files/ClassWithToString.php | 19 + .../comparator/tests/_files/SampleClass.php | 34 + .../comparator/tests/_files/Struct.php | 30 + .../comparator/tests/_files/TestClass.php | 14 + .../tests/_files/TestClassComparator.php | 14 + .../sebastian/comparator/tests/autoload.php | 38 + .../sebastian/comparator/tests/bootstrap.php | 7 + .../vendor/sebastian/diff/.gitignore | 10 + .../vendor/sebastian/diff/.travis.yml | 16 + .../vendor/sebastian/diff/LICENSE | 33 + .../vendor/sebastian/diff/README.md | 126 + .../vendor/sebastian/diff/build.xml | 26 + .../vendor/sebastian/diff/composer.json | 33 + .../vendor/sebastian/diff/phpunit.xml.dist | 17 + .../vendor/sebastian/diff/src/Chunk.php | 110 + .../vendor/sebastian/diff/src/Diff.php | 81 + .../vendor/sebastian/diff/src/Differ.php | 256 ++ .../diff/src/LCS/LongestCommonSubsequence.php | 33 + ...LongestCommonSubsequenceImplementation.php | 98 + ...LongestCommonSubsequenceImplementation.php | 79 + .../vendor/sebastian/diff/src/Line.php | 62 + .../vendor/sebastian/diff/src/Parser.php | 105 + .../sebastian/diff/tests/DifferTest.php | Bin 0 -> 11371 bytes .../LCS/TimeEfficientImplementationTest.php | 175 + .../sebastian/diff/tests/ParserTest.php | 62 + .../sebastian/diff/tests/fixtures/patch.txt | 9 + .../sebastian/diff/tests/fixtures/patch2.txt | 21 + .../vendor/sebastian/exporter/.gitignore | 9 + .../vendor/sebastian/exporter/.travis.yml | 23 + .../vendor/sebastian/exporter/LICENSE | 33 + .../vendor/sebastian/exporter/README.md | 171 + .../vendor/sebastian/exporter/build.xml | 27 + .../vendor/sebastian/exporter/composer.json | 47 + .../sebastian/exporter/phpunit.xml.dist | 21 + .../sebastian/exporter/src/Exporter.php | 302 ++ .../sebastian/exporter/tests/ExporterTest.php | 333 ++ .../sebastian/recursion-context/.gitignore | 9 + .../sebastian/recursion-context/.travis.yml | 22 + .../sebastian/recursion-context/LICENSE | 33 + .../sebastian/recursion-context/README.md | 13 + .../sebastian/recursion-context/build.xml | 27 + .../sebastian/recursion-context/composer.json | 36 + .../recursion-context/phpunit.xml.dist | 20 + .../recursion-context/src/Context.php | 158 + .../recursion-context/src/Exception.php | 22 + .../src/InvalidArgumentException.php | 22 + .../recursion-context/tests/ContextTest.php | 144 + .../vendor/symfony/console/.gitignore | 3 + .../vendor/symfony/console/Application.php | 1177 +++++ .../vendor/symfony/console/CHANGELOG.md | 62 + .../symfony/console/Command/Command.php | 697 +++ .../symfony/console/Command/HelpCommand.php | 93 + .../symfony/console/Command/ListCommand.php | 97 + .../vendor/symfony/console/ConsoleEvents.php | 61 + .../Descriptor/ApplicationDescription.php | 157 + .../symfony/console/Descriptor/Descriptor.php | 121 + .../Descriptor/DescriptorInterface.php | 31 + .../console/Descriptor/JsonDescriptor.php | 166 + .../console/Descriptor/MarkdownDescriptor.php | 143 + .../console/Descriptor/TextDescriptor.php | 282 ++ .../console/Descriptor/XmlDescriptor.php | 263 ++ .../console/Event/ConsoleCommandEvent.php | 62 + .../symfony/console/Event/ConsoleEvent.php | 67 + .../console/Event/ConsoleExceptionEvent.php | 67 + .../console/Event/ConsoleTerminateEvent.php | 58 + .../console/Formatter/OutputFormatter.php | 242 ++ .../Formatter/OutputFormatterInterface.php | 83 + .../Formatter/OutputFormatterStyle.php | 227 + .../OutputFormatterStyleInterface.php | 72 + .../Formatter/OutputFormatterStyleStack.php | 121 + .../console/Helper/DebugFormatterHelper.php | 127 + .../console/Helper/DescriptorHelper.php | 96 + .../symfony/console/Helper/DialogHelper.php | 483 ++ .../console/Helper/FormatterHelper.php | 82 + .../vendor/symfony/console/Helper/Helper.php | 121 + .../console/Helper/HelperInterface.php | 49 + .../symfony/console/Helper/HelperSet.php | 116 + .../console/Helper/InputAwareHelper.php | 33 + .../symfony/console/Helper/ProcessHelper.php | 142 + .../symfony/console/Helper/ProgressBar.php | 615 +++ .../symfony/console/Helper/ProgressHelper.php | 465 ++ .../symfony/console/Helper/QuestionHelper.php | 441 ++ .../console/Helper/SymfonyQuestionHelper.php | 106 + .../vendor/symfony/console/Helper/Table.php | 606 +++ .../symfony/console/Helper/TableCell.php | 77 + .../symfony/console/Helper/TableHelper.php | 268 ++ .../symfony/console/Helper/TableSeparator.php | 29 + .../symfony/console/Helper/TableStyle.php | 255 ++ .../symfony/console/Input/ArgvInput.php | 353 ++ .../symfony/console/Input/ArrayInput.php | 211 + .../vendor/symfony/console/Input/Input.php | 226 + .../symfony/console/Input/InputArgument.php | 132 + .../console/Input/InputAwareInterface.php | 28 + .../symfony/console/Input/InputDefinition.php | 485 +++ .../symfony/console/Input/InputInterface.php | 152 + .../symfony/console/Input/InputOption.php | 213 + .../symfony/console/Input/StringInput.php | 87 + .../vendor/symfony/console/LICENSE | 19 + .../symfony/console/Logger/ConsoleLogger.php | 118 + .../symfony/console/Output/BufferedOutput.php | 48 + .../symfony/console/Output/ConsoleOutput.php | 113 + .../console/Output/ConsoleOutputInterface.php | 35 + .../symfony/console/Output/NullOutput.php | 113 + .../vendor/symfony/console/Output/Output.php | 165 + .../console/Output/OutputInterface.php | 113 + .../symfony/console/Output/StreamOutput.php | 103 + .../console/Question/ChoiceQuestion.php | 174 + .../console/Question/ConfirmationQuestion.php | 61 + .../symfony/console/Question/Question.php | 247 ++ .../vendor/symfony/console/README.md | 67 + .../console/Resources/bin/hiddeninput.exe | Bin 0 -> 9216 bytes .../vendor/symfony/console/Shell.php | 228 + .../symfony/console/Style/OutputStyle.php | 116 + .../symfony/console/Style/StyleInterface.php | 159 + .../symfony/console/Style/SymfonyStyle.php | 406 ++ .../console/Tester/ApplicationTester.php | 128 + .../symfony/console/Tester/CommandTester.php | 132 + .../symfony/console/Tests/ApplicationTest.php | 1060 +++++ .../console/Tests/Command/CommandTest.php | 339 ++ .../console/Tests/Command/HelpCommandTest.php | 70 + .../console/Tests/Command/ListCommandTest.php | 64 + .../Descriptor/AbstractDescriptorTest.php | 105 + .../Tests/Descriptor/JsonDescriptorTest.php | 35 + .../Descriptor/MarkdownDescriptorTest.php | 27 + .../Tests/Descriptor/ObjectsProvider.php | 76 + .../Tests/Descriptor/TextDescriptorTest.php | 27 + .../Tests/Descriptor/XmlDescriptorTest.php | 27 + .../console/Tests/Fixtures/BarBucCommand.php | 11 + .../Tests/Fixtures/DescriptorApplication1.php | 18 + .../Tests/Fixtures/DescriptorApplication2.php | 24 + .../Tests/Fixtures/DescriptorCommand1.php | 27 + .../Tests/Fixtures/DescriptorCommand2.php | 32 + .../console/Tests/Fixtures/DummyOutput.php | 36 + .../console/Tests/Fixtures/Foo1Command.php | 26 + .../console/Tests/Fixtures/Foo2Command.php | 21 + .../console/Tests/Fixtures/Foo3Command.php | 29 + .../console/Tests/Fixtures/Foo4Command.php | 11 + .../console/Tests/Fixtures/Foo5Command.php | 10 + .../console/Tests/Fixtures/FooCommand.php | 33 + .../Fixtures/FooSubnamespaced1Command.php | 26 + .../Fixtures/FooSubnamespaced2Command.php | 26 + .../console/Tests/Fixtures/FoobarCommand.php | 25 + .../Style/SymfonyStyle/command/command_0.php | 11 + .../Style/SymfonyStyle/command/command_1.php | 13 + .../Style/SymfonyStyle/command/command_2.php | 16 + .../Style/SymfonyStyle/command/command_3.php | 12 + .../Style/SymfonyStyle/command/command_4.php | 34 + .../Style/SymfonyStyle/command/command_5.php | 29 + .../Style/SymfonyStyle/command/command_6.php | 16 + .../Style/SymfonyStyle/command/command_7.php | 15 + .../Style/SymfonyStyle/output/output_0.txt | 3 + .../Style/SymfonyStyle/output/output_1.txt | 9 + .../Style/SymfonyStyle/output/output_2.txt | 13 + .../Style/SymfonyStyle/output/output_3.txt | 7 + .../Style/SymfonyStyle/output/output_4.txt | 32 + .../Style/SymfonyStyle/output/output_5.txt | 11 + .../Style/SymfonyStyle/output/output_6.txt | 6 + .../Style/SymfonyStyle/output/output_7.txt | 5 + .../console/Tests/Fixtures/TestCommand.php | 28 + .../console/Tests/Fixtures/application_1.json | 1 + .../console/Tests/Fixtures/application_1.md | 201 + .../console/Tests/Fixtures/application_1.txt | 17 + .../console/Tests/Fixtures/application_1.xml | 110 + .../console/Tests/Fixtures/application_2.json | 1 + .../console/Tests/Fixtures/application_2.md | 396 ++ .../console/Tests/Fixtures/application_2.txt | 22 + .../console/Tests/Fixtures/application_2.xml | 190 + .../Tests/Fixtures/application_astext1.txt | 20 + .../Tests/Fixtures/application_astext2.txt | 16 + .../Tests/Fixtures/application_asxml1.txt | 146 + .../Tests/Fixtures/application_asxml2.txt | 37 + .../Tests/Fixtures/application_gethelp.txt | 1 + .../Fixtures/application_renderexception1.txt | 8 + .../Fixtures/application_renderexception2.txt | 11 + .../Fixtures/application_renderexception3.txt | 27 + .../application_renderexception3decorated.txt | 27 + .../Fixtures/application_renderexception4.txt | 9 + ...plication_renderexception_doublewidth1.txt | 11 + ..._renderexception_doublewidth1decorated.txt | 11 + ...plication_renderexception_doublewidth2.txt | 12 + .../Tests/Fixtures/application_run1.txt | 17 + .../Tests/Fixtures/application_run2.txt | 29 + .../Tests/Fixtures/application_run3.txt | 27 + .../Tests/Fixtures/application_run4.txt | 1 + .../console/Tests/Fixtures/command_1.json | 1 + .../console/Tests/Fixtures/command_1.md | 11 + .../console/Tests/Fixtures/command_1.txt | 7 + .../console/Tests/Fixtures/command_1.xml | 12 + .../console/Tests/Fixtures/command_2.json | 1 + .../console/Tests/Fixtures/command_2.md | 33 + .../console/Tests/Fixtures/command_2.txt | 13 + .../console/Tests/Fixtures/command_2.xml | 21 + .../console/Tests/Fixtures/command_astext.txt | 18 + .../console/Tests/Fixtures/command_asxml.txt | 38 + .../Tests/Fixtures/definition_astext.txt | 11 + .../Tests/Fixtures/definition_asxml.txt | 39 + .../Tests/Fixtures/input_argument_1.json | 1 + .../Tests/Fixtures/input_argument_1.md | 7 + .../Tests/Fixtures/input_argument_1.txt | 1 + .../Tests/Fixtures/input_argument_1.xml | 5 + .../Tests/Fixtures/input_argument_2.json | 1 + .../Tests/Fixtures/input_argument_2.md | 7 + .../Tests/Fixtures/input_argument_2.txt | 1 + .../Tests/Fixtures/input_argument_2.xml | 5 + .../Tests/Fixtures/input_argument_3.json | 1 + .../Tests/Fixtures/input_argument_3.md | 7 + .../Tests/Fixtures/input_argument_3.txt | 1 + .../Tests/Fixtures/input_argument_3.xml | 7 + .../Tests/Fixtures/input_argument_4.json | 1 + .../Tests/Fixtures/input_argument_4.md | 8 + .../Tests/Fixtures/input_argument_4.txt | 2 + .../Tests/Fixtures/input_argument_4.xml | 6 + .../Tests/Fixtures/input_definition_1.json | 1 + .../Tests/Fixtures/input_definition_1.md | 0 .../Tests/Fixtures/input_definition_1.txt | 0 .../Tests/Fixtures/input_definition_1.xml | 5 + .../Tests/Fixtures/input_definition_2.json | 1 + .../Tests/Fixtures/input_definition_2.md | 9 + .../Tests/Fixtures/input_definition_2.txt | 2 + .../Tests/Fixtures/input_definition_2.xml | 10 + .../Tests/Fixtures/input_definition_3.json | 1 + .../Tests/Fixtures/input_definition_3.md | 11 + .../Tests/Fixtures/input_definition_3.txt | 2 + .../Tests/Fixtures/input_definition_3.xml | 9 + .../Tests/Fixtures/input_definition_4.json | 1 + .../Tests/Fixtures/input_definition_4.md | 21 + .../Tests/Fixtures/input_definition_4.txt | 5 + .../Tests/Fixtures/input_definition_4.xml | 14 + .../Tests/Fixtures/input_option_1.json | 1 + .../console/Tests/Fixtures/input_option_1.md | 9 + .../console/Tests/Fixtures/input_option_1.txt | 1 + .../console/Tests/Fixtures/input_option_1.xml | 4 + .../Tests/Fixtures/input_option_2.json | 1 + .../console/Tests/Fixtures/input_option_2.md | 9 + .../console/Tests/Fixtures/input_option_2.txt | 1 + .../console/Tests/Fixtures/input_option_2.xml | 7 + .../Tests/Fixtures/input_option_3.json | 1 + .../console/Tests/Fixtures/input_option_3.md | 9 + .../console/Tests/Fixtures/input_option_3.txt | 1 + .../console/Tests/Fixtures/input_option_3.xml | 5 + .../Tests/Fixtures/input_option_4.json | 1 + .../console/Tests/Fixtures/input_option_4.md | 9 + .../console/Tests/Fixtures/input_option_4.txt | 1 + .../console/Tests/Fixtures/input_option_4.xml | 5 + .../Tests/Fixtures/input_option_5.json | 1 + .../console/Tests/Fixtures/input_option_5.md | 10 + .../console/Tests/Fixtures/input_option_5.txt | 2 + .../console/Tests/Fixtures/input_option_5.xml | 6 + .../OutputFormatterStyleStackTest.php | 70 + .../Formatter/OutputFormatterStyleTest.php | 93 + .../Tests/Formatter/OutputFormatterTest.php | 273 ++ .../Tests/Helper/FormatterHelperTest.php | 99 + .../console/Tests/Helper/HelperSetTest.php | 153 + .../Tests/Helper/LegacyDialogHelperTest.php | 200 + .../Tests/Helper/LegacyProgressHelperTest.php | 232 + .../Tests/Helper/LegacyTableHelperTest.php | 325 ++ .../Tests/Helper/ProcessHelperTest.php | 118 + .../console/Tests/Helper/ProgressBarTest.php | 598 +++ .../Tests/Helper/QuestionHelperTest.php | 376 ++ .../console/Tests/Helper/TableStyleTest.php | 27 + .../console/Tests/Helper/TableTest.php | 566 +++ .../console/Tests/Input/ArgvInputTest.php | 317 ++ .../console/Tests/Input/ArrayInputTest.php | 138 + .../console/Tests/Input/InputArgumentTest.php | 111 + .../Tests/Input/InputDefinitionTest.php | 441 ++ .../console/Tests/Input/InputOptionTest.php | 204 + .../symfony/console/Tests/Input/InputTest.php | 121 + .../console/Tests/Input/StringInputTest.php | 101 + .../Tests/Logger/ConsoleLoggerTest.php | 58 + .../Tests/Output/ConsoleOutputTest.php | 25 + .../console/Tests/Output/NullOutputTest.php | 39 + .../console/Tests/Output/OutputTest.php | 156 + .../console/Tests/Output/StreamOutputTest.php | 60 + .../console/Tests/Style/SymfonyStyleTest.php | 64 + .../Tests/Tester/ApplicationTesterTest.php | 69 + .../Tests/Tester/CommandTesterTest.php | 84 + .../vendor/symfony/console/composer.json | 41 + .../vendor/symfony/console/phpunit.xml.dist | 29 + .../symfony/event-dispatcher/.gitignore | 3 + .../symfony/event-dispatcher/CHANGELOG.md | 23 + .../ContainerAwareEventDispatcher.php | 202 + .../Debug/TraceableEventDispatcher.php | 335 ++ .../TraceableEventDispatcherInterface.php | 34 + .../Debug/WrappedListener.php | 71 + .../RegisterListenersPass.php | 110 + .../vendor/symfony/event-dispatcher/Event.php | 134 + .../event-dispatcher/EventDispatcher.php | 185 + .../EventDispatcherInterface.php | 96 + .../EventSubscriberInterface.php | 50 + .../symfony/event-dispatcher/GenericEvent.php | 186 + .../ImmutableEventDispatcher.php | 93 + .../vendor/symfony/event-dispatcher/LICENSE | 19 + .../vendor/symfony/event-dispatcher/README.md | 27 + .../Tests/AbstractEventDispatcherTest.php | 385 ++ .../ContainerAwareEventDispatcherTest.php | 249 ++ .../Debug/TraceableEventDispatcherTest.php | 199 + .../RegisterListenersPassTest.php | 200 + .../Tests/EventDispatcherTest.php | 22 + .../event-dispatcher/Tests/EventTest.php | 100 + .../Tests/GenericEventTest.php | 139 + .../Tests/ImmutableEventDispatcherTest.php | 105 + .../symfony/event-dispatcher/composer.json | 42 + .../symfony/event-dispatcher/phpunit.xml.dist | 29 + .../vendor/symfony/finder/.gitignore | 3 + .../finder/Adapter/AbstractAdapter.php | 236 + .../finder/Adapter/AbstractFindAdapter.php | 327 ++ .../finder/Adapter/AdapterInterface.php | 144 + .../symfony/finder/Adapter/BsdFindAdapter.php | 103 + .../symfony/finder/Adapter/GnuFindAdapter.php | 104 + .../symfony/finder/Adapter/PhpAdapter.php | 98 + .../vendor/symfony/finder/CHANGELOG.md | 34 + .../symfony/finder/Comparator/Comparator.php | 98 + .../finder/Comparator/DateComparator.php | 53 + .../finder/Comparator/NumberComparator.php | 81 + .../Exception/AccessDeniedException.php | 19 + .../Exception/AdapterFailureException.php | 46 + .../finder/Exception/ExceptionInterface.php | 23 + .../OperationNotPermitedException.php | 19 + .../ShellCommandFailureException.php | 45 + .../symfony/finder/Expression/Expression.php | 146 + .../vendor/symfony/finder/Expression/Glob.php | 109 + .../symfony/finder/Expression/Regex.php | 321 ++ .../finder/Expression/ValueInterface.php | 60 + .../vendor/symfony/finder/Finder.php | 840 ++++ .../vendor/symfony/finder/Glob.php | 104 + .../finder/Iterator/CustomFilterIterator.php | 63 + .../Iterator/DateRangeFilterIterator.php | 60 + .../Iterator/DepthRangeFilterIterator.php | 47 + .../ExcludeDirectoryFilterIterator.php | 55 + .../finder/Iterator/FilePathsIterator.php | 131 + .../Iterator/FileTypeFilterIterator.php | 55 + .../Iterator/FilecontentFilterIterator.php | 76 + .../Iterator/FilenameFilterIterator.php | 67 + .../finder/Iterator/FilterIterator.php | 49 + .../Iterator/MultiplePcreFilterIterator.php | 66 + .../finder/Iterator/PathFilterIterator.php | 74 + .../Iterator/RecursiveDirectoryIterator.php | 126 + .../Iterator/SizeRangeFilterIterator.php | 59 + .../finder/Iterator/SortableIterator.php | 82 + .../vendor/symfony/finder/LICENSE | 19 + .../vendor/symfony/finder/README.md | 53 + .../vendor/symfony/finder/Shell/Command.php | 294 ++ .../vendor/symfony/finder/Shell/Shell.php | 97 + .../vendor/symfony/finder/SplFileInfo.php | 77 + .../Tests/Comparator/ComparatorTest.php | 64 + .../Tests/Comparator/DateComparatorTest.php | 63 + .../Tests/Comparator/NumberComparatorTest.php | 107 + .../Tests/Expression/ExpressionTest.php | 68 + .../finder/Tests/Expression/GlobTest.php | 47 + .../finder/Tests/Expression/RegexTest.php | 143 + .../finder/Tests/FakeAdapter/DummyAdapter.php | 57 + .../Tests/FakeAdapter/FailingAdapter.php | 45 + .../finder/Tests/FakeAdapter/NamedAdapter.php | 57 + .../Tests/FakeAdapter/UnsupportedAdapter.php | 44 + .../symfony/finder/Tests/FinderTest.php | 866 ++++ .../finder/Tests/Fixtures/A/B/C/abc.dat | 0 .../symfony/finder/Tests/Fixtures/A/B/ab.dat | 0 .../symfony/finder/Tests/Fixtures/A/a.dat | 0 .../Tests/Fixtures/copy/A/B/C/abc.dat.copy | 0 .../Tests/Fixtures/copy/A/B/ab.dat.copy | 0 .../finder/Tests/Fixtures/copy/A/a.dat.copy | 0 .../symfony/finder/Tests/Fixtures/dolor.txt | 2 + .../symfony/finder/Tests/Fixtures/ipsum.txt | 2 + .../symfony/finder/Tests/Fixtures/lorem.txt | 2 + .../symfony/finder/Tests/Fixtures/one/a | 0 .../finder/Tests/Fixtures/one/b/c.neon | 0 .../finder/Tests/Fixtures/one/b/d.neon | 0 .../Fixtures/r+e.gex[c]a(r)s/dir/bar.dat | 0 .../finder/Tests/Fixtures/with space/foo.txt | 0 .../vendor/symfony/finder/Tests/GlobTest.php | 25 + .../Iterator/CustomFilterIteratorTest.php | 46 + .../Iterator/DateRangeFilterIteratorTest.php | 72 + .../Iterator/DepthRangeFilterIteratorTest.php | 80 + .../ExcludeDirectoryFilterIteratorTest.php | 64 + .../Tests/Iterator/FilePathsIteratorTest.php | 69 + .../Iterator/FileTypeFilterIteratorTest.php | 72 + .../FilecontentFilterIteratorTest.php | 86 + .../Iterator/FilenameFilterIteratorTest.php | 54 + .../Tests/Iterator/FilterIteratorTest.php | 50 + .../finder/Tests/Iterator/Iterator.php | 55 + .../Tests/Iterator/IteratorTestCase.php | 98 + .../Tests/Iterator/MockFileListIterator.php | 21 + .../finder/Tests/Iterator/MockSplFileInfo.php | 134 + .../MultiplePcreFilterIteratorTest.php | 67 + .../Tests/Iterator/PathFilterIteratorTest.php | 83 + .../Tests/Iterator/RealIteratorTestCase.php | 109 + .../RecursiveDirectoryIteratorTest.php | 83 + .../Iterator/SizeRangeFilterIteratorTest.php | 68 + .../Tests/Iterator/SortableIteratorTest.php | 169 + .../vendor/symfony/finder/composer.json | 33 + .../vendor/symfony/finder/phpunit.xml.dist | 28 + .../vendor/symfony/process/.gitignore | 3 + .../vendor/symfony/process/CHANGELOG.md | 40 + .../process/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../process/Exception/LogicException.php | 21 + .../Exception/ProcessFailedException.php | 53 + .../Exception/ProcessTimedOutException.php | 69 + .../process/Exception/RuntimeException.php | 21 + .../symfony/process/ExecutableFinder.php | 89 + .../vendor/symfony/process/LICENSE | 19 + .../symfony/process/PhpExecutableFinder.php | 86 + .../vendor/symfony/process/PhpProcess.php | 71 + .../symfony/process/Pipes/AbstractPipes.php | 74 + .../symfony/process/Pipes/PipesInterface.php | 60 + .../symfony/process/Pipes/UnixPipes.php | 214 + .../symfony/process/Pipes/WindowsPipes.php | 254 ++ .../vendor/symfony/process/Process.php | 1526 +++++++ .../vendor/symfony/process/ProcessBuilder.php | 287 ++ .../vendor/symfony/process/ProcessUtils.php | 115 + .../vendor/symfony/process/README.md | 51 + .../process/Tests/AbstractProcessTest.php | 1198 +++++ .../process/Tests/ExecutableFinderTest.php | 149 + .../process/Tests/NonStopableProcess.php | 36 + .../process/Tests/PhpExecutableFinderTest.php | 97 + .../symfony/process/Tests/PhpProcessTest.php | 49 + .../PipeStdinInStdoutStdErrStreamSelect.php | 63 + .../process/Tests/ProcessBuilderTest.php | 225 + .../Tests/ProcessFailedExceptionTest.php | 136 + .../Tests/ProcessInSigchildEnvironment.php | 22 + .../process/Tests/ProcessUtilsTest.php | 48 + .../Tests/SigchildDisabledProcessTest.php | 263 ++ .../Tests/SigchildEnabledProcessTest.php | 148 + .../symfony/process/Tests/SignalListener.php | 16 + .../process/Tests/SimpleProcessTest.php | 222 + .../vendor/symfony/process/composer.json | 33 + .../vendor/symfony/process/phpunit.xml.dist | 27 + .../vendor/symfony/yaml/.gitignore | 3 + .../vendor/symfony/yaml/CHANGELOG.md | 8 + .../vendor/symfony/yaml/Dumper.php | 73 + .../vendor/symfony/yaml/Escaper.php | 97 + .../symfony/yaml/Exception/DumpException.php | 23 + .../yaml/Exception/ExceptionInterface.php | 23 + .../symfony/yaml/Exception/ParseException.php | 148 + .../yaml/Exception/RuntimeException.php | 23 + .../vendor/symfony/yaml/Inline.php | 546 +++ .../vendor/symfony/yaml/LICENSE | 19 + .../vendor/symfony/yaml/Parser.php | 697 +++ .../vendor/symfony/yaml/README.md | 21 + .../vendor/symfony/yaml/Tests/DumperTest.php | 236 + .../yaml/Tests/Fixtures/YtsAnchorAlias.yml | 31 + .../yaml/Tests/Fixtures/YtsBasicTests.yml | 202 + .../yaml/Tests/Fixtures/YtsBlockMapping.yml | 51 + .../Tests/Fixtures/YtsDocumentSeparator.yml | 85 + .../yaml/Tests/Fixtures/YtsErrorTests.yml | 25 + .../Tests/Fixtures/YtsFlowCollections.yml | 60 + .../yaml/Tests/Fixtures/YtsFoldedScalars.yml | 176 + .../Tests/Fixtures/YtsNullsAndEmpties.yml | 45 + .../Fixtures/YtsSpecificationExamples.yml | 1697 ++++++++ .../yaml/Tests/Fixtures/YtsTypeTransfers.yml | 244 ++ .../yaml/Tests/Fixtures/embededPhp.yml | 1 + .../yaml/Tests/Fixtures/escapedCharacters.yml | 147 + .../symfony/yaml/Tests/Fixtures/index.yml | 18 + .../yaml/Tests/Fixtures/sfComments.yml | 73 + .../symfony/yaml/Tests/Fixtures/sfCompact.yml | 159 + .../yaml/Tests/Fixtures/sfMergeKey.yml | 45 + .../symfony/yaml/Tests/Fixtures/sfObjects.yml | 11 + .../symfony/yaml/Tests/Fixtures/sfQuotes.yml | 33 + .../symfony/yaml/Tests/Fixtures/sfTests.yml | 135 + .../Tests/Fixtures/unindentedCollections.yml | 82 + .../vendor/symfony/yaml/Tests/InlineTest.php | 380 ++ .../symfony/yaml/Tests/ParseExceptionTest.php | 41 + .../vendor/symfony/yaml/Tests/ParserTest.php | 740 ++++ .../vendor/symfony/yaml/Tests/YamlTest.php | 39 + .../vendor/symfony/yaml/Unescaper.php | 146 + .../vendor/symfony/yaml/Yaml.php | 105 + .../vendor/symfony/yaml/composer.json | 33 + .../vendor/symfony/yaml/phpunit.xml.dist | 28 + .../providers/digitalocean/options.php | 141 + 2017 files changed, 214198 insertions(+), 24 deletions(-) create mode 100644 modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.drush.inc create mode 100644 modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.info create mode 100644 modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.module create mode 100644 modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.service.inc create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/.scrutinizer.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.lock create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AbstractAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AdapterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzOAuthListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/Guzzle5Adapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/GuzzleAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/AbstractApi.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Account.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Action.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Domain.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/DomainRecord.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Droplet.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Image.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Key.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/RateLimit.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Region.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Size.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/DigitalOceanV2.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/AbstractEntity.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Account.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Action.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Domain.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/DomainRecord.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Droplet.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Image.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Kernel.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Key.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Meta.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Network.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/NextBackupWindow.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/RateLimit.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Region.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Size.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Upgrade.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionReader.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ResponseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/functions.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/autoload.php create mode 120000 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/bin/phpspec create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/ClassLoader.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_classmap.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_files.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_namespaces.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_psr4.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_real.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/installed.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.scrutinizer.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.install.sh create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/CONTRIBUTING.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpmd.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/UPGRADING.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/build.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/Makefile create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/homepage.css create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/logo.png create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.css create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.js create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/index.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/leftbar.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/nav_links.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/batching/batching.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/conf.py create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/docs.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/faq.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/installation.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/overview.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/client.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/request.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/response.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/index.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/requirements.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/testing/unit-testing.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phar-stub.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/build.properties.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/dependencies.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/deploy.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Version.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Request.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Response.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/Operation.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/GuzzleTestCase.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockMulti.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserProvider.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/MockClient.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/bootstrap.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/.editorconfig create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/Makefile create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/UPGRADING.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/build/packager.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/Makefile create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_static/logo.png create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/clients.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/conf.py create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/events.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/faq.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/handlers.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/http-messages.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/index.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/overview.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/quickstart.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/requirements.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/streams.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/testing.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/BatchResults.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Client.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ClientInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Collection.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/Emitter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EndEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/SubscriberInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/ParseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/StateException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/XmlParseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/HasDataTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/FutureResponse.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/Request.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/Response.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Mimetypes.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Pool.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBody.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostFile.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Query.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/QueryParser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RequestFsm.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RingBridge.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Cookie.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/History.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Transaction.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/UriTemplate.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Url.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Utils.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/ClientTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/CollectionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/PoolTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Server.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/TransactionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UrlTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UtilsTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/bootstrap.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/perf.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/Makefile create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/README.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/Makefile create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_handlers.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_middleware.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/conf.py create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/futures.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/index.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/requirements.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/spec.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/testing.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/Middleware.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Core.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Exception/RingException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/Server.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/server.js create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/CoreTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/bootstrap.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/CHANGELOG.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/Makefile create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/README.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AppendStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AsyncReadStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/BufferStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/CachingStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/DroppingStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Exception/SeekException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/FnStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/InflateStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LazyOpenStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LimitStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/NoSeekStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/NullStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/PumpStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Stream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Utils.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AppendStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/BufferStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/CachingStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/FnStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/InflateStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LimitStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NullStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/PumpStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/UtilsTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Browser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractClient.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractCurl.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractStream.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/BatchClientInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/ClientInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/Curl.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/FileGetContents.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/MultiCurl.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ClientException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ExceptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/InvalidArgumentException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/LogicException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RequestException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RuntimeException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/BasicAuthListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CallbackListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Entry.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Journal.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/HistoryListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerChain.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/LoggerListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/AbstractMessage.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Factory/Factory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Factory/FactoryInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormRequest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormRequestInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUpload.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUploadInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/MessageInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Request.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/RequestInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Response.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Cookie.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/CookieJar.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Url.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/BrowserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/AbstractStreamTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/ClientTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/FunctionalTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/BasicAuthListenerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/CallbackListenerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/EntryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/JournalTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/HistoryListenerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/ListenerChainTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/LoggerListenerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/AbstractMessageTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/Fixtures/google.png create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormRequestTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormUploadTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/RequestTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/ResponseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieJarTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/UrlTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/nginx.conf create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/squid.conf create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/server.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/composer.lock create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Context.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Description.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Location.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Serializer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VarTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Type/Collection.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/DescriptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/CoversTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ExampleTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/LinkTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/MethodTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ParamTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ReturnTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SeeTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SinceTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SourceTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/UsesTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VarTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VersionTagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/TagTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Type/CollectionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlockTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/README create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/a.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/b.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/example.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/styles.css create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Abstract.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Array.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Inline.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/SideBySide.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Context.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Unified.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/SequenceMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitattributes create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.scrutinizer.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CHANGES.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CONTRIBUTING.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/Makefile create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/README.rst create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/behat.yml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/bin/phpspec create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/box.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/ApplicationContext.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/Fake/Prompter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/Fake/ReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/FilesystemContext.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/IsolatedProcessContext.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/Matcher/ApplicationOutputMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/Matcher/FileExistsMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/Matcher/FileHasContentsMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/Matcher/ValidJUnitXmlMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_generates_class.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_generates_collaborator.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_generates_collaborator_method.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_generates_method.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_generates_named_constructor.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_generates_return_constant.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_generates_spec.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/code_generation/developer_reruns_features.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/construction/developer_specifies_object_construction.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/exception_handling/developer_specifies_exceptions.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/formatter/developer_is_shown_diffs.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/formatter/use_the_junit_formatter.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/formatter/use_the_tap_formatter.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_users_array_key_value_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_array_contain_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_array_key_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_comparison_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_count_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_identity_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_inline_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_object_state_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_scalar_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_string_end_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_string_regex_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_string_start_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_throw_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/matchers/developer_uses_type_matcher.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/options/developer_chooses_no_code_generation.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/options/developer_chooses_stop_on_failure.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/runner/developer_is_told_about_pending_specs.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/runner/developer_runs_specs.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/runner/developer_runs_specs_with_bootstrap.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/runner/developer_skips_specs.feature create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/integration/PhpSpec/Console/Prompter/DialogTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/integration/PhpSpec/Console/Prompter/FactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/integration/PhpSpec/Console/Prompter/QuestionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/phpspec.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/phpunit.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/CodeGenerator/Generator/ClassGeneratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/CodeGenerator/Generator/MethodGeneratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/CodeGenerator/Generator/NamedConstructorGeneratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/CodeGenerator/Generator/ReturnConstantGeneratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/CodeGenerator/Generator/SpecificationGeneratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/CodeGenerator/GeneratorManagerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/CodeGenerator/TemplateRendererSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Config/OptionsConfigSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Console/ApplicationSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Console/IOSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Console/ResultConverterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Event/ExampleEventSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Event/ExpectationEventSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Event/MethodCallEventSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Event/SpecificationEventSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Event/SuiteEventSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Example/NotEqualExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Example/StopOnFailureExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/ExceptionFactorySpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/ExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Fracture/ClassNotFoundExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Fracture/InterfaceNotImplementedExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Fracture/MethodNotFoundExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Fracture/MethodNotVisibleExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Fracture/NamedConstructorNotFoundExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Exception/Fracture/PropertyNotFoundExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/BasicFormatterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/DotFormatterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Html/HtmlPresenterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Html/IOSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Html/ReportFailedItemSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Html/ReportItemFactorySpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Html/ReportPassedItemSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Html/ReportPendingItemSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Html/TemplateSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/HtmlFormatterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/JUnitFormatterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Presenter/Differ/ArrayEngineSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Presenter/Differ/DifferSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Presenter/Differ/ObjectEngineSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Presenter/Differ/StringEngineSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Presenter/StringPresenterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/Presenter/TaggedPresenterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/ProgressFormatterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Formatter/TapFormatterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/ClassNotFoundListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/CollaboratorMethodNotFoundListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/CollaboratorNotFoundListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/MethodNotFoundListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/MethodReturnedNullListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/NamedConstructorNotFoundListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/RerunListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/StatisticsCollectorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Listener/StopOnFailureListenerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Loader/Node/ExampleNodeSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Loader/Node/SpecificationNodeSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Loader/SuiteSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Locator/PSR0/PSR0LocatorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Locator/PSR0/PSR0ResourceSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Locator/ResourceManagerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/ArrayContainMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/ArrayCountMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/ArrayKeyMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/ArrayKeyValueMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/CallbackMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/ComparisonMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/IdentityMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/ObjectStateMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/StringEndMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/StringRegexMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/StringStartMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/ThrowMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Matcher/TypeMatcherSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Process/ReRunner/CompositeReRunnerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Process/ReRunner/OptionalReRunnerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Process/ReRunner/PassthruRerunnerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Process/ReRunner/PcntlReRunnerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Runner/CollaboratorManagerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Runner/ExampleRunnerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Runner/Maintainer/MatchersMaintainerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Runner/MatcherManagerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Runner/SpecificationRunnerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Runner/SuiteRunnerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/ServiceContainerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Util/ExampleObjectUsingTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Util/ExampleTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Util/InstantiatorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Util/MethodAnalyserSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/CallerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/Expectation/ConstructorDecoratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/Expectation/DecoratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/Expectation/DispatcherDecoratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/Expectation/NegativeSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/Expectation/PositiveSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/ExpectationFactorySpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/Subject/WrappedObjectSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/spec/PhpSpec/Wrapper/SubjectSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/ClassGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/CreateObjectTemplate.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/ExistingConstructorTemplate.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/GeneratorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/InterfaceGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/MethodGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/MethodSignatureGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/NamedConstructorGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/PromptingGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/ReturnConstantGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/SpecificationGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/class.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/interface.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/interface_method_signature.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/method.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/named_constructor_create_object.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/named_constructor_exception.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/returnconstant.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/Generator/templates/specification.template create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/GeneratorManager.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/CodeGenerator/TemplateRenderer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Config/OptionsConfig.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Application.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Command/DescribeCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Command/RunCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/ContainerAssembler.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Formatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/IO.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Prompter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Prompter/Dialog.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Prompter/Factory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/Prompter/Question.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Console/ResultConverter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Event/EventInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Event/ExampleEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Event/ExpectationEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Event/MethodCallEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Event/SpecificationEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Event/SuiteEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/ErrorException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/ExampleException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/FailureException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/MatcherException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/NotEqualException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/PendingException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/SkippingException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Example/StopOnFailureException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Exception.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/ExceptionFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/ClassNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/CollaboratorNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/FactoryDoesNotReturnObjectException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/FractureException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/InterfaceNotImplementedException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/MethodInvocationException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/MethodNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/MethodNotVisibleException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/NamedConstructorNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Fracture/PropertyNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Locator/ResourceCreationException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Wrapper/CollaboratorException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Wrapper/MatcherNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Exception/Wrapper/SubjectException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Extension/ExtensionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Factory/ReflectionFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/BasicFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/ConsoleFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/DotFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/HtmlPresenter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/IO.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/InvalidExampleResultException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/ReportFailedItem.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/ReportItem.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/ReportItemFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/ReportPassedItem.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/ReportPendingItem.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/ReportSkippedItem.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportFailed.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportFooter.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportHeader.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportLine.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportPass.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportPending.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportRed.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportSkipped.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportSpecificationEnds.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportSpecificationStarts.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Html/Template/ReportSummary.html create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/HtmlFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/JUnitFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/Differ/ArrayEngine.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/Differ/Differ.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/Differ/EngineInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/Differ/ObjectEngine.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/Differ/StringEngine.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/PresenterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/StringPresenter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Presenter/TaggedPresenter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/PrettyFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/ProgressFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/TapFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Formatter/Template.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/IO/IOInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/BootstrapListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/ClassNotFoundListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/CollaboratorMethodNotFoundListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/CollaboratorNotFoundListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/MethodNotFoundListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/MethodReturnedNullListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/NamedConstructorNotFoundListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/RerunListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/StatisticsCollector.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Listener/StopOnFailureListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Loader/Node/ExampleNode.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Loader/Node/SpecificationNode.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Loader/ResourceLoader.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Loader/Suite.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Locator/PSR0/PSR0Locator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Locator/PSR0/PSR0Resource.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Locator/ResourceInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Locator/ResourceLocatorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Locator/ResourceManager.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Locator/ResourceManagerInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ArrayContainMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ArrayCountMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ArrayKeyMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ArrayKeyValueMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/BasicMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/CallbackMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ComparisonMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/IdentityMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/MatcherInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/MatchersProviderInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ObjectStateMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ScalarMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/StringEndMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/StringRegexMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/StringStartMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/ThrowMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Matcher/TypeMatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/ObjectBehavior.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Process/ReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Process/ReRunner/CompositeReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Process/ReRunner/OptionalReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Process/ReRunner/PassthruReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Process/ReRunner/PcntlReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Process/ReRunner/PhpExecutableReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Process/ReRunner/PlatformSpecificReRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Resources/schema/junit.xsd create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/CollaboratorManager.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/ExampleRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/Maintainer/CollaboratorsMaintainer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/Maintainer/ErrorMaintainer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/Maintainer/LetAndLetgoMaintainer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/Maintainer/MaintainerInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/Maintainer/MatchersMaintainer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/Maintainer/SubjectMaintainer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/MatcherManager.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/SpecificationRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Runner/SuiteRunner.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/ServiceContainer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/SpecificationInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Util/Filesystem.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Util/Instantiator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Util/MethodAnalyser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Collaborator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/DelayedCall.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Caller.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/ConstructorDecorator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/Decorator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/DispatcherDecorator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/DuringCall.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/ExpectationInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/Negative.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/NegativeThrow.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/Positive.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/PositiveThrow.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/ThrowExpectation.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Expectation/UnwrapDecorator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/ExpectationFactory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/SubjectWithArrayAccess.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/WrappedObject.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/SubjectContainerInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Unwrapper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Wrapper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/WrapperInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/CHANGES.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/CONTRIBUTING.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/ArgumentsWildcardSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/AnyValueTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/AnyValuesTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ArrayCountTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ArrayEntryTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ArrayEveryEntryTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/CallbackTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ExactValueTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/IdenticalValueTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/LogicalAndTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/LogicalNotTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/ObjectStateTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/StringContainsTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Argument/Token/TypeTokenSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/ArgumentSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Call/CallCenterSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Call/CallSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Comparator/ClosureComparatorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Comparator/FactorySpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/DisableConstructorPatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/HhvmExceptionPatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/KeywordPatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/MagicCallPatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/ProphecySubjectPatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/SplFileInfoPatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/ClassPatch/TraversablePatchSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/DoublerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/ClassCodeGeneratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/ClassCreatorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/ClassMirrorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/Node/ArgumentNodeSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/Node/ClassNodeSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/LazyDoubleSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Doubler/NameGeneratorSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Call/UnexpectedCallExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/ClassCreatorExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/ClassMirrorExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/ClassNotFoundExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/DoubleExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/InterfaceNotFoundExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Doubler/MethodNotFoundExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/AggregateExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/NoCallsExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/UnexpectedCallsCountExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Prediction/UnexpectedCallsExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Prophecy/MethodProphecyExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Exception/Prophecy/ObjectProphecyExceptionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Prediction/CallPredictionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Prediction/CallTimesPredictionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Prediction/CallbackPredictionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Prediction/NoCallsPredictionSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Promise/CallbackPromiseSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Promise/ReturnArgumentPromiseSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Promise/ReturnPromiseSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Promise/ThrowPromiseSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Prophecy/MethodProphecySpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Prophecy/ObjectProphecySpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Prophecy/RevealerSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/ProphetSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/spec/Prophecy/Util/StringUtilSpec.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Call/Call.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Call/CallCenter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Comparator/Factory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Exception.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Prophet.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Util/ExportUtil.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/prophecy/src/Prophecy/Util/StringUtil.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/CancellablePromiseInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/Deferred.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/ExtendedPromiseInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/FulfilledPromise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/LazyPromise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/Promise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/PromiseInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/PromisorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/RejectedPromise.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/UnhandledRejectionException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/functions.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/src/functions_include.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/DeferredTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FulfilledPromiseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionAllTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionAnyTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionCheckTypehintTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionMapTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionRaceTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionReduceTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionRejectTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionResolveTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/FunctionSomeTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/LazyPromiseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/FullTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/NotifyTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/RejectedPromiseTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/Stub/CallableStub.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/TestCase.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/react/promise/tests/bootstrap.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/build.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/build/travis-ci.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/ArrayComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/Comparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/ComparisonFailure.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/DOMNodeComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/DateTimeComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/DoubleComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/ExceptionComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/Factory.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/MockObjectComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/NumericComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/ObjectComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/ResourceComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/ScalarComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/SplObjectStorageComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/src/TypeComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/ArrayComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/DOMNodeComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/DateTimeComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/DoubleComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/ExceptionComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/FactoryTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/MockObjectComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/NumericComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/ObjectComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/ResourceComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/ScalarComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/SplObjectStorageComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/TypeComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/_files/Author.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/_files/Book.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/_files/ClassWithToString.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/_files/SampleClass.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/_files/Struct.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/_files/TestClass.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/_files/TestClassComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/autoload.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/comparator/tests/bootstrap.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/build.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/Chunk.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/Diff.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/Differ.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/LCS/LongestCommonSubsequence.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/Line.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/src/Parser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/tests/DifferTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/tests/LCS/TimeEfficientImplementationTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/tests/ParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/tests/fixtures/patch.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/diff/tests/fixtures/patch2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/build.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/src/Exporter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/exporter/tests/ExporterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/.travis.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/build.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/src/Context.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/src/Exception.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/src/InvalidArgumentException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/sebastian/recursion-context/tests/ContextTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Application.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Command/Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Command/HelpCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Command/ListCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/ConsoleEvents.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Descriptor/ApplicationDescription.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Descriptor/Descriptor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Descriptor/DescriptorInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Descriptor/JsonDescriptor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Descriptor/MarkdownDescriptor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Descriptor/TextDescriptor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Descriptor/XmlDescriptor.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Event/ConsoleCommandEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Event/ConsoleEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Event/ConsoleExceptionEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Event/ConsoleTerminateEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Formatter/OutputFormatter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Formatter/OutputFormatterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Formatter/OutputFormatterStyle.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/DebugFormatterHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/DescriptorHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/DialogHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/FormatterHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/Helper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/HelperInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/HelperSet.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/InputAwareHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/ProcessHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/ProgressBar.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/ProgressHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/QuestionHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/SymfonyQuestionHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/Table.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/TableCell.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/TableHelper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/TableSeparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Helper/TableStyle.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/ArgvInput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/ArrayInput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/Input.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/InputArgument.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/InputAwareInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/InputDefinition.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/InputInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/InputOption.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Input/StringInput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Logger/ConsoleLogger.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Output/BufferedOutput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Output/ConsoleOutput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Output/ConsoleOutputInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Output/NullOutput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Output/Output.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Output/OutputInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Output/StreamOutput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Question/ChoiceQuestion.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Question/ConfirmationQuestion.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Question/Question.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Resources/bin/hiddeninput.exe create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Shell.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Style/OutputStyle.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Style/StyleInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Style/SymfonyStyle.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tester/ApplicationTester.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tester/CommandTester.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/ApplicationTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Command/CommandTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Command/HelpCommandTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Command/ListCommandTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/DummyOutput.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Foo1Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Foo2Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Foo3Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Foo4Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Foo5Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/FooCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/TestCommand.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_1.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_1.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_1.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_2.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_2.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_2.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_astext1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_astext2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_asxml1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_asxml2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_run1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_run2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_run3.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/application_run4.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_1.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_1.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_1.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_2.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_2.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_2.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_astext.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/command_asxml.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/definition_astext.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/definition_asxml.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_1.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_1.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_2.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_2.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_3.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_3.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_4.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_4.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_1.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_1.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_2.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_2.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_3.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_3.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_4.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_4.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_1.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_1.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_1.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_1.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_2.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_2.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_2.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_2.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_3.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_3.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_3.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_3.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_4.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_4.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_4.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_4.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_5.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_5.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_5.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Fixtures/input_option_5.xml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/HelperSetTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/LegacyDialogHelperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/LegacyProgressHelperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/LegacyTableHelperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/ProgressBarTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/TableStyleTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Helper/TableTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Input/ArgvInputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Input/ArrayInputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Input/InputArgumentTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Input/InputDefinitionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Input/InputOptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Input/InputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Input/StringInputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Output/NullOutputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Output/OutputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Output/StreamOutputTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/Tests/Tester/CommandTesterTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/console/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Debug/WrappedListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Event.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/EventDispatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/EventDispatcherInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/EventSubscriberInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/GenericEvent.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/EventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/event-dispatcher/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Adapter/AbstractAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Adapter/AbstractFindAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Adapter/AdapterInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Adapter/BsdFindAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Adapter/GnuFindAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Adapter/PhpAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Comparator/Comparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Comparator/DateComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Comparator/NumberComparator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Exception/AccessDeniedException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Exception/AdapterFailureException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Exception/ExceptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Exception/OperationNotPermitedException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Exception/ShellCommandFailureException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Expression/Expression.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Expression/Glob.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Expression/Regex.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Expression/ValueInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Finder.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Glob.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/CustomFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/FilePathsIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/FilecontentFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/FilenameFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/FilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/PathFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Iterator/SortableIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Shell/Command.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Shell/Shell.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/SplFileInfo.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Comparator/ComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Comparator/DateComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Expression/ExpressionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Expression/GlobTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Expression/RegexTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/FakeAdapter/DummyAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/FakeAdapter/FailingAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/FakeAdapter/NamedAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/FakeAdapter/UnsupportedAdapter.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/FinderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/A/B/C/abc.dat create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/A/B/ab.dat create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/A/a.dat create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/copy/A/B/ab.dat.copy create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/copy/A/a.dat.copy create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/dolor.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/ipsum.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/lorem.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/one/a create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/one/b/c.neon create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/one/b/d.neon create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/r+e.gex[c]a(r)s/dir/bar.dat create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Fixtures/with space/foo.txt create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/GlobTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/FilePathsIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/FilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/Iterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/MockFileListIterator.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/finder/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Exception/ExceptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Exception/InvalidArgumentException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Exception/LogicException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Exception/ProcessFailedException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Exception/ProcessTimedOutException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Exception/RuntimeException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/ExecutableFinder.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/PhpExecutableFinder.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/PhpProcess.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Pipes/AbstractPipes.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Pipes/PipesInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Pipes/UnixPipes.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Pipes/WindowsPipes.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Process.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/ProcessBuilder.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/ProcessUtils.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/AbstractProcessTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/ExecutableFinderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/NonStopableProcess.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/PhpExecutableFinderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/PhpProcessTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/PipeStdinInStdoutStdErrStreamSelect.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/ProcessBuilderTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/ProcessFailedExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/ProcessInSigchildEnvironment.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/ProcessUtilsTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/SigchildDisabledProcessTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/SigchildEnabledProcessTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/SignalListener.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/Tests/SimpleProcessTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/process/phpunit.xml.dist create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/.gitignore create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/CHANGELOG.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Dumper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Escaper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Exception/DumpException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Exception/ExceptionInterface.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Exception/ParseException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Exception/RuntimeException.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Inline.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/LICENSE create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Parser.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/README.md create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/DumperTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsAnchorAlias.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsBasicTests.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsBlockMapping.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsDocumentSeparator.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsErrorTests.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsFlowCollections.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsFoldedScalars.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsNullsAndEmpties.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsSpecificationExamples.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/YtsTypeTransfers.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/embededPhp.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/escapedCharacters.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/index.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/sfComments.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/sfCompact.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/sfMergeKey.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/sfObjects.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/sfQuotes.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/sfTests.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/Fixtures/unindentedCollections.yml create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/InlineTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/ParseExceptionTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/ParserTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Tests/YamlTest.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Unescaper.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/Yaml.php create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/composer.json create mode 100755 modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/symfony/yaml/phpunit.xml.dist create mode 100644 modules/devshop/devshop_cloud/providers/digitalocean/options.php diff --git a/modules/devshop/devshop_cloud/devshop_cloud.drush.inc b/modules/devshop/devshop_cloud/devshop_cloud.drush.inc index e7ba37e59..222938357 100644 --- a/modules/devshop/devshop_cloud/devshop_cloud.drush.inc +++ b/modules/devshop/devshop_cloud/devshop_cloud.drush.inc @@ -18,7 +18,6 @@ function devshop_cloud_hosting_server_context_options(&$task) { * Implementation of hook_post_hosting_TASK_TYPE_task() for verified tasks. */ function devshop_cloud_post_hosting_verify_task($task, $data) { - // When a project is verified... if ($task->ref->type == 'server') { if (empty($data['context']['provider_server_identifier'])) { @@ -35,13 +34,13 @@ function devshop_cloud_post_hosting_verify_task($task, $data) { else { return drush_set_error('DEVSHOP_CLOUD_UNABLE_TO_SAVE', dt('Unable to save server ID and data to database.')); } -// -// // Save IP addresses. -// if (empty($task->ref->ip_addresses)){ -// $task->ref->new_ip_addresses = $data['context']['ip_addresses']; -// } -// else { -// $task->ref->ip_addresses = $data['context']['ip_addresses']; -// } + + // Save IP addresses. + if (empty($task->ref->ip_addresses)){ + $task->ref->new_ip_addresses = $data['context']['ip_addresses']; + } + else { + $task->ref->ip_addresses = $data['context']['ip_addresses']; + } } } diff --git a/modules/devshop/devshop_cloud/devshop_cloud.module b/modules/devshop/devshop_cloud/devshop_cloud.module index cda80d957..07449f7e9 100644 --- a/modules/devshop/devshop_cloud/devshop_cloud.module +++ b/modules/devshop/devshop_cloud/devshop_cloud.module @@ -62,14 +62,6 @@ function devshop_cloud_hosting_service_type() { ); } -/** - * Implements hook_hosting_service() - */ -function devshop_cloud_hosting_service() { - return array( - 'digitalocean' => 'provider', - ); -} /** * Implementation of hook_nodeapi() */ diff --git a/modules/devshop/devshop_cloud/devshop_cloud.service.inc b/modules/devshop/devshop_cloud/devshop_cloud.service.inc index dfe6d365d..e92237a0a 100644 --- a/modules/devshop/devshop_cloud/devshop_cloud.service.inc +++ b/modules/devshop/devshop_cloud/devshop_cloud.service.inc @@ -52,13 +52,6 @@ class hostingService_provider extends hostingService { } } -/** - * DigitalOcean provider. - */ -class hostingService_provider_digitalocean extends hostingService_provider { - public $type = 'digitalocean'; - -} /** * rackspace provider. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.drush.inc b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.drush.inc new file mode 100644 index 000000000..cc8461887 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.drush.inc @@ -0,0 +1,23 @@ +ref->type == 'server') { + $task->options['digital_ocean_token'] = $task->ref->digital_ocean_token; + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.info b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.info new file mode 100644 index 000000000..bbe9e7f73 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.info @@ -0,0 +1,6 @@ +name = DevShop Cloud: Digital Ocean +description = Launches servers on Digital Ocean. +core = 6.x +package = DevShop + +dependencies[] = devshop_cloud diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.module b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.module new file mode 100644 index 000000000..22032c72d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.module @@ -0,0 +1,68 @@ + 'Digital Ocean', + 'description' => 'Retrieve options from Digital Ocean.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('devshop_digital_ocean_options_form'), + 'access arguments' => array('administer devshop cloud'), + 'file' => 'options.php', + 'type' => MENU_LOCAL_TASK, + ); + return $items; +} +/** + * Implements hook_hosting_service() + */ +function devshop_digital_ocean_hosting_service() { + return array( + 'digital_ocean' => 'provider', + ); +} + +/** + * Implements hook_form_FORM_ID_alter() + * + * Alters devshop cloud settings form to add digital_ocean api keys. + */ +function devshop_digital_ocean_form_devshop_cloud_settings_form_alter(&$form, &$form_state) { + + $form['digital_ocean'] = array( + '#type' => 'fieldset', + '#title' => t('Digital Ocean'), + '#weight' => -100, + ); + + $form['digital_ocean']['devshop_cloud_digital_ocean_api_token'] = array( + '#type' => 'textfield', + '#title' => t('API token'), + '#description' => t('Enter your digital_ocean username here.'), + '#default_value' => variable_get('devshop_cloud_digital_ocean_api_token', ''), + ); + +} + +function devshop_digital_ocean_load_api(){ + require_once dirname(__FILE__) . '/digital-ocean-master/vendor/autoload.php'; + require_once dirname(__FILE__) . '/digital-ocean-master/src/DigitalOceanV2.php'; + $api_token = variable_get('devshop_cloud_digital_ocean_api_token', array()); + + $adapter = new BuzzAdapter($api_token); + $digitalocean = new DigitalOceanV2($adapter); + return $digitalocean; +} + +/** + * Implementation of hook_nodeapi() + */ +function devshop_digital_ocean_nodeapi_server_load(&$node) { + return array('digital_ocean_token' => variable_get('devshop_cloud_digital_ocean_api_token', array())); +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.service.inc b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.service.inc new file mode 100644 index 000000000..f8774af97 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/devshop_digital_ocean.service.inc @@ -0,0 +1,62 @@ +server->nid) { + $default_options = variable_get('devshop_digital_ocean_default_options', array()); + $options = $this->getDigitalOceanOptions(); + $form['provider_options'] = array(); + $form['provider_options']['region'] = array( + '#type' => 'select', + '#title' => t('Region'), + '#options' => $options['regions'], + '#default_value' => $default_options['devshop_digital_ocean_default_region'], + ); + $form['provider_options']['image'] = array( + '#type' => 'select', + '#title' => t('Image'), + '#options' => $options['images'], + '#default_value' => $default_options['devshop_digital_ocean_default_image'], + ); + $form['provider_options']['size'] = array( + '#type' => 'select', + '#title' => t('Server Size'), + '#options' => $options['sizes'], + '#default_value' => $default_options['devshop_digital_ocean_default_size'], + ); + $form['provider_options']['keys'] = array( + '#type' => 'checkboxes', + '#title' => t('SSH Keys'), + '#options' => $options['keys'], + '#default_value' => $default_options['devshop_digital_ocean_default_keys'], + ); + } + } + + /** + * Process options + */ + function getDigitalOceanOptions() { + $options = variable_get('devshop_cloud_digital_ocean_options', array()); + if (empty($options)) { + drupal_set_message(t('You must !link before you can create a server there.', array('!link' => l(t('retrieve Digital Ocean options'), 'admin/hosting/devshop/digital_ocean'))), 'error'); + return; + } + return $options; + } + + public function context_options($task_type, $ref_type, &$task) { + parent::context_options($task_type, $ref_type, $task); + $task->context_options['digital_ocean_token'] = variable_get('devshop_cloud_digital_ocean_api_token', NULL); + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/.scrutinizer.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/.scrutinizer.yml new file mode 100755 index 000000000..176594bd0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/.scrutinizer.yml @@ -0,0 +1,8 @@ +filter: + paths: [src/*] +tools: + php_analyzer: true + php_mess_detector: true + php_pdepend: true + external_code_coverage: + timeout: '600' diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/LICENSE new file mode 100755 index 000000000..0e93f2dc7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Antoine Corcy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.json new file mode 100755 index 000000000..35741ea81 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.json @@ -0,0 +1,58 @@ +{ + "name" : "toin0u/digitalocean-v2", + "type" : "library", + "description": "DigitalOcean API v2 PHP 5.4+ library", + "keywords" : ["DigitalOcean", "API", "Cloud Hosting", "SSD", "VPS"], + "homepage" : "https://github.com/toin0u/DigitalOceanV2", + "license" : "MIT", + + "authors": [ + { + "name" : "Antoine Corcy", + "email" : "contact@sbin.dk", + "homepage": "http://sbin.dk", + "role" : "Developer" + }, + { + "name" : "Yassir Hannnoun", + "email" : "yassir.hannoun@gmail.com", + "homepage": "https://yassirh.com", + "role" : "Developer" + } + ], + + "support": { + "issues": "https://github.com/toin0u/DigitalOceanV2/issues", + "source": "https://github.com/toin0u/DigitalOceanV2" + }, + + "require": { + "php": ">=5.4.0" + }, + + "require-dev": { + "kriswallsmith/buzz": "~0.10", + "guzzle/guzzle" : "~3.7", + "guzzlehttp/guzzle" : "~5.0", + "phpspec/phpspec" : "~2.0" + }, + + "suggest": { + "kriswallsmith/buzz": "To use BuzzAdapter, require kriswallsmith/buzz:~0.10.", + "guzzle/guzzle" : "To use GuzzleAdapter, require guzzle/guzzle:~3.7.", + "guzzlehttp/guzzle" : "To use GuzzleV5Adapter, require guzzlehttp/guzzle:~5.0." + }, + + "autoload": { + "psr-4": { + "DigitalOceanV2\\": "src/" + }, + "files": ["src/functions.php"] + }, + + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.lock b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.lock new file mode 100755 index 000000000..d33dff6de --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/composer.lock @@ -0,0 +1,1138 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "83598bc95b504fb0ff28126865e59d0d", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2015-03-18 18:23:50" + }, + { + "name": "guzzlehttp/guzzle", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f3c8c22471cb55475105c14769644a49c3262b93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f3c8c22471cb55475105c14769644a49c3262b93", + "reference": "f3c8c22471cb55475105c14769644a49c3262b93", + "shasum": "" + }, + "require": { + "guzzlehttp/ringphp": "^1.1", + "php": ">=5.4.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2015-05-20 03:47:55" + }, + { + "name": "guzzlehttp/ringphp", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/RingPHP.git", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "shasum": "" + }, + "require": { + "guzzlehttp/streams": "~3.0", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", + "time": "2015-05-20 03:37:09" + }, + { + "name": "guzzlehttp/streams", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/streams.git", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ], + "time": "2014-10-12 19:18:40" + }, + { + "name": "kriswallsmith/buzz", + "version": "v0.14", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/Buzz.git", + "reference": "f802a9b9d01b53496f50c9c5e09b5c779cbbd300" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/f802a9b9d01b53496f50c9c5e09b5c779cbbd300", + "reference": "f802a9b9d01b53496f50c9c5e09b5c779cbbd300", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "suggest": { + "ext-curl": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Buzz": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Lightweight HTTP client", + "homepage": "https://github.com/kriswallsmith/Buzz", + "keywords": [ + "curl", + "http client" + ], + "time": "2015-06-02 16:48:29" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2015-02-03 12:10:50" + }, + { + "name": "phpspec/php-diff", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/phpspec/php-diff.git", + "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a", + "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Diff": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Chris Boulton", + "homepage": "http://github.com/chrisboulton", + "role": "Original developer" + } + ], + "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", + "time": "2013-11-01 13:02:21" + }, + { + "name": "phpspec/phpspec", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/phpspec.git", + "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/phpspec/zipball/e9a40577323e67f1de2e214abf32976a0352d8f8", + "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.1", + "php": ">=5.3.3", + "phpspec/php-diff": "~1.0.0", + "phpspec/prophecy": "~1.4", + "sebastian/exporter": "~1.0", + "symfony/console": "~2.3", + "symfony/event-dispatcher": "~2.1", + "symfony/finder": "~2.1", + "symfony/process": "~2.1", + "symfony/yaml": "~2.1" + }, + "require-dev": { + "behat/behat": "^3.0.11", + "bossa/phpspec2-expect": "~1.0", + "phpunit/phpunit": "~4.4", + "symfony/filesystem": "~2.1", + "symfony/process": "~2.1" + }, + "suggest": { + "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" + }, + "bin": [ + "bin/phpspec" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "PhpSpec": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "homepage": "http://marcelloduarte.net/" + } + ], + "description": "Specification-oriented BDD framework for PHP 5.3+", + "homepage": "http://phpspec.net/", + "keywords": [ + "BDD", + "SpecBDD", + "TDD", + "spec", + "specification", + "testing", + "tests" + ], + "time": "2015-05-30 15:21:40" + }, + { + "name": "phpspec/prophecy", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", + "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2015-04-27 22:15:08" + }, + { + "name": "react/promise", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef", + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@googlemail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "time": "2014-12-30 13:32:42" + }, + { + "name": "sebastian/comparator", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-01-29 16:28:08" + }, + { + "name": "sebastian/diff", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", + "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-02-22 15:13:53" + }, + { + "name": "sebastian/exporter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "84839970d05254c73cde183a721c7af13aede943" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", + "reference": "84839970d05254c73cde183a721c7af13aede943", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2015-01-27 07:23:06" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2015-01-24 09:48:32" + }, + { + "name": "symfony/console", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/Console.git", + "reference": "564398bc1f33faf92fc2ec86859983d30eb81806" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Console/zipball/564398bc1f33faf92fc2ec86859983d30eb81806", + "reference": "564398bc1f33faf92fc2ec86859983d30eb81806", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1", + "symfony/phpunit-bridge": "~2.7", + "symfony/process": "~2.1" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2015-06-10 15:30:22" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/be3c5ff8d503c46768aeb78ce6333051aa6f26d9", + "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/phpunit-bridge": "~2.7", + "symfony/stopwatch": "~2.3" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2015-06-08 09:37:21" + }, + { + "name": "symfony/finder", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/c13a40d638aeede1e8400f8c956c7f9246c05f75", + "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2015-06-04 20:11:48" + }, + { + "name": "symfony/process", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/552d8efdc80980cbcca50b28d626ac8e36e3cdd1", + "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2015-06-08 09:37:21" + }, + { + "name": "symfony/yaml", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/9808e75c609a14f6db02f70fccf4ca4aab53c160", + "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2015-06-10 15:30:22" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.4.0" + }, + "platform-dev": [] +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AbstractAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AbstractAdapter.php new file mode 100755 index 000000000..5379d032f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AbstractAdapter.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Adapter; + +/** + * @author Antoine Corcy + */ +abstract class AbstractAdapter +{ + /** + * @var string + */ + protected $accessToken; + + /** + * @param string $accessToken + */ + public function __construct($accessToken) + { + $this->accessToken = $accessToken; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AdapterInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AdapterInterface.php new file mode 100755 index 000000000..8eae51df5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/AdapterInterface.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Adapter; + +/** + * @author Antoine Corcy + */ +interface AdapterInterface +{ + /** + * @param string $url + * + * @throws \RuntimeException|ExceptionInterface + * + * @return string + */ + public function get($url); + + /** + * @param string $url + * @param array $headers (optional) + * + * @throws \RuntimeException|ExceptionInterface + */ + public function delete($url, array $headers = array()); + + /** + * @param string $url + * @param array $headers (optional) + * @param string $content (optional) + * + * @throws \RuntimeException|ExceptionInterface + * + * @return string + */ + public function put($url, array $headers = array(), $content = ''); + + /** + * @param string $url + * @param array $headers (optional) + * @param string $content (optional) + * + * @throws \RuntimeException|ExceptionInterface + * + * @return string + */ + public function post($url, array $headers = array(), $content = ''); + + /** + * @return null|array + */ + public function getLatestResponseHeaders(); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzAdapter.php new file mode 100755 index 000000000..607b3d478 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzAdapter.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Adapter; + +use Buzz\Browser; +use Buzz\Client\Curl; +use Buzz\Listener\ListenerInterface; +use Buzz\Message\Response; +use DigitalOceanV2\Exception\ExceptionInterface; + +/** + * @author Antoine Corcy + */ +class BuzzAdapter extends AbstractAdapter implements AdapterInterface +{ + /** + * @var Browser + */ + protected $browser; + + /** + * @var ExceptionInterface + */ + protected $exception; + + /** + * @param string $accessToken + * @param Browser $browser (optional) + * @param ListenerInterface $listener (optional) + * @param ExceptionInterface $exception (optional) + */ + public function __construct($accessToken, Browser $browser = null, ListenerInterface $listener = null, ExceptionInterface $exception = null) + { + $this->browser = $browser ?: new Browser(new Curl()); + $this->browser->addListener($listener ?: new BuzzOAuthListener($accessToken)); + $this->exception = $exception; + } + + /** + * {@inheritdoc} + */ + public function get($url) + { + $response = $this->browser->get($url); + + if (!$response->isSuccessful()) { + throw $this->handleResponse($response); + } + + return $response->getContent(); + } + + /** + * {@inheritdoc} + */ + public function delete($url, array $headers = array()) + { + $response = $this->browser->delete($url, $headers); + + if (!$response->isSuccessful()) { + throw $this->handleResponse($response); + } + } + + /** + * {@inheritdoc} + */ + public function put($url, array $headers = array(), $content = '') + { + $response = $this->browser->put($url, $headers, $content); + + if (!$response->isSuccessful()) { + throw $this->handleResponse($response); + } + + return $response->getContent(); + } + + /** + * {@inheritdoc} + */ + public function post($url, array $headers = array(), $content = '') + { + $response = $this->browser->post($url, $headers, $content); + + if (!$response->isSuccessful()) { + throw $this->handleResponse($response); + } + + return $response->getContent(); + } + + /** + * {@inheritdoc} + */ + public function getLatestResponseHeaders() + { + if (null === $response = $this->browser->getLastResponse()) { + return; + } + + return array( + 'reset' => (int) $response->getHeader('RateLimit-Reset'), + 'remaining' => (int) $response->getHeader('RateLimit-Remaining'), + 'limit' => (int) $response->getHeader('RateLimit-Limit'), + ); + } + + /** + * @param Response $response + * + * @return \Exception + */ + protected function handleResponse(Response $response) + { + if ($this->exception) { + return $this->exception->create($response->getContent(), $response->getStatusCode()); + } + + $content = json_decode($response->getContent()); + + return new \RuntimeException( + sprintf('[%d] %s (%s)', $response->getStatusCode(), $content->message, $content->id) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzOAuthListener.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzOAuthListener.php new file mode 100755 index 000000000..47c5fa95e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/BuzzOAuthListener.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Adapter; + +use Buzz\Message\MessageInterface; +use Buzz\Message\RequestInterface; + +/** + * @author Antoine Corcy + */ +class BuzzOAuthListener implements \Buzz\Listener\ListenerInterface +{ + /** + * @var string + */ + protected $accessToken; + + /** + * @param string $accessToken + */ + public function __construct($accessToken) + { + $this->accessToken = $accessToken; + } + + /** + * {@inheritdoc} + */ + public function preSend(RequestInterface $request) + { + $request->addHeader(sprintf('Authorization: Bearer %s', $this->accessToken)); + } + + /** + * {@inheritdoc} + */ + public function postSend(RequestInterface $request, MessageInterface $response) + { + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/Guzzle5Adapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/Guzzle5Adapter.php new file mode 100755 index 000000000..f73b5c2ad --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/Guzzle5Adapter.php @@ -0,0 +1,134 @@ +client = $client ?: new \GuzzleHttp\Client(); + $this->exception = $exception; + + $this->client->setDefaultOption('headers/Authorization', sprintf('Bearer %s', $accessToken)); + + $this->client->getEmitter()->on('complete', function (CompleteEvent $e) use ($that) { + $that->handleResponse($e); + $e->stopPropagation(); + }); + } + + /** + * {@inheritdoc} + */ + public function get($url) + { + $this->response = $this->client->get($url); + + return $this->response->getBody(); + } + + /** + * {@inheritdoc} + */ + public function delete($url, array $headers = array()) + { + $options = array('headers' => $headers); + $this->response = $this->client->delete($url, $options); + + return $this->response->getBody(); + } + + /** + * {@inheritdoc} + */ + public function put($url, array $headers = array(), $content = '') + { + $headers['content-type'] = 'application/json'; + $options = array('headers' => $headers, 'body' => $content); + $request = $this->client->put($url, $options); + $this->response = $request; + + return $this->response->getBody(); + } + + /** + * {@inheritdoc} + */ + public function post($url, array $headers = array(), $content = '') + { + $headers['content-type'] = 'application/json'; + $options = array('headers' => $headers, 'body' => $content); + $request = $this->client->post($url, $options); + $this->response = $request; + + return $this->response->getBody(); + } + + /** + * {@inheritdoc} + */ + public function getLatestResponseHeaders() + { + if (null === $this->response) { + return; + } + + return array( + 'reset' => (int) (string) $this->response->getHeader('RateLimit-Reset'), + 'remaining' => (int) (string) $this->response->getHeader('RateLimit-Remaining'), + 'limit' => (int) (string) $this->response->getHeader('RateLimit-Limit'), + ); + } + + /** + * @param CompleteEvent $event + * + * @throws \RuntimeException|ExceptionInterface + */ + protected function handleResponse(CompleteEvent $event) + { + $this->response = $event->getResponse(); + + if ($this->response->getStatusCode() >= 200 && $this->response->getStatusCode() <= 299) { + return; + } + + $body = $this->response->getBody(); + $code = $this->response->getStatusCode(); + + if ($this->exception) { + throw $this->exception->create($body, $code); + } + + $content = $this->response->json(); + + throw new \RuntimeException(sprintf('[%d] %s (%s)', $code, $content->message, $content->id), $code); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/GuzzleAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/GuzzleAdapter.php new file mode 100755 index 000000000..16e635d1e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Adapter/GuzzleAdapter.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Adapter; + +use DigitalOceanV2\Exception\ExceptionInterface; +use Guzzle\Common\Event; +use Guzzle\Http\Client; +use Guzzle\Http\ClientInterface; +use Guzzle\Http\Message\Response; + +/** + * @author liverbool + */ +class GuzzleAdapter extends AbstractAdapter implements AdapterInterface +{ + /** + * @var ClientInterface + */ + protected $client; + + /** + * @var Response + */ + protected $response; + + /** + * @var ExceptionInterface + */ + protected $exception; + + /** + * @param string $accessToken + * @param ClientInterface $client (optional) + * @param ExceptionInterface $exception (optional) + */ + public function __construct($accessToken, ClientInterface $client = null, ExceptionInterface $exception = null) + { + $that = $this; + $this->client = $client ?: new Client(); + $this->exception = $exception; + + $this->client + + // Set default Bearer header for all request + ->setDefaultOption('headers/Authorization', sprintf('Bearer %s', $accessToken)) + + // Subscribe completed request event + ->setDefaultOption('events/request.complete', function (Event $event) use ($that) { + $that->handleResponse($event); + $event->stopPropagation(); + }); + } + + /** + * {@inheritdoc} + */ + public function get($url) + { + $this->response = $this->client->get($url)->send(); + + return $this->response->getBody(true); + } + + /** + * {@inheritdoc} + */ + public function delete($url, array $headers = array()) + { + $this->response = $this->client->delete($url, $headers)->send(); + + return $this->response->getBody(true); + } + + /** + * {@inheritdoc} + */ + public function put($url, array $headers = array(), $content = '') + { + $headers['content-type'] = 'application/json'; + $request = $this->client->put($url, $headers, $content); + $this->response = $request->send(); + + return $this->response->getBody(true); + } + + /** + * {@inheritdoc} + */ + public function post($url, array $headers = array(), $content = '') + { + $headers['content-type'] = 'application/json'; + $request = $this->client->post($url, $headers, $content); + $this->response = $request->send(); + + return $this->response->getBody(true); + } + + /** + * {@inheritdoc} + */ + public function getLatestResponseHeaders() + { + if (null === $this->response) { + return; + } + + return array( + 'reset' => (int) (string) $this->response->getHeader('RateLimit-Reset'), + 'remaining' => (int) (string) $this->response->getHeader('RateLimit-Remaining'), + 'limit' => (int) (string) $this->response->getHeader('RateLimit-Limit'), + ); + } + + /** + * @param Event $event + * + * @throws \RuntimeException|ExceptionInterface + */ + protected function handleResponse(Event $event) + { + $this->response = $event['response']; + + if ($this->response->isSuccessful()) { + return; + } + + $body = $this->response->getBody(true); + $code = $this->response->getStatusCode(); + + if ($this->exception) { + throw $this->exception->create($body, $code); + } + + $content = json_decode($body); + + throw new \RuntimeException(sprintf('[%d] %s (%s)', $code, $content->message, $content->id), $code); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/AbstractApi.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/AbstractApi.php new file mode 100755 index 000000000..ad70281ea --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/AbstractApi.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Adapter\AdapterInterface; +use DigitalOceanV2\Entity\Meta; + +/** + * @author Antoine Corcy + */ +abstract class AbstractApi +{ + /** + * API v2. + */ + const ENDPOINT = 'https://api.digitalocean.com/v2'; + + /** + * @var AdapterInterface + */ + protected $adapter; + + /** + * @var Meta + */ + protected $meta; + + /** + * @param AdapterInterface $adapter + */ + public function __construct(AdapterInterface $adapter) + { + $this->adapter = $adapter; + } + + /** + * @param \stdClass $data + * + * @return Meta|null + */ + protected function extractMeta(\StdClass $data) + { + if (isset($data->meta)) { + $this->meta = new Meta($data->meta); + } + + return $this->meta; + } + + /** + * @return Meta|null + */ + public function getMeta() + { + return $this->meta; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Account.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Account.php new file mode 100755 index 000000000..f1cdf1774 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Account.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Account as AccountEntity; + +/** + * @author Antoine Corcy + */ +class Account extends AbstractApi +{ + /** + * @return AccountEntity + */ + public function getUserInformation() + { + $account = $this->adapter->get(sprintf('%s/account', self::ENDPOINT)); + $account = json_decode($account); + + return new AccountEntity($account->account); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Action.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Action.php new file mode 100755 index 000000000..10827c4c9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Action.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Action as ActionEntity; + +/** + * @author Antoine Corcy + */ +class Action extends AbstractApi +{ + /** + * @return ActionEntity[] + */ + public function getAll() + { + $actions = $this->adapter->get(sprintf('%s/actions?per_page=%d', self::ENDPOINT, PHP_INT_MAX)); + $actions = json_decode($actions); + + $this->extractMeta($actions); + + return array_map(function ($action) { + return new ActionEntity($action); + }, $actions->actions); + } + + /** + * @param int $id + * + * @return ActionEntity + */ + public function getById($id) + { + $action = $this->adapter->get(sprintf('%s/actions/%d', self::ENDPOINT, $id)); + $action = json_decode($action); + + $this->meta = null; + + return new ActionEntity($action->action); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Domain.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Domain.php new file mode 100755 index 000000000..50f5d95cc --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Domain.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Domain as DomainEntity; + +/** + * @author Yassir Hannoun + */ +class Domain extends AbstractApi +{ + /** + * @return DomainEntity[] + */ + public function getAll() + { + $domains = $this->adapter->get(sprintf('%s/domains?per_page=%d', self::ENDPOINT, PHP_INT_MAX)); + $domains = json_decode($domains); + + $this->extractMeta($domains); + + return array_map(function ($domain) { + return new DomainEntity($domain); + }, $domains->domains); + } + + /** + * @param string $domainName + * + * @throws \RuntimeException + * + * @return DomainEntity + */ + public function getByName($domainName) + { + $domain = $this->adapter->get(sprintf('%s/domains/%s', self::ENDPOINT, $domainName)); + $domain = json_decode($domain); + + return new DomainEntity($domain->domain); + } + + /** + * @param string $name + * @param string $ipAddress + * + * @throws \RuntimeException + * + * @return DomainEntity + */ + public function create($name, $ipAddress) + { + $headers = array('Content-Type: application/json'); + $content = json_encode(array('name' => $name, 'ip_address' => $ipAddress)); + + $domain = $this->adapter->post(sprintf('%s/domains', self::ENDPOINT), $headers, $content); + $domain = json_decode($domain); + + return new DomainEntity($domain->domain); + } + + /** + * @param string $domain + * + * @throws \RuntimeException + */ + public function delete($domain) + { + $headers = array('Content-Type: application/x-www-form-urlencoded'); + $this->adapter->delete(sprintf('%s/domains/%s', self::ENDPOINT, $domain), $headers); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/DomainRecord.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/DomainRecord.php new file mode 100755 index 000000000..51d2155ed --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/DomainRecord.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\DomainRecord as DomainRecordEntity; + +/** + * @author Yassir Hannoun + */ +class DomainRecord extends AbstractApi +{ + /** + * @param string $domainName + * + * @return DomainRecordEntity[] + */ + public function getAll($domainName) + { + $domainRecords = $this->adapter->get(sprintf('%s/domains/%s/records?per_page=%d', self::ENDPOINT, $domainName, PHP_INT_MAX)); + $domainRecords = json_decode($domainRecords); + + $this->extractMeta($domainRecords); + + return array_map(function ($domainRecord) { + return new DomainRecordEntity($domainRecord); + }, $domainRecords->domain_records); + } + + /** + * @param string $domainName + * @param int $id + * + * @return DomainRecordEntity + */ + public function getById($domainName, $id) + { + $domainRecords = $this->adapter->get(sprintf('%s/domains/%s/records/%d', self::ENDPOINT, $domainName, $id)); + $domainRecords = json_decode($domainRecords); + + return new DomainRecordEntity($domainRecords->domain_record); + } + + /** + * @param string $domainName + * @param string $type + * @param string $name + * @param string $data + * @param int $priority (optional) + * @param int $port (optional) + * @param int $weight (optional) + * + * @throws \RuntimeException + * + * @return DomainRecordEntity + */ + public function create($domainName, $type, $name, $data, $priority = null, $port = null, $weight = null) + { + $headers = array('Content-Type: application/json'); + $content = ''; + + switch ($type = strtoupper($type)) { + case 'A': + case 'AAAA': + case 'CNAME': + case 'TXT': + $content .= json_encode(array('name' => $name, 'type' => $type, 'data' => $data)); + break; + + case 'NS': + $content .= json_encode(array('type' => $type, 'data' => $data)); + break; + + case 'SRV': + $content .= json_encode(array( + 'name' => $name, + 'type' => $type, + 'data' => $data, + 'priority' => (int) $priority, + 'port' => (int) $port, + 'weight' => (int) $weight + )); + break; + + case 'MX': + $content .= json_encode(array('type' => $type, 'data' => $data, 'priority' => $priority)); + break; + + default: + throw new \RuntimeException('Domain record type is invalid.'); + } + + $domainRecord = $this->adapter->post(sprintf('%s/domains/%s/records', self::ENDPOINT, $domainName), $headers, $content); + $domainRecord = json_decode($domainRecord); + + return new DomainRecordEntity($domainRecord->domain_record); + } + + /** + * @param string $domainName + * @param int $recordId + * @param string $name + * + * @throws \RuntimeException + * + * @return DomainRecordEntity + */ + public function update($domainName, $recordId, $name) + { + $headers = array('Content-Type: application/json'); + $content = json_encode(array('name' => $name)); + + $domainRecord = $this->adapter->put(sprintf('%s/domains/%s/records/%d', self::ENDPOINT, $domainName, $recordId), $headers, $content); + $domainRecord = json_decode($domainRecord); + + return new DomainRecordEntity($domainRecord->domain_record); + } + + /** + * @param string $domainName + * @param int $recordId + * @param string $data + * + * @throws \RuntimeException + * + * @return DomainRecordEntity + */ + public function updateData($domainName, $recordId, $data) + { + $headers = array('Content-Type: application/json'); + $content = json_encode(array('data' => $data)); + + $domainRecord = $this->adapter->put(sprintf('%s/domains/%s/records/%d', self::ENDPOINT, $domainName, $recordId), $headers, $content); + $domainRecord = json_decode($domainRecord); + + return new DomainRecordEntity($domainRecord->domain_record); + } + + /** + * @param string $domainName + * @param int $recordId + */ + public function delete($domainName, $recordId) + { + $headers = array('Content-Type: application/x-www-form-urlencoded'); + $this->adapter->delete(sprintf('%s/domains/%s/records/%d', self::ENDPOINT, $domainName, $recordId), $headers); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Droplet.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Droplet.php new file mode 100755 index 000000000..3905d1a50 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Droplet.php @@ -0,0 +1,444 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Action as ActionEntity; +use DigitalOceanV2\Entity\Droplet as DropletEntity; +use DigitalOceanV2\Entity\Image as ImageEntity; +use DigitalOceanV2\Entity\Kernel as KernelEntity; +use DigitalOceanV2\Entity\Upgrade as UpgradeEntity; + +/** + * @author Yassir Hannoun + */ +class Droplet extends AbstractApi +{ + /** + * @return DropletEntity[] + */ + public function getAll() + { + $droplets = $this->adapter->get(sprintf('%s/droplets?per_page=%d', self::ENDPOINT, PHP_INT_MAX)); + $droplets = json_decode($droplets); + + $this->extractMeta($droplets); + + return array_map(function ($droplet) { + return new DropletEntity($droplet); + }, $droplets->droplets); + } + + /** + * @param int $id + * + * @return DropletEntity[] + */ + public function getNeighborsById($id) + { + $droplets = $this->adapter->get(sprintf('%s/droplets/%d/neighbors', self::ENDPOINT, $id)); + $droplets = json_decode($droplets); + + return array_map(function ($droplet) { + return new DropletEntity($droplet); + }, $droplets->droplets); + } + + /** + * @return DropletEntity[] + */ + public function getAllNeighbors() + { + $neighbors = $this->adapter->get(sprintf('%s/reports/droplet_neighbors', self::ENDPOINT)); + $neighbors = json_decode($neighbors); + + return array_map(function ($neighbor) { + return new DropletEntity($neighbor); + }, $neighbors->neighbors); + } + + /** + * @return UpgradeEntity[] + */ + public function getUpgrades() + { + $upgrades = $this->adapter->get(sprintf('%s/droplet_upgrades', self::ENDPOINT)); + $upgrades = json_decode($upgrades); + + return array_map(function ($upgrade) { + return new UpgradeEntity($upgrade); + }, $upgrades); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return DropletEntity + */ + public function getById($id) + { + $droplet = $this->adapter->get(sprintf('%s/droplets/%d', self::ENDPOINT, $id)); + $droplet = json_decode($droplet); + + return new DropletEntity($droplet->droplet); + } + + /** + * @param string $name + * @param string $region + * @param string $size + * @param string|int $image + * @param bool $backups (optional) + * @param bool $ipv6 (optional) + * @param bool $privateNetworking (optional) + * @param int[] $sshKeys (optional) + * @param string $userData (optional) + * + * @throws \RuntimeException + * + * @return DropletEntity + */ + public function create($name, $region, $size, $image, $backups = false, $ipv6 = false, + $privateNetworking = false, array $sshKeys = array(), $userData = "" + ) { + $headers = array('Content-Type: application/json'); + + $data = array( + 'name' => $name, + 'region' => $region, + 'size' => $size, + 'image' => $image, + 'backups' => \DigitalOceanV2\bool_to_string($backups), + 'ipv6' => \DigitalOceanV2\bool_to_string($ipv6), + 'private_networking' => \DigitalOceanV2\bool_to_string($privateNetworking) + ); + + if (0 < count($sshKeys)) { + $data["ssh_keys"] = $sshKeys; + } + + if (!empty($userData)) { + $data["user_data"] = $userData; + } + + $content = json_encode($data); + + $droplet = $this->adapter->post(sprintf('%s/droplets', self::ENDPOINT), $headers, $content); + $droplet = json_decode($droplet); + + return new DropletEntity($droplet->droplet); + } + + /** + * @param int $id + * + * @throws \RuntimeException + */ + public function delete($id) + { + $headers = array('Content-Type: application/x-www-form-urlencoded'); + $this->adapter->delete(sprintf('%s/droplets/%d', self::ENDPOINT, $id), $headers); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return KernelEntity[] + */ + public function getAvailableKernels($id) + { + $kernels = $this->adapter->get(sprintf('%s/droplets/%d/kernels', self::ENDPOINT, $id)); + $kernels = json_decode($kernels); + + $this->meta = $this->extractMeta($kernels); + + return array_map(function ($kernel) { + return new KernelEntity($kernel); + }, $kernels->kernels); + } + + /** + * @param int $id + * + * @return ImageEntity[] + */ + public function getSnapshots($id) + { + $snapshots = $this->adapter->get(sprintf('%s/droplets/%d/snapshots?per_page=%d', self::ENDPOINT, $id, PHP_INT_MAX)); + $snapshots = json_decode($snapshots); + + $this->meta = $this->extractMeta($snapshots); + + return array_map(function ($snapshot) { + $snapshot = new ImageEntity($snapshot); + + return $snapshot; + }, $snapshots->snapshots); + } + + /** + * @param int $id + * + * @return ImageEntity[] + */ + public function getBackups($id) + { + $backups = $this->adapter->get(sprintf('%s/droplets/%d/backups?per_page=%d', self::ENDPOINT, $id, PHP_INT_MAX)); + $backups = json_decode($backups); + + $this->meta = $this->extractMeta($backups); + + return array_map(function ($backup) { + return new ImageEntity($backup); + }, $backups->backups); + } + + /** + * @param int $id + * + * @return ActionEntity[] + */ + public function getActions($id) + { + $actions = $this->adapter->get(sprintf('%s/droplets/%d/actions?per_page=%d', self::ENDPOINT, $id, PHP_INT_MAX)); + $actions = json_decode($actions); + + $this->meta = $this->extractMeta($actions); + + return array_map(function ($action) { + return new ActionEntity($action); + }, $actions->actions); + } + + /** + * @param int $id + * @param int $actionId + * + * @return ActionEntity + */ + public function getActionById($id, $actionId) + { + $action = $this->adapter->get(sprintf('%s/droplets/%d/actions/%d', self::ENDPOINT, $id, $actionId)); + $action = json_decode($action); + + return new ActionEntity($action->action); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function reboot($id) + { + return $this->executeAction($id, array('type' => 'reboot')); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function powerCycle($id) + { + return $this->executeAction($id, array('type' => 'power_cycle')); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function shutdown($id) + { + return $this->executeAction($id, array('type' => 'shutdown')); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function powerOff($id) + { + return $this->executeAction($id, array('type' => 'power_off')); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function powerOn($id) + { + return $this->executeAction($id, array('type' => 'power_on')); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function passwordReset($id) + { + return $this->executeAction($id, array('type' => 'password_reset')); + } + + /** + * @param int $id + * @param string $size + * @param bool $disk (optional) + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function resize($id, $size, $disk = true) + { + return $this->executeAction($id, array('type' => 'resize', 'size' => $size, 'disk' => $disk)); + } + + /** + * @param int $id + * @param int $image + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function restore($id, $image) + { + return $this->executeAction($id, array('type' => 'restore', 'image' => $image)); + } + + /** + * @param int $id + * @param int|string $image + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function rebuild($id, $image) + { + return $this->executeAction($id, array('type' => 'rebuild', 'image' => $image)); + } + + /** + * @param int $id + * @param string $name + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function rename($id, $name) + { + return $this->executeAction($id, array('type' => 'rename', 'name' => $name)); + } + + /** + * @param int $id + * @param int $kernel + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function changeKernel($id, $kernel) + { + return $this->executeAction($id, array('type' => 'change_kernel', 'kernel' => $kernel)); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function enableIpv6($id) + { + return $this->executeAction($id, array('type' => 'enable_ipv6')); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function disableBackups($id) + { + return $this->executeAction($id, array('type' => 'disable_backups')); + } + + /** + * @param int $id + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function enablePrivateNetworking($id) + { + return $this->executeAction($id, array('type' => 'enable_private_networking')); + } + + /** + * @param int $id + * @param string $name + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function snapshot($id, $name) + { + return $this->executeAction($id, array('type' => 'snapshot', 'name' => $name)); + } + + /** + * @param int $id + * @param array $options + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + private function executeAction($id, array $options) + { + $headers = array('Content-Type: application/json'); + $content = json_encode($options); + + $action = $this->adapter->post(sprintf('%s/droplets/%d/actions', self::ENDPOINT, $id), $headers, $content); + $action = json_decode($action); + + return new ActionEntity($action->action); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Image.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Image.php new file mode 100755 index 000000000..f17b09939 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Image.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Action as ActionEntity; +use DigitalOceanV2\Entity\Image as ImageEntity; + +/** + * @author Yassir Hannoun + */ +class Image extends AbstractApi +{ + /** + * @param array $criteria + * + * @return ImageEntity[] + */ + public function getAll(array $criteria = []) + { + $query = sprintf('%s/images?per_page=%d', self::ENDPOINT, PHP_INT_MAX); + + if (isset($criteria['type']) && in_array($criteria['type'], ['distribution', 'application'])) { + $query = sprintf('%s&type=%s', $query, $criteria['type']); + } + + if (isset($criteria['private']) && true === (boolean) $criteria['private']) { + $query = sprintf('%s&private=true', $query); + } + + $images = $this->adapter->get($query); + $images = json_decode($images); + + $this->extractMeta($images); + + return array_map(function ($image) { + return new ImageEntity($image); + }, $images->images); + } + + /** + * @param int $id + * + * @return ImageEntity + */ + public function getById($id) + { + $image = $this->adapter->get(sprintf('%s/images/%d', self::ENDPOINT, $id)); + $image = json_decode($image); + + return new ImageEntity($image->image); + } + + /** + * @param string $slug + * + * @return ImageEntity + */ + public function getBySlug($slug) + { + $image = $this->adapter->get(sprintf('%s/images/%s', self::ENDPOINT, $slug)); + $image = json_decode($image); + + return new ImageEntity($image->image); + } + + /** + * @param int $id + * @param string $name + * + * @throws \RuntimeException + * + * @return ImageEntity + */ + public function update($id, $name) + { + $headers = array('Content-Type: application/json'); + $content = json_encode(array('name' => $name)); + + $image = $this->adapter->put(sprintf('%s/images/%d', self::ENDPOINT, $id), $headers, $content); + $image = json_decode($image); + + return new ImageEntity($image->image); + } + + /** + * @param int $id + * + * @throws \RuntimeException + */ + public function delete($id) + { + $headers = array('Content-Type: application/x-www-form-urlencoded'); + $this->adapter->delete(sprintf('%s/images/%d', self::ENDPOINT, $id), $headers); + } + + /** + * @param int $id + * @param string $regionSlug + * + * @throws \RuntimeException + * + * @return ActionEntity + */ + public function transfer($id, $regionSlug) + { + $headers = array('Content-Type: application/json'); + $content = json_encode(array('type' => 'transfer', 'region' => $regionSlug)); + + $action = $this->adapter->post(sprintf('%s/images/%d/actions', self::ENDPOINT, $id), $headers, $content); + $action = json_decode($action); + + return new ActionEntity($action->action); + } + + /** + * @param int $id + * @param int $actionId + * + * @return ActionEntity + */ + public function getAction($id, $actionId) + { + $action = $this->adapter->get(sprintf('%s/images/%d/actions/%d', self::ENDPOINT, $id, $actionId)); + $action = json_decode($action); + + return new ActionEntity($action->action); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Key.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Key.php new file mode 100755 index 000000000..7ddee9ab6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Key.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Key as KeyEntity; + +/** + * @author Antoine Corcy + */ +class Key extends AbstractApi +{ + /** + * @return KeyEntity[] + */ + public function getAll() + { + $keys = $this->adapter->get(sprintf('%s/account/keys?per_page=%d', self::ENDPOINT, PHP_INT_MAX)); + $keys = json_decode($keys); + $this->extractMeta($keys); + + return array_map(function ($key) { + return new KeyEntity($key); + }, $keys->ssh_keys); + } + + /** + * @param int $id + * + * @return KeyEntity + */ + public function getById($id) + { + $key = $this->adapter->get(sprintf('%s/account/keys/%d', self::ENDPOINT, $id)); + $key = json_decode($key); + + return new KeyEntity($key->ssh_key); + } + + /** + * @param string $fingerprint + * + * @return KeyEntity + */ + public function getByFingerprint($fingerprint) + { + $key = $this->adapter->get(sprintf('%s/account/keys/%s', self::ENDPOINT, $fingerprint)); + $key = json_decode($key); + + return new KeyEntity($key->ssh_key); + } + + /** + * @param string $name + * @param string $publicKey + * + * @throws \RuntimeException + * + * @return KeyEntity + */ + public function create($name, $publicKey) + { + $headers = array('Content-Type: application/json'); + $content = json_encode(array('name' => $name, 'public_key' => $publicKey)); + + $key = $this->adapter->post(sprintf('%s/account/keys', self::ENDPOINT), $headers, $content); + $key = json_decode($key); + + return new KeyEntity($key->ssh_key); + } + + /** + * @param int $id + * @param string $name + * + * @throws \RuntimeException + * + * @return KeyEntity + */ + public function update($id, $name) + { + $headers = array('Content-Type: application/json'); + $content = json_encode(array('name' => $name)); + + $key = $this->adapter->put(sprintf('%s/account/keys/%d', self::ENDPOINT, $id), $headers, $content); + $key = json_decode($key); + + return new KeyEntity($key->ssh_key); + } + + /** + * @param int $id + * + * @throws \RuntimeException + */ + public function delete($id) + { + $headers = array('Content-Type: application/x-www-form-urlencoded'); + $this->adapter->delete(sprintf('%s/account/keys/%d', self::ENDPOINT, $id), $headers); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/RateLimit.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/RateLimit.php new file mode 100755 index 000000000..1ebef94f6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/RateLimit.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\RateLimit as RateLimitEntity; + +/** + * @author Yassir Hannoun + */ +class RateLimit extends AbstractApi +{ + /** + * @return RateLimitEntity|null + */ + public function getRateLimit() + { + $results = $this->adapter->getLatestResponseHeaders(); + + return null !== $results ? new RateLimitEntity($results) : null; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Region.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Region.php new file mode 100755 index 000000000..a7a46e04c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Region.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Region as RegionEntity; + +/** + * @author Yassir Hannoun + */ +class Region extends AbstractApi +{ + /** + * @return RegionEntity[] + */ + public function getAll() + { + $regions = $this->adapter->get(sprintf('%s/regions?per_page=%d', self::ENDPOINT, PHP_INT_MAX)); + $regions = json_decode($regions); + + $this->extractMeta($regions); + + return array_map(function ($region) { + return new RegionEntity($region); + }, $regions->regions); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Size.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Size.php new file mode 100755 index 000000000..7449dcc99 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Api/Size.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Api; + +use DigitalOceanV2\Entity\Size as SizeEntity; + +/** + * @author Yassir Hannoun + */ +class Size extends AbstractApi +{ + /** + * @return SizeEntity[] + */ + public function getAll() + { + $sizes = $this->adapter->get(sprintf('%s/sizes?per_page=%d', self::ENDPOINT, PHP_INT_MAX)); + $sizes = json_decode($sizes); + + $this->extractMeta($sizes); + + return array_map(function ($size) { + return new SizeEntity($size); + }, $sizes->sizes); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/DigitalOceanV2.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/DigitalOceanV2.php new file mode 100755 index 000000000..ef3a21969 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/DigitalOceanV2.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2; + +use DigitalOceanV2\Adapter\AdapterInterface; +use DigitalOceanV2\Api\Account; +use DigitalOceanV2\Api\Action; +use DigitalOceanV2\Api\Domain; +use DigitalOceanV2\Api\DomainRecord; +use DigitalOceanV2\Api\Droplet; +use DigitalOceanV2\Api\Image; +use DigitalOceanV2\Api\Key; +use DigitalOceanV2\Api\RateLimit; +use DigitalOceanV2\Api\Region; +use DigitalOceanV2\Api\Size; + +/** + * @author Antoine Corcy + */ +class DigitalOceanV2 +{ + /** + * @see http://semver.org/ + */ + const VERSION = '0.1.2-dev'; + + /** + * @var AdapterInterface + */ + protected $adapter; + + /** + * @param AdapterInterface $adapter + */ + public function __construct(AdapterInterface $adapter) + { + $this->adapter = $adapter; + } + + /** + * @return Account + */ + public function account() + { + return new Account($this->adapter); + } + + /** + * @return Action + */ + public function action() + { + return new Action($this->adapter); + } + + /** + * @return Image + */ + public function image() + { + return new Image($this->adapter); + } + + /** + * @return Domain + */ + public function domain() + { + return new Domain($this->adapter); + } + + /** + * @return DomainRecord + */ + public function domainRecord() + { + return new DomainRecord($this->adapter); + } + + /** + * @return Size + */ + public function size() + { + return new Size($this->adapter); + } + + /** + * @return Region + */ + public function region() + { + return new Region($this->adapter); + } + + /** + * @return Key + */ + public function key() + { + return new Key($this->adapter); + } + + /** + * @return Droplet + */ + public function droplet() + { + return new Droplet($this->adapter); + } + + /** + * @return RateLimit + */ + public function rateLimit() + { + return new RateLimit($this->adapter); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/AbstractEntity.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/AbstractEntity.php new file mode 100755 index 000000000..183b7c32b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/AbstractEntity.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Antoine Corcy + */ +abstract class AbstractEntity +{ + /** + * @var array + */ + protected $unknownProperties = []; + + /** + * @param \stdClass|array $parameters + */ + public function __construct($parameters) + { + $this->build($parameters); + } + + /** + * @param string $property + * + * @throws \InvalidArgumentException + * + * @return mixed + */ + public function __get($property) + { + if (!property_exists($this, $property)) { + if (array_key_exists($property, $this->unknownProperties)) { + return $this->unknownProperties[$property]; + } + + throw new \InvalidArgumentException(sprintf( + 'Property "%s::%s" does not exist.', get_class($this), $property + )); + } + } + + /** + * @param string $property + * @param mixed $value + */ + public function __set($property, $value) + { + if (!property_exists($this, $property)) { + $this->unknownProperties[$property] = $value; + } + } + + /** + * @return array + */ + public function getUnknownProperties() + { + return $this->unknownProperties; + } + + /** + * @param \stdClass|array $parameters + */ + public function build($parameters) + { + foreach ((array) $parameters as $property => $value) { + $property = \DigitalOceanV2\convert_to_camel_case($property); + + $this->$property = $value; + } + } + + /** + * @param string $date DateTime string + * + * @return null|string DateTime in ISO8601 format + */ + protected function convertDateTime($date) + { + if (empty($date)) { + return; + } + + $date = new \DateTime($date); + $date->setTimezone(new \DateTimeZone(date_default_timezone_get())); + + return $date->format(\DateTime::ISO8601); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Account.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Account.php new file mode 100755 index 000000000..ee9a5fbcf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Account.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Antoine Corcy + */ +class Account extends AbstractEntity +{ + /** + * @var int + */ + public $dropletLimit; + + /** + * @var string + */ + public $email; + + /** + * @var string + */ + public $uuid; + + /** + * @var bool + */ + public $emailVerified; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Action.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Action.php new file mode 100755 index 000000000..33f7d3171 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Action.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Antoine Corcy + */ +class Action extends AbstractEntity +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $status; + + /** + * @var string + */ + public $type; + + /** + * @var string + */ + public $startedAt; + + /** + * @var string + */ + public $completedAt; + + /** + * @var string + */ + public $resourceId; + + /** + * @var string + */ + public $resourceType; + + /** + * @var Region + */ + public $region; + + /** + * @var string + */ + public $regionSlug; + + /** + * @param \stdClass|array $parameters + */ + public function build($parameters) + { + parent::build($parameters); + + foreach ($parameters as $property => $value) { + if ('region' === $property && is_object($value)) { + $this->region = new Region($value); + } + } + } + + /** + * @param string $completedAt + */ + public function setCompletedAt($completedAt) + { + $this->completedAt = $this->convertDateTime($completedAt); + } + + /** + * @param string $startedAt + */ + public function setStartedAt($startedAt) + { + $this->startedAt = $this->convertDateTime($startedAt); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Domain.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Domain.php new file mode 100755 index 000000000..03c621dbd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Domain.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class Domain extends AbstractEntity +{ + /** + * @var string + */ + public $name; + + /** + * @var int + */ + public $ttl; + + /** + * @var string + */ + public $zoneFile; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/DomainRecord.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/DomainRecord.php new file mode 100755 index 000000000..9fe1db563 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/DomainRecord.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class DomainRecord extends AbstractEntity +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $type; + + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $data; + + /** + * @var int + */ + public $priority; + + /** + * @var int + */ + public $port; + + /** + * @var int + */ + public $weight; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Droplet.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Droplet.php new file mode 100755 index 000000000..dc5255198 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Droplet.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class Droplet extends AbstractEntity +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $name; + + /** + * @var int + */ + public $memory; + + /** + * @var int + */ + public $vcpus; + + /** + * @var int + */ + public $disk; + + /** + * @var Region + */ + public $region; + + /** + * @var Image + */ + public $image; + + /** + * @var Kernel + */ + public $kernel; + + /** + * @var Size + */ + public $size; + + /** + * @var String + */ + public $sizeSlug; + + /** + * @var bool + */ + public $locked; + + /** + * @var string + */ + public $createdAt; + + /** + * @var string + */ + public $status; + + /** + * @var Network[] + */ + public $networks; + + /** + * @var int[] + */ + public $backupIds; + + /** + * @var int[] + */ + public $snapshotIds; + + /** + * @var string[] + */ + public $features; + + /** + * @var bool + */ + public $backupsEnabled; + + /** + * @var bool + */ + public $privateNetworkingEnabled; + + /** + * @var bool + */ + public $ipv6Enabled; + + /** + * @var bool + */ + public $virtIOEnabled; + + /** + * @var NextBackupWindow + */ + public $nextBackupWindow; + + /** + * @param \stdClass|array $parameters + */ + public function build($parameters) + { + foreach ($parameters as $property => $value) { + switch ($property) { + case 'networks': + if (is_object($value)) { + if (property_exists($value, 'v4')) { + foreach ($value->v4 as $subProperty => $subValue) { + $this->networks[] = new Network($subValue); + } + } + + if (property_exists($value, 'v6')) { + foreach ($value->v6 as $subProperty => $subValue) { + $this->networks[] = new Network($subValue); + } + } + } + break; + + case 'kernel': + if (is_object($value)) { + $this->kernel = new Kernel($value); + } + break; + + case 'size': + if (is_object($value)) { + $this->size = new Size($value); + } + break; + + case 'region': + if (is_object($value)) { + $this->region = new Region($value); + } + break; + + case 'image': + if (is_object($value)) { + $this->image = new Image($value); + } + break; + + case 'next_backup_window': + $this->nextBackupWindow = new NextBackupWindow($value); + break; + + default: + $this->{\DigitalOceanV2\convert_to_camel_case($property)} = $value; + } + } + + if (is_array($this->features) && count($this->features)) { + $this->backupsEnabled = in_array("backups", $this->features); + $this->virtIOEnabled = in_array("virtio", $this->features); + $this->privateNetworkingEnabled = in_array("private_networking", $this->features); + $this->ipv6Enabled = in_array("ipv6", $this->features); + } + } + + /** + * @param string $createdAt + */ + public function setCreatedAt($createdAt) + { + $this->createdAt = $this->convertDateTime($createdAt); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Image.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Image.php new file mode 100755 index 000000000..304b8d341 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Image.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class Image extends AbstractEntity +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $type; + + /** + * @var string + */ + public $distribution; + + /** + * @var string + */ + public $slug; + + /** + * @var int + */ + public $minDiskSize; + + /** + * @var string + */ + public $createdAt; + + /** + * @var bool + */ + public $public; + + /** + * @var string[] + */ + public $regions; + + /** + * @param string $createdAt + */ + public function setCreatedAt($createdAt) + { + $this->createdAt = $this->convertDateTime($createdAt); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Kernel.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Kernel.php new file mode 100755 index 000000000..d6680ef01 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Kernel.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class Kernel extends AbstractEntity +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $version; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Key.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Key.php new file mode 100755 index 000000000..0b8b93aac --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Key.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Antoine Corcy + */ +class Key extends AbstractEntity +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $fingerprint; + + /** + * @var string + */ + public $publicKey; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Meta.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Meta.php new file mode 100755 index 000000000..2addc96bb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Meta.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Antoine Corcy + */ +class Meta extends AbstractEntity +{ + /** + * @var int + */ + public $total; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Network.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Network.php new file mode 100755 index 000000000..91f1eb3b9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Network.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class Network extends AbstractEntity +{ + /** + * @var string + */ + public $ipAddress; + + /** + * @var string + */ + public $gateway; + + /** + * @var string + */ + public $type; + + /** + * IPv6 specific. + * + * @var string + */ + public $cidr; + + /** + * IPv4 specific. + * + * @var string + */ + public $netmask; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/NextBackupWindow.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/NextBackupWindow.php new file mode 100755 index 000000000..519344cc5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/NextBackupWindow.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Antoine Corcy + */ +class NextBackupWindow extends AbstractEntity +{ + /** + * @var string + */ + public $start; + + /** + * @var string + */ + public $end; + + /** + * Build a new backup window instance + * + * @param \stdClass|array|null $parameters + */ + public function build($parameters) + { + if (is_null($parameters)) { + return; + } + + parent::build($parameters); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/RateLimit.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/RateLimit.php new file mode 100755 index 000000000..b794b7c46 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/RateLimit.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class RateLimit extends AbstractEntity +{ + /** + * @var int + */ + public $limit; + + /** + * @var int + */ + public $remaining; + + /** + * @var int + */ + public $reset; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Region.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Region.php new file mode 100755 index 000000000..361983591 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Region.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class Region extends AbstractEntity +{ + /** + * @var string + */ + public $slug; + + /** + * @var string + */ + public $name; + + /** + * @var bool + */ + public $available; + + /** + * @var string[] + */ + public $sizes; + + /** + * @var string[] + */ + public $features; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Size.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Size.php new file mode 100755 index 000000000..eba01b144 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Size.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Yassir Hannoun + */ +class Size extends AbstractEntity +{ + /** + * @var string + */ + public $slug; + + /** + * @var boolean + */ + public $available; + + /** + * @var int + */ + public $memory; + + /** + * @var int + */ + public $vcpus; + + /** + * @var int + */ + public $disk; + + /** + * @var int + */ + public $transfer; + + /** + * @var string + */ + public $priceMonthly; + + /** + * @var string + */ + public $priceHourly; + + /** + * @var string[] + */ + public $regions; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Upgrade.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Upgrade.php new file mode 100755 index 000000000..386c5806d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Entity/Upgrade.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Entity; + +/** + * @author Antoine Corcy + */ +class Upgrade extends AbstractEntity +{ + /** + * @var int + */ + public $dropletId; + + /** + * @var string + */ + public $dateOfMigration; + + /** + * @var string + */ + public $url; + + /** + * @param string $dateOfMigration + */ + public function setDateOfMigration($dateOfMigration) + { + $this->dateOfMigration = $this->convertDateTime($dateOfMigration); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionInterface.php new file mode 100755 index 000000000..a5ff9d171 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Exception; + +/** + * @author liverbool + */ +interface ExceptionInterface +{ + /** + * Create an exception. + * + * @param string $message + * @param int $code (optional) + * @param \Exception $previous (optional) + * + * @return ExceptionInterface + */ + public static function create($message, $code = 0, \Exception $previous = null); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionReader.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionReader.php new file mode 100755 index 000000000..ceae0c09c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ExceptionReader.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Exception; + +/** + * @author liverbool + */ +class ExceptionReader +{ + /** + * @var string Error id + */ + protected $id; + + /** + * @var string Error message + */ + protected $message; + + /** + * @var int Exception error code + */ + protected $code; + + /** + * Error message in DigitalOcean format. + * + * @param string $content + * @param int $code (optional) + */ + public function __construct($content, $code = 0) + { + $content = json_decode($content, true); + $codeId = empty($content['id']) ? null : $content['id']; + $message = empty($content['message']) ? 'Request not processed.' : $content['message']; + + // just example to modify message + $message = str_replace( + array('Droplet', 'droplet'), + array('Machine', 'machine'), + $message + ); + + $this->id = $codeId; + $this->code = $code; + $this->message = $message; + } + + /** + * Message Id (DigitalOcean error code). + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Error message. + * + * @param bool $includeCodeId (optional) + * + * @return string + */ + public function getMessage($includeCodeId = true) + { + if ($includeCodeId) { + $message = sprintf('%s (%s)', $this->message, $this->id); + + if ($this->code) { + $message = sprintf('[%d] %s', $this->code, $message); + } + + return $message; + } + + return $this->message; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ResponseException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ResponseException.php new file mode 100755 index 000000000..f30f2a6cd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/Exception/ResponseException.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2\Exception; + +/** + * @author liverbool + */ +class ResponseException extends \RuntimeException implements ExceptionInterface +{ + /** + * @var ExceptionReader + */ + protected $exception; + + /** + * {@inheritdoc} + */ + public function __construct($message = '', $code = 0, \Exception $previous = null) + { + $this->exception = new ExceptionReader($message, $code); + + parent::__construct($this->exception->getMessage(), $code, $previous); + } + + /** + * {@inheritdoc} + */ + public static function create($message, $code = 0, \Exception $previous = null) + { + return new self($message, $code, $previous); + } + + /** + * @param bool $includeCodeId (optional) + * + * @return string + */ + public function getErrorMessage($includeCodeId = false) + { + return $this->exception->getMessage($includeCodeId); + } + + /** + * @return string + */ + public function getErrorId() + { + return strtoupper($this->exception->getId()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/functions.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/functions.php new file mode 100755 index 000000000..21bbdd95c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/src/functions.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace DigitalOceanV2; + +/** + * Transform snake_case to camelCase. + * + * @param string $property + * + * @return string + */ +function convert_to_camel_case($property) +{ + return lcfirst(preg_replace_callback( + '/(^|_)([a-z])/', + function ($match) { + return strtoupper($match[2]); + }, + $property + )); +} + +/** + * Transform camelCase to snake_case. + * + * @param string $property + * + * @return string + */ +function convert_to_snake_case($property) +{ + return strtolower(preg_replace('/([A-Z])/', '_$1', $property)); +} + +/** + * Returns a string representation of a boolean. + * + * @param bool $value + * + * @return string + */ +function bool_to_string($value) +{ + return $value ? 'true' : 'false'; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/autoload.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/autoload.php new file mode 100755 index 000000000..784f417fb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + private $classMapAuthoritative = false; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-0 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative) { + return false; + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_classmap.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_classmap.php new file mode 100755 index 000000000..e4c104449 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_classmap.php @@ -0,0 +1,36 @@ + $vendorDir . '/sebastian/comparator/src/ArrayComparator.php', + 'SebastianBergmann\\Comparator\\Comparator' => $vendorDir . '/sebastian/comparator/src/Comparator.php', + 'SebastianBergmann\\Comparator\\ComparisonFailure' => $vendorDir . '/sebastian/comparator/src/ComparisonFailure.php', + 'SebastianBergmann\\Comparator\\DOMNodeComparator' => $vendorDir . '/sebastian/comparator/src/DOMNodeComparator.php', + 'SebastianBergmann\\Comparator\\DateTimeComparator' => $vendorDir . '/sebastian/comparator/src/DateTimeComparator.php', + 'SebastianBergmann\\Comparator\\DoubleComparator' => $vendorDir . '/sebastian/comparator/src/DoubleComparator.php', + 'SebastianBergmann\\Comparator\\ExceptionComparator' => $vendorDir . '/sebastian/comparator/src/ExceptionComparator.php', + 'SebastianBergmann\\Comparator\\Factory' => $vendorDir . '/sebastian/comparator/src/Factory.php', + 'SebastianBergmann\\Comparator\\MockObjectComparator' => $vendorDir . '/sebastian/comparator/src/MockObjectComparator.php', + 'SebastianBergmann\\Comparator\\NumericComparator' => $vendorDir . '/sebastian/comparator/src/NumericComparator.php', + 'SebastianBergmann\\Comparator\\ObjectComparator' => $vendorDir . '/sebastian/comparator/src/ObjectComparator.php', + 'SebastianBergmann\\Comparator\\ResourceComparator' => $vendorDir . '/sebastian/comparator/src/ResourceComparator.php', + 'SebastianBergmann\\Comparator\\ScalarComparator' => $vendorDir . '/sebastian/comparator/src/ScalarComparator.php', + 'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => $vendorDir . '/sebastian/comparator/src/SplObjectStorageComparator.php', + 'SebastianBergmann\\Comparator\\TypeComparator' => $vendorDir . '/sebastian/comparator/src/TypeComparator.php', + 'SebastianBergmann\\Diff\\Chunk' => $vendorDir . '/sebastian/diff/src/Chunk.php', + 'SebastianBergmann\\Diff\\Diff' => $vendorDir . '/sebastian/diff/src/Diff.php', + 'SebastianBergmann\\Diff\\Differ' => $vendorDir . '/sebastian/diff/src/Differ.php', + 'SebastianBergmann\\Diff\\LCS\\LongestCommonSubsequence' => $vendorDir . '/sebastian/diff/src/LCS/LongestCommonSubsequence.php', + 'SebastianBergmann\\Diff\\LCS\\MemoryEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php', + 'SebastianBergmann\\Diff\\LCS\\TimeEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php', + 'SebastianBergmann\\Diff\\Line' => $vendorDir . '/sebastian/diff/src/Line.php', + 'SebastianBergmann\\Diff\\Parser' => $vendorDir . '/sebastian/diff/src/Parser.php', + 'SebastianBergmann\\Exporter\\Exporter' => $vendorDir . '/sebastian/exporter/src/Exporter.php', + 'SebastianBergmann\\RecursionContext\\Context' => $vendorDir . '/sebastian/recursion-context/src/Context.php', + 'SebastianBergmann\\RecursionContext\\Exception' => $vendorDir . '/sebastian/recursion-context/src/Exception.php', + 'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => $vendorDir . '/sebastian/recursion-context/src/InvalidArgumentException.php', +); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_files.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_files.php new file mode 100755 index 000000000..aeb5dcf99 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_files.php @@ -0,0 +1,11 @@ + array($vendorDir . '/phpdocumentor/reflection-docblock/src'), + 'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src'), + 'PhpSpec' => array($vendorDir . '/phpspec/phpspec/src'), + 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'), + 'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'), + 'Diff' => array($vendorDir . '/phpspec/php-diff/lib'), + 'Buzz' => array($vendorDir . '/kriswallsmith/buzz/lib'), +); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_psr4.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_psr4.php new file mode 100755 index 000000000..7743a95f4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_psr4.php @@ -0,0 +1,20 @@ + array($vendorDir . '/symfony/yaml'), + 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), + 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'React\\Promise\\' => array($vendorDir . '/react/promise/src'), + 'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'), + 'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'), + 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), + 'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), + 'DigitalOceanV2\\' => array($baseDir . '/src'), +); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_real.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_real.php new file mode 100755 index 000000000..988ee93b0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/autoload_real.php @@ -0,0 +1,55 @@ + $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(true); + + $includeFiles = require __DIR__ . '/autoload_files.php'; + foreach ($includeFiles as $file) { + composerRequire62dbb165501d1f2d048309fe141bec71($file); + } + + return $loader; + } +} + +function composerRequire62dbb165501d1f2d048309fe141bec71($file) +{ + require $file; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/installed.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/installed.json new file mode 100755 index 000000000..15eb89a48 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/composer/installed.json @@ -0,0 +1,1160 @@ +[ + { + "name": "kriswallsmith/buzz", + "version": "v0.14", + "version_normalized": "0.14.0.0", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/Buzz.git", + "reference": "f802a9b9d01b53496f50c9c5e09b5c779cbbd300" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/f802a9b9d01b53496f50c9c5e09b5c779cbbd300", + "reference": "f802a9b9d01b53496f50c9c5e09b5c779cbbd300", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "suggest": { + "ext-curl": "*" + }, + "time": "2015-06-02 16:48:29", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Buzz": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Lightweight HTTP client", + "homepage": "https://github.com/kriswallsmith/Buzz", + "keywords": [ + "curl", + "http client" + ] + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.7.1", + "version_normalized": "2.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/be3c5ff8d503c46768aeb78ce6333051aa6f26d9", + "reference": "be3c5ff8d503c46768aeb78ce6333051aa6f26d9", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/phpunit-bridge": "~2.7", + "symfony/stopwatch": "~2.3" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2015-06-08 09:37:21", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.3", + "version_normalized": "3.9.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + "time": "2015-03-18 18:23:50", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "name": "react/promise", + "version": "v2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef", + "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "time": "2014-12-30 13:32:42", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@googlemail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP" + }, + { + "name": "guzzlehttp/streams", + "version": "3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/streams.git", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2014-10-12 19:18:40", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ] + }, + { + "name": "guzzlehttp/ringphp", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/RingPHP.git", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "shasum": "" + }, + "require": { + "guzzlehttp/streams": "~3.0", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "time": "2015-05-20 03:37:09", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function." + }, + { + "name": "guzzlehttp/guzzle", + "version": "5.3.0", + "version_normalized": "5.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f3c8c22471cb55475105c14769644a49c3262b93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f3c8c22471cb55475105c14769644a49c3262b93", + "reference": "f3c8c22471cb55475105c14769644a49c3262b93", + "shasum": "" + }, + "require": { + "guzzlehttp/ringphp": "^1.1", + "php": ">=5.4.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" + }, + "time": "2015-05-20 03:47:55", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "version_normalized": "1.0.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "time": "2015-06-14 21:17:01", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ] + }, + { + "name": "symfony/yaml", + "version": "v2.7.1", + "version_normalized": "2.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/9808e75c609a14f6db02f70fccf4ca4aab53c160", + "reference": "9808e75c609a14f6db02f70fccf4ca4aab53c160", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "time": "2015-06-10 15:30:22", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/process", + "version": "v2.7.1", + "version_normalized": "2.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/552d8efdc80980cbcca50b28d626ac8e36e3cdd1", + "reference": "552d8efdc80980cbcca50b28d626ac8e36e3cdd1", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "time": "2015-06-08 09:37:21", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/finder", + "version": "v2.7.1", + "version_normalized": "2.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/c13a40d638aeede1e8400f8c956c7f9246c05f75", + "reference": "c13a40d638aeede1e8400f8c956c7f9246c05f75", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "time": "2015-06-04 20:11:48", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/console", + "version": "v2.7.1", + "version_normalized": "2.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/Console.git", + "reference": "564398bc1f33faf92fc2ec86859983d30eb81806" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Console/zipball/564398bc1f33faf92fc2ec86859983d30eb81806", + "reference": "564398bc1f33faf92fc2ec86859983d30eb81806", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1", + "symfony/phpunit-bridge": "~2.7", + "symfony/process": "~2.1" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "time": "2015-06-10 15:30:22", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "time": "2015-01-24 09:48:32", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context" + }, + { + "name": "sebastian/exporter", + "version": "1.2.0", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "84839970d05254c73cde183a721c7af13aede943" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", + "reference": "84839970d05254c73cde183a721c7af13aede943", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "time": "2015-01-27 07:23:06", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ] + }, + { + "name": "phpspec/php-diff", + "version": "v1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/php-diff.git", + "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a", + "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a", + "shasum": "" + }, + "time": "2013-11-01 13:02:21", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Diff": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Chris Boulton", + "homepage": "http://github.com/chrisboulton", + "role": "Original developer" + } + ], + "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays)." + }, + { + "name": "sebastian/diff", + "version": "1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", + "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "time": "2015-02-22 15:13:53", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ] + }, + { + "name": "sebastian/comparator", + "version": "1.1.1", + "version_normalized": "1.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "time": "2015-01-29 16:28:08", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ] + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "version_normalized": "2.0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "time": "2015-02-03 12:10:50", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ] + }, + { + "name": "phpspec/prophecy", + "version": "v1.4.1", + "version_normalized": "1.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", + "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "phpdocumentor/reflection-docblock": "~2.0", + "sebastian/comparator": "~1.1" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "time": "2015-04-27 22:15:08", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ] + }, + { + "name": "phpspec/phpspec", + "version": "2.2.1", + "version_normalized": "2.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/phpspec.git", + "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/phpspec/zipball/e9a40577323e67f1de2e214abf32976a0352d8f8", + "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.1", + "php": ">=5.3.3", + "phpspec/php-diff": "~1.0.0", + "phpspec/prophecy": "~1.4", + "sebastian/exporter": "~1.0", + "symfony/console": "~2.3", + "symfony/event-dispatcher": "~2.1", + "symfony/finder": "~2.1", + "symfony/process": "~2.1", + "symfony/yaml": "~2.1" + }, + "require-dev": { + "behat/behat": "^3.0.11", + "bossa/phpspec2-expect": "~1.0", + "phpunit/phpunit": "~4.4", + "symfony/filesystem": "~2.1", + "symfony/process": "~2.1" + }, + "suggest": { + "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" + }, + "time": "2015-05-30 15:21:40", + "bin": [ + "bin/phpspec" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "PhpSpec": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "homepage": "http://marcelloduarte.net/" + } + ], + "description": "Specification-oriented BDD framework for PHP 5.3+", + "homepage": "http://phpspec.net/", + "keywords": [ + "BDD", + "SpecBDD", + "TDD", + "spec", + "specification", + "testing", + "tests" + ] + } +] diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.gitignore b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.gitignore new file mode 100755 index 000000000..e3e368dd9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.gitignore @@ -0,0 +1,5 @@ +phpunit.xml +composer.lock +build +vendor +coverage.clover diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.scrutinizer.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.scrutinizer.yml new file mode 100755 index 000000000..aad5e4039 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.scrutinizer.yml @@ -0,0 +1,46 @@ +before_commands: + - "composer install --prefer-source" + +tools: + external_code_coverage: + timeout: 600 + php_code_coverage: + enabled: true + test_command: ./vendor/bin/phpunit + php_code_sniffer: + enabled: true + config: + standard: PSR2 + filter: + paths: ["src/*", "tests/*"] + php_cpd: + enabled: true + excluded_dirs: ["build/*", "tests", "vendor"] + php_cs_fixer: + enabled: true + config: + level: all + filter: + paths: ["src/*", "tests/*"] + php_loc: + enabled: true + excluded_dirs: ["build", "tests", "vendor"] + php_mess_detector: + enabled: true + config: + ruleset: phpmd.xml.dist + design_rules: { eval_expression: false } + filter: + paths: ["src/*"] + php_pdepend: + enabled: true + excluded_dirs: ["build", "tests", "vendor"] + php_analyzer: + enabled: true + filter: + paths: ["src/*", "tests/*"] + php_hhvm: + enabled: true + filter: + paths: ["src/*", "tests/*"] + sensiolabs_security_checker: true diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.install.sh b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.install.sh new file mode 100755 index 000000000..2819188c9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.install.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -x +if [ "$TRAVIS_PHP_VERSION" = 'hhvm' ] || [ "$TRAVIS_PHP_VERSION" = 'hhvm-nightly' ] ; then + curl -sS https://getcomposer.org/installer > composer-installer.php + hhvm composer-installer.php + hhvm -v ResourceLimit.SocketDefaultTimeout=30 -v Http.SlowQueryThreshold=30000 composer.phar update --prefer-source +elif [ "$TRAVIS_PHP_VERSION" = '5.3.3' ] ; then + composer self-update + composer update --prefer-source --no-dev + composer dump-autoload +else + composer self-update + composer update --prefer-source +fi diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.yml new file mode 100755 index 000000000..7f1ec5f98 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/.travis.yml @@ -0,0 +1,22 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - ./.travis.install.sh + - if [ $TRAVIS_PHP_VERSION = '5.6' ]; then PHPUNIT_FLAGS="--coverage-clover coverage.clover"; else PHPUNIT_FLAGS=""; fi + +script: + - if [ $TRAVIS_PHP_VERSION = '5.3.3' ]; then phpunit; fi + - if [ $TRAVIS_PHP_VERSION != '5.3.3' ]; then ./vendor/bin/phpunit $PHPUNIT_FLAGS; fi + - if [ $TRAVIS_PHP_VERSION != '5.3.3' ]; then ./vendor/bin/phpcs --standard=PSR2 ./src/ ./tests/; fi + - if [[ $TRAVIS_PHP_VERSION != '5.3.3' && $TRAVIS_PHP_VERSION != '5.4.29' && $TRAVIS_PHP_VERSION != '5.5.13' ]]; then php -n ./vendor/bin/athletic -p ./tests/DoctrineTest/InstantiatorPerformance/ -f GroupedFormatter; fi + +after_script: + - if [ $TRAVIS_PHP_VERSION = '5.6' ]; then wget https://scrutinizer-ci.com/ocular.phar; php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/CONTRIBUTING.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/CONTRIBUTING.md new file mode 100755 index 000000000..75b84b2aa --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing + + * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) + * The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php) + * Any contribution must provide tests for additional introduced conditions + * Any un-confirmed issue needs a failing test case before being accepted + * Pull requests must be sent from a new hotfix/feature branch, not from `master`. + +## Installation + +To install the project and run the tests, you need to clone it first: + +```sh +$ git clone git://github.com/doctrine/instantiator.git +``` + +You will then need to run a composer installation: + +```sh +$ cd Instantiator +$ curl -s https://getcomposer.org/installer | php +$ php composer.phar update +``` + +## Testing + +The PHPUnit version to be used is the one installed as a dev- dependency via composer: + +```sh +$ ./vendor/bin/phpunit +``` + +Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement +won't be merged. + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/LICENSE new file mode 100755 index 000000000..4d983d1ac --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/README.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/README.md new file mode 100755 index 000000000..393ec7caa --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/README.md @@ -0,0 +1,40 @@ +# Instantiator + +This library provides a way of avoiding usage of constructors when instantiating PHP classes. + +[![Build Status](https://travis-ci.org/doctrine/instantiator.svg?branch=master)](https://travis-ci.org/doctrine/instantiator) +[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) +[![Dependency Status](https://www.versioneye.com/package/php--doctrine--instantiator/badge.svg)](https://www.versioneye.com/package/php--doctrine--instantiator) +[![HHVM Status](http://hhvm.h4cc.de/badge/doctrine/instantiator.png)](http://hhvm.h4cc.de/package/doctrine/instantiator) + +[![Latest Stable Version](https://poser.pugx.org/doctrine/instantiator/v/stable.png)](https://packagist.org/packages/doctrine/instantiator) +[![Latest Unstable Version](https://poser.pugx.org/doctrine/instantiator/v/unstable.png)](https://packagist.org/packages/doctrine/instantiator) + +## Installation + +The suggested installation method is via [composer](https://getcomposer.org/): + +```sh +php composer.phar require "doctrine/instantiator:~1.0.3" +``` + +## Usage + +The instantiator is able to create new instances of any class without using the constructor or any API of the class +itself: + +```php +$instantiator = new \Doctrine\Instantiator\Instantiator(); + +$instance = $instantiator->instantiate('My\\ClassName\\Here'); +``` + +## Contributing + +Please read the [CONTRIBUTING.md](CONTRIBUTING.md) contents if you wish to help out! + +## Credits + +This library was migrated from [ocramius/instantiator](https://github.com/Ocramius/Instantiator), which +has been donated to the doctrine organization, and which is now deprecated in favour of this package. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/composer.json new file mode 100755 index 000000000..4823890b4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/composer.json @@ -0,0 +1,45 @@ +{ + "name": "doctrine/instantiator", + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "type": "library", + "license": "MIT", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "instantiate", + "constructor" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "ext-phar": "*", + "ext-pdo": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0", + "athletic/athletic": "~0.1.8" + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "autoload-dev": { + "psr-0": { + "DoctrineTest\\InstantiatorPerformance\\": "tests", + "DoctrineTest\\InstantiatorTest\\": "tests", + "DoctrineTest\\InstantiatorTestAsset\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpmd.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpmd.xml.dist new file mode 100755 index 000000000..825410562 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpmd.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpunit.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpunit.xml.dist new file mode 100755 index 000000000..0a8d5709b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + ./tests/DoctrineTest/InstantiatorTest + + + + ./src + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php new file mode 100755 index 000000000..3065375a8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +/** + * Base exception marker interface for the instantiator component + * + * @author Marco Pivetta + */ +interface ExceptionInterface +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php new file mode 100755 index 000000000..ea8d28c5c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +use InvalidArgumentException as BaseInvalidArgumentException; +use ReflectionClass; + +/** + * Exception for invalid arguments provided to the instantiator + * + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface +{ + /** + * @param string $className + * + * @return self + */ + public static function fromNonExistingClass($className) + { + if (interface_exists($className)) { + return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className)); + } + + if (PHP_VERSION_ID >= 50400 && trait_exists($className)) { + return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); + } + + return new self(sprintf('The provided class "%s" does not exist', $className)); + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return self + */ + public static function fromAbstractClass(ReflectionClass $reflectionClass) + { + return new self(sprintf( + 'The provided class "%s" is abstract, and can not be instantiated', + $reflectionClass->getName() + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php new file mode 100755 index 000000000..1681e56e8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +use Exception; +use ReflectionClass; +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Exception for given parameters causing invalid/unexpected state on instantiation + * + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface +{ + /** + * @param ReflectionClass $reflectionClass + * @param Exception $exception + * + * @return self + */ + public static function fromSerializationTriggeredException(ReflectionClass $reflectionClass, Exception $exception) + { + return new self( + sprintf( + 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', + $reflectionClass->getName() + ), + 0, + $exception + ); + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $errorString + * @param int $errorCode + * @param string $errorFile + * @param int $errorLine + * + * @return UnexpectedValueException + */ + public static function fromUncleanUnSerialization( + ReflectionClass $reflectionClass, + $errorString, + $errorCode, + $errorFile, + $errorLine + ) { + return new self( + sprintf( + 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' + . 'in file "%s" at line "%d"', + $reflectionClass->getName(), + $errorFile, + $errorLine + ), + 0, + new Exception($errorString, $errorCode) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php new file mode 100755 index 000000000..6d5b3b656 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php @@ -0,0 +1,273 @@ +. + */ + +namespace Doctrine\Instantiator; + +use Closure; +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use ReflectionClass; + +/** + * {@inheritDoc} + * + * @author Marco Pivetta + */ +final class Instantiator implements InstantiatorInterface +{ + /** + * Markers used internally by PHP to define whether {@see \unserialize} should invoke + * the method {@see \Serializable::unserialize()} when dealing with classes implementing + * the {@see \Serializable} interface. + */ + const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; + const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; + + /** + * @var \Closure[] of {@see \Closure} instances used to instantiate specific classes + */ + private static $cachedInstantiators = array(); + + /** + * @var object[] of objects that can directly be cloned + */ + private static $cachedCloneables = array(); + + /** + * {@inheritDoc} + */ + public function instantiate($className) + { + if (isset(self::$cachedCloneables[$className])) { + return clone self::$cachedCloneables[$className]; + } + + if (isset(self::$cachedInstantiators[$className])) { + $factory = self::$cachedInstantiators[$className]; + + return $factory(); + } + + return $this->buildAndCacheFromFactory($className); + } + + /** + * Builds the requested object and caches it in static properties for performance + * + * @param string $className + * + * @return object + */ + private function buildAndCacheFromFactory($className) + { + $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); + $instance = $factory(); + + if ($this->isSafeToClone(new ReflectionClass($instance))) { + self::$cachedCloneables[$className] = clone $instance; + } + + return $instance; + } + + /** + * Builds a {@see \Closure} capable of instantiating the given $className without + * invoking its constructor. + * + * @param string $className + * + * @return Closure + */ + private function buildFactory($className) + { + $reflectionClass = $this->getReflectionClass($className); + + if ($this->isInstantiableViaReflection($reflectionClass)) { + return function () use ($reflectionClass) { + return $reflectionClass->newInstanceWithoutConstructor(); + }; + } + + $serializedString = sprintf( + '%s:%d:"%s":0:{}', + $this->getSerializationFormat($reflectionClass), + strlen($className), + $className + ); + + $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); + + return function () use ($serializedString) { + return unserialize($serializedString); + }; + } + + /** + * @param string $className + * + * @return ReflectionClass + * + * @throws InvalidArgumentException + */ + private function getReflectionClass($className) + { + if (! class_exists($className)) { + throw InvalidArgumentException::fromNonExistingClass($className); + } + + $reflection = new ReflectionClass($className); + + if ($reflection->isAbstract()) { + throw InvalidArgumentException::fromAbstractClass($reflection); + } + + return $reflection; + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) + { + set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) { + $error = UnexpectedValueException::fromUncleanUnSerialization( + $reflectionClass, + $message, + $code, + $file, + $line + ); + }); + + $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); + + restore_error_handler(); + + if ($error) { + throw $error; + } + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) + { + try { + unserialize($serializedString); + } catch (Exception $exception) { + restore_error_handler(); + + throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); + } + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function isInstantiableViaReflection(ReflectionClass $reflectionClass) + { + if (\PHP_VERSION_ID >= 50600) { + return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); + } + + return \PHP_VERSION_ID >= 50400 && ! $this->hasInternalAncestors($reflectionClass); + } + + /** + * Verifies whether the given class is to be considered internal + * + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function hasInternalAncestors(ReflectionClass $reflectionClass) + { + do { + if ($reflectionClass->isInternal()) { + return true; + } + } while ($reflectionClass = $reflectionClass->getParentClass()); + + return false; + } + + /** + * Verifies if the given PHP version implements the `Serializable` interface serialization + * with an incompatible serialization format. If that's the case, use serialization marker + * "C" instead of "O". + * + * @link http://news.php.net/php.internals/74654 + * + * @param ReflectionClass $reflectionClass + * + * @return string the serialization format marker, either self::SERIALIZATION_FORMAT_USE_UNSERIALIZER + * or self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER + */ + private function getSerializationFormat(ReflectionClass $reflectionClass) + { + if ($this->isPhpVersionWithBrokenSerializationFormat() + && $reflectionClass->implementsInterface('Serializable') + ) { + return self::SERIALIZATION_FORMAT_USE_UNSERIALIZER; + } + + return self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER; + } + + /** + * Checks whether the current PHP runtime uses an incompatible serialization format + * + * @return bool + */ + private function isPhpVersionWithBrokenSerializationFormat() + { + return PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513; + } + + /** + * Checks if a class is cloneable + * + * @param ReflectionClass $reflection + * + * @return bool + */ + private function isSafeToClone(ReflectionClass $reflection) + { + if (method_exists($reflection, 'isCloneable') && ! $reflection->isCloneable()) { + return false; + } + + // not cloneable if it implements `__clone`, as we want to avoid calling it + return ! $reflection->hasMethod('__clone'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php new file mode 100755 index 000000000..b665bea85 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Instantiator; + +/** + * Instantiator provides utility methods to build objects without invoking their constructors + * + * @author Marco Pivetta + */ +interface InstantiatorInterface +{ + /** + * @param string $className + * + * @return object + * + * @throws \Doctrine\Instantiator\Exception\ExceptionInterface + */ + public function instantiate($className); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php new file mode 100755 index 000000000..3e8fc6ff4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php @@ -0,0 +1,96 @@ +. + */ + +namespace DoctrineTest\InstantiatorPerformance; + +use Athletic\AthleticEvent; +use Doctrine\Instantiator\Instantiator; + +/** + * Performance tests for {@see \Doctrine\Instantiator\Instantiator} + * + * @author Marco Pivetta + */ +class InstantiatorPerformanceEvent extends AthleticEvent +{ + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->instantiator = new Instantiator(); + + $this->instantiator->instantiate(__CLASS__); + $this->instantiator->instantiate('ArrayObject'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'); + } + + /** + * @iterations 20000 + * @baseline + * @group instantiation + */ + public function testInstantiateSelf() + { + $this->instantiator->instantiate(__CLASS__); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateInternalClass() + { + $this->instantiator->instantiate('ArrayObject'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateSimpleSerializableAssetClass() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateSerializableArrayObjectAsset() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateUnCloneableAsset() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php new file mode 100755 index 000000000..39d9b94df --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php @@ -0,0 +1,83 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest\Exception; + +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Exception\InvalidArgumentException} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Exception\InvalidArgumentException + */ +class InvalidArgumentExceptionTest extends PHPUnit_Framework_TestCase +{ + public function testFromNonExistingTypeWithNonExistingClass() + { + $className = __CLASS__ . uniqid(); + $exception = InvalidArgumentException::fromNonExistingClass($className); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\InvalidArgumentException', $exception); + $this->assertSame('The provided class "' . $className . '" does not exist', $exception->getMessage()); + } + + public function testFromNonExistingTypeWithTrait() + { + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('Need at least PHP 5.4.0, as this test requires traits support to run'); + } + + $exception = InvalidArgumentException::fromNonExistingClass( + 'DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset' + ); + + $this->assertSame( + 'The provided type "DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset" is a trait, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } + + public function testFromNonExistingTypeWithInterface() + { + $exception = InvalidArgumentException::fromNonExistingClass('Doctrine\\Instantiator\\InstantiatorInterface'); + + $this->assertSame( + 'The provided type "Doctrine\\Instantiator\\InstantiatorInterface" is an interface, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } + + public function testFromAbstractClass() + { + $reflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'); + $exception = InvalidArgumentException::fromAbstractClass($reflection); + + $this->assertSame( + 'The provided class "DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset" is abstract, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php new file mode 100755 index 000000000..84154e739 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php @@ -0,0 +1,69 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest\Exception; + +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Exception\UnexpectedValueException} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Exception\UnexpectedValueException + */ +class UnexpectedValueExceptionTest extends PHPUnit_Framework_TestCase +{ + public function testFromSerializationTriggeredException() + { + $reflectionClass = new ReflectionClass($this); + $previous = new Exception(); + $exception = UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $previous); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\UnexpectedValueException', $exception); + $this->assertSame($previous, $exception->getPrevious()); + $this->assertSame( + 'An exception was raised while trying to instantiate an instance of "' + . __CLASS__ . '" via un-serialization', + $exception->getMessage() + ); + } + + public function testFromUncleanUnSerialization() + { + $reflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'); + $exception = UnexpectedValueException::fromUncleanUnSerialization($reflection, 'foo', 123, 'bar', 456); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\UnexpectedValueException', $exception); + $this->assertSame( + 'Could not produce an instance of "DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset" ' + . 'via un-serialization, since an error was triggered in file "bar" at line "456"', + $exception->getMessage() + ); + + $previous = $exception->getPrevious(); + + $this->assertInstanceOf('Exception', $previous); + $this->assertSame('foo', $previous->getMessage()); + $this->assertSame(123, $previous->getCode()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php new file mode 100755 index 000000000..0a2cb9313 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php @@ -0,0 +1,219 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest; + +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Doctrine\Instantiator\Instantiator; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Instantiator} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Instantiator + */ +class InstantiatorTest extends PHPUnit_Framework_TestCase +{ + /** + * @var Instantiator + */ + private $instantiator; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->instantiator = new Instantiator(); + } + + /** + * @param string $className + * + * @dataProvider getInstantiableClasses + */ + public function testCanInstantiate($className) + { + $this->assertInstanceOf($className, $this->instantiator->instantiate($className)); + } + + /** + * @param string $className + * + * @dataProvider getInstantiableClasses + */ + public function testInstantiatesSeparateInstances($className) + { + $instance1 = $this->instantiator->instantiate($className); + $instance2 = $this->instantiator->instantiate($className); + + $this->assertEquals($instance1, $instance2); + $this->assertNotSame($instance1, $instance2); + } + + public function testExceptionOnUnSerializationException() + { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped( + 'As of facebook/hhvm#3432, HHVM has no PDORow, and therefore ' + . ' no internal final classes that cannot be instantiated' + ); + } + + $className = 'DoctrineTest\\InstantiatorTestAsset\\UnserializeExceptionArrayObjectAsset'; + + if (\PHP_VERSION_ID >= 50600) { + $className = 'PDORow'; + } + + if (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513) { + $className = 'DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'; + } + + $this->setExpectedException('Doctrine\\Instantiator\\Exception\\UnexpectedValueException'); + + $this->instantiator->instantiate($className); + } + + public function testNoticeOnUnSerializationException() + { + if (\PHP_VERSION_ID >= 50600) { + $this->markTestSkipped( + 'PHP 5.6 supports `ReflectionClass#newInstanceWithoutConstructor()` for some internal classes' + ); + } + + try { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + + $this->fail('No exception was raised'); + } catch (UnexpectedValueException $exception) { + $wakeUpNoticesReflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + $previous = $exception->getPrevious(); + + $this->assertInstanceOf('Exception', $previous); + + // in PHP 5.4.29 and PHP 5.5.13, this case is not a notice, but an exception being thrown + if (! (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513)) { + $this->assertSame( + 'Could not produce an instance of "DoctrineTest\\InstantiatorTestAsset\WakeUpNoticesAsset" ' + . 'via un-serialization, since an error was triggered in file "' + . $wakeUpNoticesReflection->getFileName() . '" at line "36"', + $exception->getMessage() + ); + + $this->assertSame('Something went bananas while un-serializing this instance', $previous->getMessage()); + $this->assertSame(\E_USER_NOTICE, $previous->getCode()); + } + } + } + + /** + * @param string $invalidClassName + * + * @dataProvider getInvalidClassNames + */ + public function testInstantiationFromNonExistingClass($invalidClassName) + { + $this->setExpectedException('Doctrine\\Instantiator\\Exception\\InvalidArgumentException'); + + $this->instantiator->instantiate($invalidClassName); + } + + public function testInstancesAreNotCloned() + { + $className = 'TemporaryClass' . uniqid(); + + eval('namespace ' . __NAMESPACE__ . '; class ' . $className . '{}'); + + $instance = $this->instantiator->instantiate(__NAMESPACE__ . '\\' . $className); + + $instance->foo = 'bar'; + + $instance2 = $this->instantiator->instantiate(__NAMESPACE__ . '\\' . $className); + + $this->assertObjectNotHasAttribute('foo', $instance2); + } + + /** + * Provides a list of instantiable classes (existing) + * + * @return string[][] + */ + public function getInstantiableClasses() + { + $classes = array( + array('stdClass'), + array(__CLASS__), + array('Doctrine\\Instantiator\\Instantiator'), + array('Exception'), + array('PharException'), + array('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\ExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\FinalExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\PharExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\XMLReaderAsset'), + ); + + if (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513) { + return $classes; + } + + $classes = array_merge( + $classes, + array( + array('PharException'), + array('ArrayObject'), + array('DoctrineTest\\InstantiatorTestAsset\\ArrayObjectAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'), + ) + ); + + if (\PHP_VERSION_ID >= 50600) { + $classes[] = array('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + $classes[] = array('DoctrineTest\\InstantiatorTestAsset\\UnserializeExceptionArrayObjectAsset'); + } + + return $classes; + } + + /** + * Provides a list of instantiable classes (existing) + * + * @return string[][] + */ + public function getInvalidClassNames() + { + $classNames = array( + array(__CLASS__ . uniqid()), + array('Doctrine\\Instantiator\\InstantiatorInterface'), + array('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'), + ); + + if (\PHP_VERSION_ID >= 50400) { + $classNames[] = array('DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset'); + } + + return $classNames; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php new file mode 100755 index 000000000..fbe28ddd0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php @@ -0,0 +1,29 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +abstract class AbstractClassAsset +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php new file mode 100755 index 000000000..56146d709 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; + +/** + * Test asset that extends an internal PHP class + * + * @author Marco Pivetta + */ +class ArrayObjectAsset extends ArrayObject +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php new file mode 100755 index 000000000..43bbe46b7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Exception; + +/** + * Test asset that extends an internal PHP base exception + * + * @author Marco Pivetta + */ +class ExceptionAsset extends Exception +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php new file mode 100755 index 000000000..7d268f5b3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Exception; + +/** + * Test asset that extends an internal PHP base exception + * + * @author Marco Pivetta + */ +final class FinalExceptionAsset extends Exception +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php new file mode 100755 index 000000000..553fd5612 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Phar; + +/** + * Test asset that extends an internal PHP class + * + * @author Marco Pivetta + */ +class PharAsset extends Phar +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php new file mode 100755 index 000000000..42bf73e7e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php @@ -0,0 +1,44 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use PharException; + +/** + * Test asset that extends an internal PHP class + * This class should be serializable without problems + * and without getting the "Erroneous data format for unserializing" + * error + * + * @author Marco Pivetta + */ +class PharExceptionAsset extends PharException +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php new file mode 100755 index 000000000..ba19aaf63 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php @@ -0,0 +1,62 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; +use Serializable; + +/** + * Serializable test asset that also extends an internal class + * + * @author Marco Pivetta + */ +class SerializableArrayObjectAsset extends ArrayObject implements Serializable +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * {@inheritDoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritDoc} + * + * Should not be called + * + * @throws BadMethodCallException + */ + public function unserialize($serialized) + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php new file mode 100755 index 000000000..39f84a6c0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php @@ -0,0 +1,61 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Serializable; + +/** + * Base serializable test asset + * + * @author Marco Pivetta + */ +class SimpleSerializableAsset implements Serializable +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * {@inheritDoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritDoc} + * + * Should not be called + * + * @throws BadMethodCallException + */ + public function unserialize($serialized) + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php new file mode 100755 index 000000000..04e78069d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php @@ -0,0 +1,29 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +/** + * A simple trait with no attached logic + * + * @author Marco Pivetta + */ +trait SimpleTraitAsset +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php new file mode 100755 index 000000000..7d03bdab0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php @@ -0,0 +1,50 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; + +/** + * Base un-cloneable asset + * + * @author Marco Pivetta + */ +class UnCloneableAsset +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * Magic `__clone` - should not be invoked + * + * @throws BadMethodCallException + */ + public function __clone() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php new file mode 100755 index 000000000..b348a4053 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php @@ -0,0 +1,39 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +class UnserializeExceptionArrayObjectAsset extends ArrayObject +{ + /** + * {@inheritDoc} + */ + public function __wakeup() + { + throw new BadMethodCallException(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php new file mode 100755 index 000000000..18dc6711d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php @@ -0,0 +1,38 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +class WakeUpNoticesAsset extends ArrayObject +{ + /** + * Wakeup method called after un-serialization + */ + public function __wakeup() + { + trigger_error('Something went bananas while un-serializing this instance'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php new file mode 100755 index 000000000..39ee6992a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use XMLReader; + +/** + * Test asset that extends an internal PHP class + * + * @author Dave Marshall + */ +class XMLReaderAsset extends XMLReader +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.gitignore b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.gitignore new file mode 100755 index 000000000..893035d5b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.gitignore @@ -0,0 +1,27 @@ +# Ingore common cruft +.DS_STORE +coverage +.idea + +# Ignore binary files +guzzle.phar +guzzle-min.phar + +# Ignore potentially sensitive phpunit file +phpunit.xml + +# Ignore composer generated files +composer.phar +composer.lock +composer-test.lock +vendor/ + +# Ignore build files +build/ +phing/build.properties + +# Ignore subsplit working directory +.subsplit + +docs/_build +docs/*.pyc diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.travis.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.travis.yml new file mode 100755 index 000000000..209e05cd6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/.travis.yml @@ -0,0 +1,17 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - curl --version + - pecl install uri_template-beta || echo "pecl uri_template not available" + - composer self-update + - composer install --no-interaction --prefer-source --dev + - ~/.nvm/nvm.sh install v0.6.14 + +script: composer test diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/CHANGELOG.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/CHANGELOG.md new file mode 100755 index 000000000..f0dc5444a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/CHANGELOG.md @@ -0,0 +1,751 @@ +# CHANGELOG + +## 3.9.3 - 2015-03-18 + +* Ensuring Content-Length is not stripped from a request when it is `0`. +* Added more information to stream wrapper exceptions. +* Message parser will no longer throw warnings for malformed messages. +* Giving a valid cache TTL when max-age is 0. + +## 3.9.2 - 2014-09-10 + +* Retrying "Connection died, retrying a fresh connect" curl errors. +* Automatically extracting the cacert from the phar in client constructor. +* Added EntityBody support for OPTIONS requests. + +## 3.9.1 - 2014-05-07 + +* Added a fix to ReadLimitEntityBody to ensure it doesn't infinitely loop. +* Added a fix to the stream checksum function so that when the first read + returns a falsey value, it still continues to consume the stream until EOF. + +## 3.9.0 - 2014-04-23 + +* `null`, `false`, and `"_guzzle_blank_"` all now serialize as an empty value + with no trailing "=". See dc1d824277. +* No longer performing an MD5 check on the cacert each time the phar is used, + but rather copying the cacert to the temp directory. +* `"0"` can now be added as a URL path +* Deleting cookies that are set to empty +* If-Modified-Since is no longer unnecessarily added to the CachePlugin +* Cookie path matching now follows RFC 6265 s5.1.4 +* Updated service descriptions are now added to a service client's composite + factory. +* MockPlugin now throws an exception if the queue is empty. +* Properly parsing URLs that start with "http" but are not absolute +* Added the ability to configure the curl_multi_select timeout setting +* OAuth parameters are now sorted using lexicographical byte value ordering +* Fixing invalid usage of an out of range PHP feature in the ErrorResponsePlugin + +## 3.8.1 -2014-01-28 + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +## 3.8.0 - 2013-12-05 + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +## 3.7.4 - 2013-10-02 + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +## 3.7.3 - 2013-09-08 + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +## 3.7.2 - 2013-08-02 + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +## 3.7.1 - 2013-07-05 + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +## 3.7.0 - 2013-06-10 + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +## 3.6.0 - 2013-05-29 + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +## 3.5.0 - 2013-05-13 + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +## 3.4.3 - 2013-04-30 + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +## 3.4.2 - 2013-04-29 + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +## 3.4.1 - 2013-04-16 + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +## 3.4.0 - 2013-04-11 + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +## 3.3.1 - 2013-03-10 + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +## 3.3.0 - 2013-03-03 + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +## 3.2.0 - 2013-02-14 + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +## 3.1.2 - 2013-01-27 + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +## 3.1.1 - 2013-01-20 + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +## 3.1.0 - 2013-01-12 + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +## 3.0.7 - 2012-12-19 + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +## 3.0.6 - 2012-12-09 + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +## 3.0.5 - 2012-11-18 + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +## 3.0.4 - 2012-11-11 + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +## 3.0.3 - 2012-11-04 + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +## 3.0.2 - 2012-10-25 + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +## 3.0.1 - 2012-10-22 + +* Models can now be used like regular collection objects by calling filter, map, etc +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +## 3.0.0 - 2012-10-15 + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +## 2.8.8 - 2012-10-15 + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +## 2.8.7 - 2012-09-30 + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +## 2.8.6 - 2012-09-05 + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +## 2.8.5 - 2012-08-29 + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +## 2.8.4 - 2012-08-15 + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +## 2.8.3 - 2012-07-30 + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()`` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +## 2.8.2 - 2012-07-24 + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`` +* Bug: ``+`` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +## 2.8.1 - 2012-07-16 + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +## 2.8.0 - 2012-07-15 + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +## 2.7.2 - 2012-07-02 + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +## 2.7.1 - 2012-06-26 + +* Minor patch to update version number in UA string +* Updating build process + +## 2.7.0 - 2012-06-25 + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +## 2.6.6 - 2012-06-10 + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +## 2.6.5 - 2012-06-03 + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +## 2.6.4 - 2012-05-30 + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +## 2.6.3 - 2012-05-23 + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +## 2.6.2 - 2012-05-19 + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +## 2.6.1 - 2012-05-19 + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +## 2.6.0 - 2012-05-15 + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +## 2.5.0 - 2012-05-08 + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/LICENSE new file mode 100755 index 000000000..d51aa6986 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/README.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/README.md new file mode 100755 index 000000000..6be06bf47 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/README.md @@ -0,0 +1,57 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +# This is an old version of Guzzle + +This repository is for Guzzle 3.x. Guzzle 5.x, the new version of Guzzle, has +been released and is available at +[https://github.com/guzzle/guzzle](https://github.com/guzzle/guzzle). The +documentation for Guzzle version 5+ can be found at +[http://guzzlephp.org](http://guzzlephp.org). + +Guzzle 3 is only maintained for bug and security fixes. Guzzle 3 will be EOL +at some point in late 2015. + +### About Guzzle 3 + +[![Composer Downloads](https://poser.pugx.org/guzzle/guzzle/d/total.png)](https://packagist.org/packages/guzzle/guzzle) + [![Build Status](https://secure.travis-ci.org/guzzle/guzzle3.png?branch=master)](http://travis-ci.org/guzzle/guzzle3) + +- Extremely powerful API provides all the power of cURL with a simple interface. +- Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests. +- Service description DSL allows you build awesome web service clients faster. +- Symfony2 event-based plugin system allows you to completely modify the behavior of a request. + +Get answers with: [Documentation](http://guzzle3.readthedocs.org/en/latest/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net) + +### Installing via Composer + +The recommended way to install Guzzle is through [Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/guzzle:~3.9 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` +## Known Issues + +1. Problem following a specific redirect: https://github.com/guzzle/guzzle/issues/385. + This has been fixed in Guzzle 4/5. +2. Root XML attributes not serialized in a service description: https://github.com/guzzle/guzzle3/issues/5. + This has been fixed in Guzzle 4/5. +3. Accept-Encoding not preserved when following redirect: https://github.com/guzzle/guzzle3/issues/9 + Fixed in Guzzle 4/5. +4. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 + Fixed in Guzzle 4/5. +5. Recursive model references with array items: https://github.com/guzzle/guzzle3/issues/13 + Fixed in Guzzle 4/5 +6. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 + Fixed in Guzzle 4/5. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/UPGRADING.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/UPGRADING.md new file mode 100755 index 000000000..f58bf1171 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/UPGRADING.md @@ -0,0 +1,537 @@ +Guzzle Upgrade Guide +==================== + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml + + + + + + Get a list of groups + + + Uses a search query to get a list of groups + + + + Create a group + + + + + Delete a group by ID + + + + + + + Update a group + + + + + + +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/build.xml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/build.xml new file mode 100755 index 000000000..2aa62ba9a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/composer.json new file mode 100755 index 000000000..59424b39b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/composer.json @@ -0,0 +1,82 @@ +{ + "name": "guzzle/guzzle", + "type": "library", + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + + "require": { + "php": ">=5.3.3", + "ext-curl": "*", + "symfony/event-dispatcher": "~2.1" + }, + + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + + "scripts": { + "test": "phpunit" + }, + + "require-dev": { + "doctrine/cache": "~1.3", + "symfony/class-loader": "~2.1", + "monolog/monolog": "~1.0", + "psr/log": "~1.0", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3", + "phpunit/phpunit": "3.7.*" + }, + + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/Makefile b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/Makefile new file mode 100755 index 000000000..d92e03f95 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json new file mode 100755 index 000000000..81683026b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json @@ -0,0 +1,176 @@ +{ + "additionalProperties": true, + "name": { + "type": "string", + "description": "Name of the web service" + }, + "apiVersion": { + "type": ["string", "number"], + "description": "Version identifier that the service description is compatible with" + }, + "baseUrl": { + "type": "string", + "description": "Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the process defined in RFC 2396" + }, + "basePath": { + "type": "string", + "description": "Alias of baseUrl" + }, + "_description": { + "type": "string", + "description": "Short summary of the web service. This is actually called 'description' but this JSON schema wont validate using just description." + }, + "operations": { + "description": "Operations of the web service", + "type": "object", + "properties": { + "extends": { + "type": "string", + "description": "Extend from another operation by name. The parent operation must be defined before the child." + }, + "httpMethod": { + "type": "string", + "description": "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" + }, + "uri": { + "type": "string", + "description": "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" + }, + "summary": { + "type": "string", + "description": "Short summary of what the operation does" + }, + "class": { + "type": "string", + "description": "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand" + }, + "responseClass": { + "type": "string", + "description": "This is what is returned from the method. Can be a primitive, class name, or model name." + }, + "responseNotes": { + "type": "string", + "description": "A description of the response returned by the operation" + }, + "responseType": { + "type": "string", + "description": "The type of response that the operation creates. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default.", + "enum": [ "primitive", "class", "model", "documentation" ] + }, + "deprecated": { + "type": "boolean", + "description": "Whether or not the operation is deprecated" + }, + "errorResponses": { + "description": "Errors that could occur while executing the operation", + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "number", + "description": "HTTP response status code of the error" + }, + "reason": { + "type": "string", + "description": "Response reason phrase or description of the error" + }, + "class": { + "type": "string", + "description": "A custom exception class that would be thrown if the error is encountered" + } + } + } + }, + "data": { + "type": "object", + "additionalProperties": "true" + }, + "parameters": { + "$ref": "parameters", + "description": "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." + }, + "additionalParameters": { + "$ref": "parameters", + "description": "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." + } + } + }, + "models": { + "description": "Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP response is parsed into a Guzzle\\Service\\Resource\\Model object.", + "type": "object", + "properties": { + "$ref": "parameters", + "description": "Parameters of the model. When a model is referenced in a responseClass attribute of an operation, parameters define how a HTTP response message is parsed into a Guzzle\\Service\\Resource\\Model." + } + }, + "includes": { + "description": "Service description files to include and extend from (can be a .json, .js, or .php file)", + "type": "array", + "items": { + "type": "string", + "pattern": ".+\\.(js|json|php)$" + } + }, + "definitions": { + "parameters": { + "extends": "http://json-schema.org/schema", + "id": "parameters", + "name": { + "type": "string", + "description": "Unique name of the parameter" + }, + "type": { + "type": ["string", "array"], + "description": "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." + }, + "instanceOf": { + "type": "string", + "description": "When the type is an object, you can specify the class that the object must implement" + }, + "required": { + "type": "boolean", + "description": "Whether or not the parameter is required" + }, + "default": { + "description": "Default value to use if no value is supplied" + }, + "static": { + "type": "bool", + "description": "Set to true to specify that the parameter value cannot be changed from the default setting" + }, + "description": { + "type": "string", + "description": "Documentation of the parameter" + }, + "location": { + "type": "string", + "description": "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" + }, + "sentAs": { + "type": "string", + "description": "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." + }, + "filters": { + "type": "array", + "description": "Array of static method names to to run a parameter value through. Each value in the array must be a string containing the full class path to a static method or an array of complex filter information. You can specify static methods of classes using the full namespace class name followed by ‘::’ (e.g. FooBar::baz()). Some filters require arguments in order to properly filter a value. For complex filters, use a hash containing a ‘method’ key pointing to a static method, and an ‘args’ key containing an array of positional arguments to pass to the method. Arguments can contain keywords that are replaced when filtering a value: '@value‘ is replaced with the value being validated, '@api‘ is replaced with the Parameter object.", + "items": { + "type": ["string", { + "object": { + "properties": { + "method": { + "type": "string", + "description": "PHP function to call", + "required": true + }, + "args": { + "type": "array" + } + } + } + }] + } + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png new file mode 100755 index 0000000000000000000000000000000000000000..f1017f7e6028c14a9e0694c66a6cfbb2d546adf5 GIT binary patch literal 803 zcmV+;1Kj+HP)@ zosv>1reIXPlo1y){RjSw2_NWGX5O-#W+NK3tBQ_IN%bh7xZ1Yehws80|4azI;aWIJ z_%xhlcTubTO7Dbx z)F-R8gg5MzGv|t4=e_El4GCwW0m6?C;0bG4DRC^TH6-pa>y8_h*QBud6Ms>Qf{oN> z=Q($Dn|DINB{`Ea{)h&^x;i{)XQ{?Z&id71eOkRPqgl17&E%|dJIC&SCwnd zY4oUR`OVorB8A||QZjLWS~cr&OD?HEtM@^bIovNUC5An6?z`xDgJL2H2Mya@dku<1YUfi&QvS8KS8=~uOs!oaF z8OMF7-5yyh}yDkaCp7Ob8b;wv(27WLL#lglguF0fh3d(d@ zP%vrDIA~G}dL)X;YnCMSE4ZM-gfVsYTLItd3J`~_vw^k=W%C_MlG002ovPDHLkV1oLqbt3=( literal 0 HcmV?d00001 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/homepage.css b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/homepage.css new file mode 100755 index 000000000..70c46d8d5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/homepage.css @@ -0,0 +1,122 @@ +/* Hero unit on homepage */ + +.hero-unit h1 { + font-size: 49px; + margin-bottom: 12px; +} + +.hero-unit { + padding: 40px; +} + +.hero-unit p { + font-size: 17px; +} + +.masthead img { + float: left; + margin-right: 17px; +} + +.hero-unit ul li { + margin-left: 220px; +} + +.hero-unit .buttons { + text-align: center; +} + +.jumbotron { + position: relative; + padding: 40px 0; + color: #fff; + text-shadow: 0 1px 3px rgba(0,0,0,.4), 0 0 30px rgba(0,0,0,.075); + background: #00312F; + background: -moz-linear-gradient(45deg, #002F31 0%, #335A6D 100%); + background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#00312D), color-stop(100%,#33566D)); + background: -webkit-linear-gradient(45deg, #020031 0%,#334F6D 100%); + background: -o-linear-gradient(45deg, #002D31 0%,#334D6D 100%); + background: -ms-linear-gradient(45deg, #002F31 0%,#33516D 100%); + background: linear-gradient(45deg, #020031 0%,#33516D 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#020031', endColorstr='#6d3353',GradientType=1 ); + -webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); + -moz-box-shadow: inset 0 3px 7px rgba(0,0,0,.2), inset 0 -3px 7px rgba(0,0,0,.2); + box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); +} + +.jumbotron h1 { + font-size: 80px; + font-weight: bold; + letter-spacing: -1px; + line-height: 1; +} + +.jumbotron p { + font-size: 24px; + font-weight: 300; + line-height: 1.25; + margin-bottom: 30px; +} + +.masthead { + padding: 40px 0 30px; + margin-bottom: 0; + color: #fff; + margin-top: -19px; +} + +.masthead h1 { + display: none; +} + +.masthead p { + font-size: 40px; + font-weight: 200; + line-height: 1.25; + margin: 12px 0 0 0; +} + +.masthead .btn { + padding: 19px 24px; + font-size: 24px; + font-weight: 200; + border: 0; +} + +/* Social bar on homepage */ + +.social { + padding: 2px 0; + text-align: center; + background-color: #f5f5f5; + border-top: 1px solid #fff; + border-bottom: 1px solid #ddd; + margin: 0 0 20px 0; +} + +.social ul { + margin-top: 0; +} + +.social-buttons { + margin-left: 0; + margin-bottom: 0; + padding-left: 0; + list-style: none; +} + +.social-buttons li { + display: inline-block; + padding: 5px 8px; + line-height: 1; + *display: inline; + *zoom: 1; +} + +.center-announcement { + padding: 10px; + background-color: rgb(238, 243, 255); + border-radius: 8px; + text-align: center; + margin: 24px 0; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/logo.png b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..965a4ef4139180ea7d33594feba2394dfce6774a GIT binary patch literal 247678 zcmb5WW3woL|(Mg6aWv=`TK0sufq`d-{kM59@!F*@2P9srH!gpS{!mKT-Z;`u~dX|C{OE-UE#F<+ZcjUa!t4 z7wtq|=KmEZTN&t1@}!IM--|wLAQO=oW-a$)ZqH&aL|tU%5w7_)b^BfKm+ZuLt{tJI zkYAsjm7kx*sIy+JigZ>_V}k;31+&}$9@f8q!!t4s{ZBG7lN9E+HY^9@E<(=c+pnhu z`xE~hM?D(*{%ZPaZf{;V9e=dWPWtAg_VF};o_9vizND3`J*VgE9xx(ON(Ka3Bu=D= zGq)yON;8ACjTNi<)Q*%UF}{whlq5G*5e)OL!EED@<|@T*mDH-$86JCzs&9DY_j4NY zWs^ZT4DKCQraikO^_41vk2+aZ@&(PpL4TR6f{kTYZ}*@Z8LW#OLGbHt~F(C zI?ud!cc^y9N%O}b@$3U|t&T0qPR3Q%@4aS^xQCUVz&ndvc za7&a5E6zF*Og=K(VrAw>jOdEw<|S5OwtC|(t-8oo3&vf`R%MbZZX-w`=PPC4LzHSY zR6JDebZPN%TCLVU?(hl+c34{?-p+vxYN#W9$Z$k)BS@DGZ5gKW;p{hj%9H=9&K?vr&a9X6dA2&f!M;D;N(@pEj$D*VB__^>an*WQz%Kqa|trML0jD;2t28YGww8`GsL7%3~e@4 z+700|R@S3m6bVj=hE7aLLtq-Dp(PCHPB^XYG*zYrtlQ^%?{lXC*V+N;NzmUWBS8zw z75&48=*UszWT5o4QPi@8*-Ga+iOrZZ5!vq2vr;0L?aIRvgr^Lx&}M)cOlBGBlq3PY zyTAF24dqR_2rkzgM0Yq;OtU51t$N?|4bQ1vog!058bH6N&Y3#+kN$T zhc`Sl4|rkRNGJ_FZSnDBVw?HeHYCcF_103U!UDu-X)=~zHX$z%rOOpcI)j=ZJF*ex zNd?qQ(rqM+TKE=eI47c+Ivz9xLv*4a1a|5*Gst_(OWg@l&dszfKCjN$c>0!j zd~=oc)RJ*8{I0FmRoEF!tXejIYh8>jNA}n69`Bxz+}R}eb&9-qZWA|HN!Etg?xVHv zTk+#BQulfE5u^}l3aljwkl|rgOJu6_Qa}Y<74M0*NVHk{2CkX=YkO*pu_g86PQ^UA6aJ^JYW%CFS8o8Us^aHBI+A&sI#PQZ*z& z6Ifl~RNFxsqNfD==V=y@IwF5w{Vx=_Xd{KQc{y5cu0RC&_%Z1M6JaY`OT2_vp>QBV zrpWk*Bs(Yc0(5?e*~amy#qp}g-XgR00`JN?U?HRq?`%iOi68k$v-k@N5Q@^UX7sS}2j!TiaFQhw_uM9oq-V=}sNL<-;{1 znG$54-jC%c>Uugi>fv3wH!?YCMc8}kHH0oISlW98juay8DN8m)HxyKtyGZ5{jQW>yREsFIX@`0qovs_}RjQQ%5mLSQw-QC5+Mp~`r zkkd@gINa8z<-bDNq# z(0ume9)8E{jHwx7d@>`=yX@b^wdLXbvqJ*&Y`?POBPO-2J3SG2W7MA_Lxm;VtD2-A zk!Hn99VY2>lrpe*2o~O%whk*Z?k?e=b2jEAacgRQ1}j@YGV7%TgWx`J3pJPG_>A>j z2Et1X0RxVjt5LRcoxm7evEN_+`4Oa${B+qWW8?1nat2(yr}@*n1`1ba%7jo1clm^z-n-`nF~57w#~!{C!wjAR-Lvykk|x{|DglX zxgl0m{mK9(6FGisiN~RTF_Kg)Zptw~o0-OioXiXw24ii`Vt9#{-pXjR5EYV#hTBv> zCFJ@CyCCuA5k%X>;zvGpd9uY3O4e4^tb`BfI!QcwUc0*v*%U_>5@T&Vf7tf<3FPs) zt;F`ebJV>KSK%~|kE=h`c|c~*Ah92rgNsezat@m)N{KgDkm*Oz4#^?gF5N@lw16%o zjDM*U^rFaU=gv3b9Hcf1y}XDKUcz}#7*V#6CY0Nj$$Z$?R`mL=Z{m1SI3%0zM4~dc zcdemPCl6*$M{buY3zjQDE}G|K&W@S#o(P(Ppq)V2x{xSnDjQM)*ps``)m zj}OAQ<(&D&YQ>(1QsGgLRwJSm_D2_;B+irAl|Hqp7ou2HNfpVL#O%dT;gqFNYtv1_ zp}yrnzq$Xh9T(bNJCCEUe+SYD?~m^wziwE}1;Q%8+OY|MjXuBdTb z2@M^0OYhe2H?=4{CXMrxbP|xHl^z4zv$my=`8u^sE3=Bk%0HJlBRC|D9j_Ce*Uu5I}?wPT=oYjrY znKFVvy&cTEzuA^Vd7SWrzV!({ycN~z;r!P8DD(uQ2wO@UFb*6Guw6U%> zwtw&QRP1vEo#_BH?L}eKdS$g0OfqUr%64Hq-7`wy8WVy*bUQNHA*-A9BE-w6|F>wL zy=a@>B z-Oml*$#|&0AI;Rbrh3FPs1g3x&5iHInpNTitCsMr`#T}6e}g<=smA-_c2_r!(w(w> z6dEP(q%2jifd`MSqgZIWl`F#5SP4z$or(74q_fk=zK{s0x~`7x!S48I&pRZ9bFcMX zCNMvGNR}_!S1>z~zO|pHbjAze_i_MDAD(PE_(B5^Ww2zqP{~Qx#{KGRSxJt;+GQ@t zZ#Co!qV;KOTNsCbtZ1^Vp1a*6pX)&uc25JUgTi8Z2 z=s`?SLp`t{_&hIgUxVLa0-UKXm@|!rEAd$#xLtcYl5Ggc1zF2^OWBTfTQDmT6{$1d z=FQZ@?=AVh7g0?H2P{M=3tr~MbZk4bJ_uu-^Yv@!!nvVlLBl^;eZ$$1|KY<`gR~=> zQ7W2rCXKF!fJ_9FyQi;;e*bM}z=lx^!GersV{ZPInc5UDMHr|p@Y$)@{O^(YEqo2# z!)sqI7B!>_NUDw!EH*vf>#ipa%-EL}ZNxDv0lTfbsyv@s`#s`V2m-`*jH1Z)-&mQn zq~xe9(H}2eK$9jA5bpgFBfq|~#=~($6OB^&D&$?OvsJx3aCg3=B)!_T(9+~GwzHzG6HTFgya_Ji4Nts@a42+HP!4357w%A5~&R50MDap z;LUW&U^euclB`du2??miw^NGT?6r~z-&(Z^1gxyGi;jyN^RfHODLTzaLROnTx8v2>1xuC? zE5z+tfcyE#?y>C*YmgRa=4!T*qT^zSO>xh+l`fE36Nqfqqf7GGKrewty!dvb2Q&7S zW!}S7SQB5*XXEm<{I~;A_PvpH>tT~V+-@ZQ`U?<|+QG{|@+zxx{ei&L^Hm4j zIfkvDnVgxhl-1ImsI!%r!8A^0qS)a!4Z@-n@d>!0&zyjpU*~+NOK+2vqb=l&Lf4&Q z9R?CDRCd;yS7Ug7B|llw${R+I`$75&8{^C0-cEi@&iKR*;T6bGn=vNNC--y}b!|m9 zc@=y!p?Ia5JtlQjR}#x@+nRsSDM0}(BD?_|!z8AvIG!&ZW{I)Yad%C~k!hVnY=nsK zO4M0A@&_(22iZomPOTqpJ}+2#@#XXq`-8GMn&?VdVJ?>*Xt#$yh2(n>Sl+EOAT2g? zwLK>%CqH{6*+oXV?kBtE>rrPMO_;`;+j+Z zW~BD5ll=HS4MKZYpCjS#q zE*cOdGNK`D#S&=bt2A05qbt?f+@k75lythn2o;aAGM@T`7kfH(1KjKA(m)m`dsR1r z32JtAwP77WQRy<5QyD*9i*(YcJU40oW#0H~nmAb{&Zou>8m|{d4GaoWAko$~!4$x) z8?m9QBWCdKKmz6Jhd`&KB$GDjnl4h{Qu;v+L+fP-idTZp(*p`Lqi5>g^%ojmV@lJ7 zcEeuFN+!s(LnUef82%vA; zA1CwuXVk1n>__uWuf9S}AxXO+`}QGx<#QUf&cMd=?$z4vBs5B4{IjJ!z4wc`78K@TK9iFtKEk!_+36)R#zn@Ke97qmAFj z!1MCH-Pm}LzZNtX1aD|ru6bfDI!a#|$z5AcYns=AwpV8nql3>8>m;BKqb|y~ixQQ1 zzh>TfN6$Ef?RU-KsRHeH^k~gy17ja;US;lH5<$%h+l)1s`cT z^MyqF3-mqtrl8>=x;AY^-cp(g_7iGi1&SzF+(!BxI`>v$?#->(gC}75NJ5CsK}r@! z>3SL4;JeeJBc!)r^XLL-+MAbTwMiv-MW_?>LQCoc{=2V6!Q|Ii+N{49Sp;oo#P`5r z0_KMq9~|}ILv{SAZWr1tqCE<*P0-^*y~wK+(a5XQ0pFGLRvEfc6z&Y(WKp!tqal z?GN#H$D(MHaavvNT@Sh1Tx#3@j55fcfqvH!ug51#f^|!F%MNqS9C3tJs*p& z{UYg~^MxlK2AGG#mA%$Po>V?Ah?q@%e`mn{nTE}3-56)&UxY~H8)t4;3tK>)eRI=R zsp=@N=rA7TDO893yAvk_Q%IQ2X1>m-x6|r3F^X7zcPiv+`7@Wig^X-t!G$N#$w*E~ zu%^ZG1||E3gdROxWc^Yb{dfXG)D;AJEQ>PD=^AFN{yR~2mqiS*!ffk>@c;v?^K!P$ z@H_vG93NJMPZ#-Dwbo_d!$dFmY7fQ$rzufhK8+8)gK(|7SiT$BWXa^!6+o z$cJDT7F`@ef@rU}^__?R3Rg%jZYBam&nfhrnRPIU+na7BKuZAW`8Qie+rIA`V)BvA zHen_){ciV6>xiv7iu6{1Z%-IWn1$8`*!aeGtvUiuI_BBQ>R+-a$u<^2RD#yx!+>5l zl8m>HP8sWkiGDj37-1cMmY-Vw^;17#s|od&t?j~JypJ&{;~9ts8g1ZT9|G^M*p?TZ zZI^+i#TDui)N3b5<0eV{(-5(D?bGxRDu@skSE%Xr-6dtPBd-C2>uBQtwk0wWJI$n< z4z*f|OuEZc=IlDn&?=JYCBkxl9{U3QOK!FS56Ik2<*MS|FQVGu!(cw?9(a_^x*Bx0 zAgqE?(WIM{mLU-Gv6gWF5&01F_j+El#>Rcz_q!{?Iupq(yit`6emL0?IHs87J)ZT> zyBmfEpCBQSynXc~4qRTocaHN$&G__Y?o4Wpwny;v&Opj+E!+sY8W`LL@aa-LkY1p? z!GZu2lF^|2EDPuC(O4prK^p-b1{4-KJ1_XPf!@|?(&yu9z6keEgJP(n!&%s?Lf0l#36yB(v)?(I&`I5Q?M$oov0 zV1zIDHU@fGDC272Wa1A$9ye6l2)F*pwRd<-^j{|1Og|8bxm1-Z)q$7cfIxiu=9i&`4yQ15FtgV758v>? z7#c<4JXY9l=fgHIn=ka3&hK~RFYw;o`6Z>8rANtiMAcvRA|&^*l)52M{fbK$aWHmj zrUSxbQfV7=3|Vq~eG6|if^-_(5Sb>%>(fGW=5NzQ{|T@DMsqj`L=dehOQqujo12JC zH7{f~q>x{9Z(lW`gb^8Xd5s1?HxEe6*PPsH9(x9C!PIm(bYOvh4n*stz8MJ3WsAfw z^xtWDOBoFdYO1c@2;u1}Nwz3HwPMrB4tfLAwet@)ddKa2YKiBPl!;9@OkHA>_>T^aRVe3>M#>-!D~S~RzI=mOml1_--Y`| z^_w*I9M??I*Lv`8*wzlo8p!;0Twh9gqPSaW1}}c8Sch>X4!?M|SmH7k*SFw;K`Dh% znD(34i#x8+qc-Ll({=bkfOY%5Ur8KBxZUWK)Dw9`Lk-9-6AkC=85R)Gf*(1DIN?*AGNZ_Xn z#p975B5C|r4N3!l)!^aBKfK5{f5Sa5lgia?F1{iS)Sf5`BObD`UV zOZJ=I3GPo@($#bU@%)^g^5m*OWb1EbRqnKm8Yo$0PSL?8<6EV;2njdx>Tdv3mbjK9 zf5q;suKiwmjz&qAn^nLOe3a9!aq>yAWAgLp;g~i`q<#fLni%DoRkuh#3J8(}BM1iP zG&q`fuGU9+%Qa7>UPeH7+HpItWZ+_2(<5y-yW~Rwv^vY^d_JlpFtc-nfX42$a}Zw! zkqm3^06n_StAfyCwnlbEBOnpqnQ2kV&71PCK$8bORt0R?d)QOYN`IG;67EMrBc#~} zlhD}Ox=Mvb4=jm_3h?6)Q4eg(a}h?np;djTz!n8NP+1_NCd(fI*jMil59~3$Z68XM zWU^#PrMbgFk2t83uDXg#3-6GKk5~jUN`g+>sFMHvTl!m*hU9dCj-pQTLu-6Mj(f(L zDcuY85-djQiya4*oBz6%a@%v9l8^3e8@TKxNLYFE54dqt$JQ%{Zue4kU5(6HZ{+F< zPGI1wi(T~UtvK7rALB}1so@HsR|f*^9f@@5mZiM3Hd_PAU`FVvgG{v^K2TnDcHp0K zb=`4hX1{bq_k~Hn&Z-ao#BbN5U-+Ie5vk4PYcu%)0EVw%6)c(FuCKmTZ{Jlo4tLTE zi)-~YQd1HBZZ%Dcqa4YdRIveuOz5C(v~i-GN94mIju5poaoM!J{ewrig|}$jde-6& z_?ZmqEZM7XO!$WI$*%8@ZO;2&B$boe&sFH17q&=Ayc?AeumXX*#Q}!V4hG`EV_*Jv z^U!YlI+^O$eQeMGs02P~03m$>gk0S5;BIQ~6`tMfEURF0!msC62+n?BA&=opUL~n+ znP~GwdA$uNV`74D+A+|m;doLi$3 zK6#1AsdZS=8+Cy2&Rt1s?KpPyRtnQKL^UCQ={hVD{MB7B4O;h_P6Kf$l~p+LFx{Lc zNFWnH0`bQ3L&Dw1l~BSK8G?fuJ#ZpA+O$`y9gDbgz2w7eGE!2F+U&Se;IHWuJRQcR zYN#XeVm***I`*QiQjm7Dpo?ei_i-}zA|f9w>m^$6+)JJatzYQN(@V9j4qsTn6Cs>r z1EKq#WXED{&|+MYM>zfC^Um&jGEv&sdUi|a=HJauiZN=NMNwzb+Izcwm~Vs#Pp57I z`T(ur9@|Y+i_3o;{W^m5n}4_vO8I>Q_d@cH_Nzz%mKC7b0o@e}-MCw+0YPo+N_|2U zrSM>oo-$)@-QLquvx&jM;{|kLYrAaR?!j@82oFiKxbXteRcaD z`;v&{;9^qJa45sLxcWjy#t;;z7T}=DY}IstM7e)(O+!Pseu_{U7yZOf_+(dUySM)c zb(eSe5Um+1)1h6GdL%bg(x5lJ=?4EG%(_qUPRQ%mKR_Y`=^Lg#k)zJes1$powLXv7 zg-GJJk5-nOT$_d1h8>1p!Y^m0N|US1>Tfn$(4G%b851^M%NEh=8Ej`@tZk&X=2o_w z)ZF~ltxRhGL>CZvJ#9Rr#f=QTx*V6$r-Z>`>@L|M%p2mM6YBLZvceN=o8O^bR&^us z_x@p?rwoD2@jeoT=*o(XhA812BmElJ%%uCUPds~?4U=vR=teFhRSv{0Ob?AU%k=eY zZB=j|x^r~!HWe@R6+@%T!b?dY^5s zW>K`+^9OV3VXCU}=}jRc-q$ug7iKOzYWP&SmDwiSgwm4Lmc#=uMyS){-H0!I!FVM_ z?%U-382eybSoI%Q8Y9~S;qF|><0{LfO8YiHnKWSIr=_KPb9FVKj~DAhkB>0d+wAm( z;WH2TjlHEPxY*a5n6Cx4Sqg9v+rk3(d2#;REZc{)GS9xHB3p6FI&b2r2Cf$w2~f5%RkscZeO|^_jeXyvuvqVRL>R0`dH=NM7 z)AgRc4~T8@8CrC4;W%R*M--)3U(HMlAN0rpO)>y#L|^TT{ppaV-8f85@~NWoG_^4-8q-mzKtU_@#&+;J^ldH~g}X z@RV{DnmxN_Y3u=eeu8mBuatADsNKB1;Ukuyc_YuLcgP<2+F8$iy#2RK_N4ZcJ8-bA zZ0cEFeRrC*zHM32=kZBDCg!jYuiO63hTZ}icXW~-82^uGud0S zuq9Hr?o_?qsnX^0+4X{*>QvajMiMZzU267s-{*)}z;ImVM|j!APJ*u!&- z#C>ZK+=bMk%&4ZU)s2%{-O$?OS39#IsC;5B_&G+bKezl#{Uw#bg?b}RTC*7FATTjK zlcclTTU`*B6kr_nX7;BEMM|eI=?#4wcV$rgi@Wf*7fei$%Qz!4f9d8QbNy={@pfU^ zzb87%^|~@J*m*hIwLoZ^H@Jg4GfSv|UW<_hj=wArp~3p$x06MC$DfRaf3w4cM zi)eFk(sJYju8JMf02pA;!av0q1YL$fF%I9w@}c!K>g7ofcD9?Cwq*!iPFx*obwSr) zyf!2MDX#4IG~^HfNuUPFzx)Ic(6!h+osmdAu6=^nS=p+e*W`iG1<>Om_3__bq`Mih z{Ur6?$GE*_h$Am9)s(YNWIM6#SQAGKNSwNe9jkW`Wst3<&R{z?C~Xu}8#D~0KV*67 z&*j4!`Eqn+u!%m$fX%p6#NgPF+B3lGmg;GpAp)dmrsI)dZkkGVp9Ku=Vfk`2JFGN2H0>C54 zajNhcz3#faaEJXoeQEJx$74NpOF`xR=0nmqj+4Z^`WjkZ5Qog@D84yX>)3$(1<-># zU~!I$pyJloG@TfgwsuZ)?k{SEfvN^IK6xEWc&LE{E`cVes} z&-s)`JmI+r4vQ9J1z_$0ezl+0I7y)Yv4(as70nXmys#2PI%DZ*kVRDJP(I}oSg7=( zT;dt+pSY-uX@XR^haiSE@L3}t0S>pSAYb_3C*>6~w)$C-bpVYeN#KUnX$ zl~1sUWAGg?`2A27?w2l|UW1|f11Fk`(AvH-;)Qp*nnz64^n?__%}vC;EkzXv#TwBQ zrUy@^^I|jd{^HaC_Su8t?d#Rl5$v?HrC9U8ktws9+z_lXp5y z_uu?tB>!6s3216Zo68rb$U=BwBwI4pOaLRt z$9&cTlNQbg?fFMrSigXWD(fgX*&tq?sJ;O#vcup4oq8S^S~lk!G*47fnR++>h19E$ zWc!#HawPij)c?ecg6;v|5VL-!8IP)j7kGfToJCrT)!{-8puGhD$);da2xRR{t(DbB-PiP7Dep>Nd%ZV?VZTJvdPmNFr`ZHSr;%Lpkda*Zjo zGUQh;Kt8iAv4UDZeAV~WjjmZ7T?~|d7HVEndyi4cQ8<@cht;^JV}SF~I@X@mXo`IVW7#$slEm zraM5lZvG@r@0$j*WFdccsEvmj@4ju}ee0V~M50#zh7WR@s0tbR_0bItH7$PTtL#(F zD@_yxDhyQmDFNo=>E z@?aEQe9v%TJBw=2V@JN>#jdWcaH#G|jO{`Z7FMS}KYv(v@u5h@h}glfJ-hvam&6=5 zl*H8UE_lU(AL5Nm>yr~jUEY`LUPk`SlR>pvZg;LMp?VHV9Jtis+l8q`t`RfRU}V zn;IxP4BbTp(={TU#HsRk=zosAqc;2_PukxLF_*36zUzvs_fmra4~kWg7Bl7#Zea8% zJ9VVoa>)f%Z(n}+LQ<3ycd3G?VJw{KO} z>_pE>Sc^nGFquK3T~&7ZY3yut0|gpPxHxIs{t$rOV9#fvS)k#fcKmr5NfB}Hj@6kL zHO}(m;rj1fOu6g4RaXH#-K4=_i1Om`wT)Slu|ivvC?nKog4ClKZgrQT%;|XAoG=+8 z=G6LPDuH11fsm}=wz~IG^yl^Q&c9vBJyxOjhBEjNCW%{=f}zNoDslYPCq~CHpX@<( zyWODp{4O7r%m-BEN=cm3XYf-+D#l6d*GlYk=#`7771uO{sJ81E+t?>|=U@tjj zBGrNK^C_W*!fK%OYa5fTqm>k_s{L+IYP$^#Jf zyo|cnRY8ynZ?WEDSJu(t`8>Liq1Z|8dNY~gnL+P7$#tw_XXZVYNb3yetjN~~sdYJo zdGhm66VK-qp4Merf(n~7sK=23T(;(Qk5ssgXa;V1bc)L4>+$qnFpxHNR-2d8PAsv& z8xd0Mmwq&1BJaMIg<>-9bF^Bye(G#CqkBB3JqD zp0?xr=>LltqQtZDyLK^q>@m9GJc>k1mUWS^%{P1p!8g*b&A+l>H^g4oe@pc5f#s#5H@I?Qz*F)Bb=nhG8($bOY$n54`ijLf>#g3-a<`g6`*3 z72(i|uteZ94g1F>ntWAt!*TgG>GHlH19l+;=}w@h#cnmpP%<&~Gi?8>(ihS9R8yAm z_qu~0Aek(>xpj3*F2BqJq1qcc@=1qp@@b^FxaP*Uw!&to3yQHjpu4wsmkeXLlOsfa zHpT}wRi~ab9F}}rfQy@)CZ4kmqLq7T|t%?D%IM`Ift@JxJp5@5Q=ONM0mx z(D|i5iVe4MZz=_GHKhK@T2T2;1lz|<7-oL6h0 zHj}H}?TgA+??PYdskWDo=eOPEN5@UYr2I^;oqA8ZupJFuRhi7M)5po}2ZQKTdX8@I z4-1L?#5PPOi?t)>@?wt_<~RGe>2=G(gO?rGRzT7g5Mm-%xx1FHxmtBI_DOGXCP%2> zKv7u2pL(f_I3+-1=1wp(jZOIP{X+^Eh{rvzN?%sy@dBX&U|JV)=u=^|lowQ8IGCyjqJ2 z8h~K}Gm$Gzg4gHinoTin?;#@Q0$@M`1AhWq7@aTeYL%&?(cX;*Ef9>FeR!-3)usyyk$xs8vH@B?R&kL8{21pRdt@sRCGWb$f!=s8lv$s-xh@3@C z4xm|dEAM)`_&+T$#|feFRY{bPVn&f`pS{ggXUN`Js{K9(qQ8W`+^9{b4d;E4_kcS< z$i+JW)jnFH(!cbC6)-j=&3<_6Ab+womV9Sz-NUOYF%v|xD;rz(>~P^oeMlw5hfpbb zraN$f+a8aZdHMN(BAXi8OJJ6BPxXZo#+2O6%}%%E`ZMQ=CCE|^7*FA(D`ADjX@b{J zmGMqwlnhYrU8nd6r>;X(Yhx-^`jJfkOg}nZUaM6GOdpnYkx0T`mxZcrorXmjb$!Y1 z64ZR!2!bppc>L(1fU6~zI&VHdEub>V4=CCE{HJi019HjRmS;T^n;)#y3ZI$AX1sul zeFyGua8Oflff)4w8U+X#{g+<(dWbWnzQS|ex-a1JKNm9BWTuGz?a1qY^TP!PivoUL zlpjpL0VMoaRS}&g-V3xT=Wh?$5BXGT2FV!5qV?-?!C3(u+ta-Bj5u^bxxKOU+Pb>1 zsD%lLm;#ocgd`Rm-!m}Nyv^X3dwXT>JV9>f0!_V zFXKDiduNb7pjrWRgfI7UIGU9wH5g~IC)$rTKR!tATibB4gEwhGO>`J+6yb|4A~G>Q za%*d-B?ANTQR9;?oGh;Ep^B9BK7iE85&PQXf!rqygd24OksNGQr9ZI4bkBW^#<5jy z@blR#V5Vuwf+#I}s&6_98kl@Z0`Yxv*&j`EB_Pm_EyX7sg-7rRK!8DfJPW}(-2S?>=xp!P%1N_2IQ3!8CB~u zw10Xl9_|`EYh9^O-zUUfXm1U?Gf%bR2N;ACjvYQ(h+_iQ06^O4tbjAn*wIB4WC5i) zWskAvh37l$=k>ogZc09~A}?Xq3qPoS#)c|H=lnRJfsjw}tQcz@9{`7tRIr1{-cQ4r#fmaX>WY zN-i%d0*syVJ|^pry0Ou+4RK!3sB6K&1nr9&wkAvyX_t|)%X{s&t#d2uoz7&0aU`j> z5}YS&8p5B|wUvH~(!Y0|)!fDj&zDa>`z)9X-RU!W@OW^|R3cKk<}=B$_a51)5~o}o z6(GBXuTrCQch4wNH);9LR|;ZS>WpP#^exJHV9495n68?_(azR|*UOjaZ z@4%ANEYCW7scO&ppHy{rx3{`bn{J#IM+MsZd}NewK$FUpvRyo4Baa?(l2|x8^^LB0 zP@&c-EPUEaeEtyVvgU9`kUQ=fm6;mL)Cs>r<K9E~?dSmR zeq99#ox;9;=eJ9cQPaF>%|AYsm9-{DhF$7bR7C4|7+H%IpX87>EEBCwS^_yeLiKM2 zAg{g1ol>&3ZugkE|Ku@sV|mI^K$vn>PLkTCIPNBc&L8O8Tjs(?{I17;Irw^RR&ae8 z=eLOE@mD*sO$_PAOAeRx3-SY@0pTOx69*2iW{Q8SA&GD4pyoKV0a1ZsvLP`Uv&ht*ip_`HVaIew?V&-|Prrzt5I07$IbK)mLQStDJ>r?eaCV?BFf zPq-Y{(+C^sj92o^Kz{9fOXd}Fiz3$=OhqV{b-DLw{=dHf{8fbS$p?EQ6F)ei%qCM_ z(q@n%?AwUz<1La9)}LtyBK;0tZ^Yu+h+^_;)xw0$X)BqzegeRSV5SB=qP3V5P%Toe z>xY=#@=Zn-JbD&)aREAykW8)W@NDHZ_x~RNVL+b0muMbJWPSr|^MJ%HdF64z>7y3z zFa;;csLhfjx9BGAGKe-uYm3cW2~w!jHP3uM$4cOBD_^HM-7k50j`iZv6efrQz|+6m0eY{8s@ zhMz*EiIMN~um3y}z@iJ~Yp0lH%>}Y7fkX<;%<9h#dcA{?!ZFrqJvc&AgI(+AS5hY{ za`afT>^2WhoS>SV;a{WdP6vv@v41gC_8nt~SBtWHdSz!Pib>>;e>Mp?zDkprjfO?Ig&KO6O%rykyZgbQcidF`k|aWTG5aCQ5I?85gHQHW6ux|6S*RVC9HZ1v zx6E%}eEB$H@^i8Yqsi?iyKPPS!^M*;d-oC|IdC**xA;o#5cs0WUjc3YFzQTwC+IEs zqG7SyP*&^L>du}Ms5%q2AgiYJ4K2Vv^>kNP;R_Ewn3k1Xvyw=+8w|)`SjtvFyX^VR zvA~(~d!KGOpDq2J66LEmHKkodEP-!#0E8&zQ>T=aj5zdDS?;GOz6f&LAN6-1T$$Ou zaYI^F*P@inv@$7hjPiA;C>4<;1x_XcJG5BQEjrlSJ9e9Sw#Y3%j?*B4jV&!bNHy=J zriqHlmKvz6Jmz^B8G^G_A9+9W>L`+oX-`EE*bnQLp{mKp5E;HAts7fW2IX#=8+v$g zrFYN#0+Mtvzqsy4gNzs5^jr^gbFAR&aRsDp~--i zfs^zBq*XtKcuD}!xd_sT|71wkU+U}MD7odd=llkfs&Nq@p4C)jQ{0C4j(IoVKIKQZ zt<7qEP055T?98aT45oX{3iI+yKl`OpT$ca6Il?;)$Jp&rcX2&E46J_ zG?;*UciFPxo9K2IBvC=PS7jtHZNh|rQg`8Wq)FmoqJiFY=9n##W4Tz2qrMh@Mzn>W z8UoFP%O+PLFmQr@_%z5S-^Cf0fKx!#V;kGtZfQI;o!Ey#3N6>A`x{!YSO_NFYb`7;PTu@Wfhc<`tvd%TmafYW$ z(u&@WjOleYN}iB(=5=J_=_S;?9O=}HpllK{j`*#9<=nv6;zD?at$;zAJjn7bdoe?ZHjoEFa>3WWrP zYw04$^N4@=Obg?mp#Qhc?chYEZRWWPN;k_yy9G7`)eJ3BSP|J+i%gySK;FBsBYr)K z_bx*aEr`-HVXUnp(m{Wo)3VQRs444QF~(1|xhWPv)%ObwBFzA4`ne!SX;N(V5X#~7 z{}bGg{1lIqKu4C88(Q`Tl#(kp8E>5$@;`x7MkDkMt^K7;R(^+P>7wCaG?L_!7V${MmXu3cpy5d?X7Xh9;Eo&8 zJNnV7;!MgVacMY|*wvG$wgll@hGG3cN6|l@OZ+w9Wxo}O0OCNqX%ES+`DjT=X6%3* zi@y#8_Js!rOOrBt2vwq|du{$%sjzr6NyjcQIGf)TJ@CUj=FfLfM95J=m6#T;pyE@l zs0dz3A+dvYuKW0Q$M%tbe`+Lf!glTNZT7&rb?Wp*m;4dDBniVX7r>D}Q6_SC5l&^~DS*UuQ)=hMV>89c%vlcb^0fZ)u1X%|34f=wvKK*J32(KQ<`lC232u_lp9# znR4I4c!@}nA!gaXfMav>B~=C8nEmih%dwXIGtQqc*|s9uCW_MgvKB)H zC^fJK=d|D-jN&tMI0;Hl&rV*%?q0bVL{mUxu$Cf4(~(51QNlg~c0di4o`q>7;Koki z_ajIN=ei3&@!8UJQ{VU~r5|!xR*_2(eh1jm^<*fzUu5phSuM2(iZ|t)>l{b@R(UvLrLV=q0csi8s^z;`MM8g25nWG)1M=`1)KVwR@&Ic18n zreSRhxjLqwLFGGkZkVV(`(O^7>aVEpv3Fm+mj_vKJ-1OoYUwc)6s!lf$L#{j+GgaWT$r8=*@Lp z5Wep2czd5#oifdP!Cp2F%kH0H&``HnJL+$9{CqiPmI5L1NuMZPtq2No^Nj(MNP~TS z4afM1Pn{U@EuGC0s0Qg>{gG!$aDqtAR(Z^L`NdVmhb=Ap6Hy_b1-0a@$QZjLT;+fC z;z?zV3$V_QId|kO$CeLBHuIq36V_@JS5IR&w34%F5iX zhK7vH?QgS4;IWsszPuW#`K!R}M&-f5v9cf@#cc5^($C=Q-AZLUt6E)MSuwk=Sgck; ziy~=kwT6}T z+RpeA@UVsj(Dm!4c0iZxXkxGgrQM06(DTPXnFJh|mERSfYilZ!)V+zp{=xVK)un9^ z!|3Afh{Souu#I*U%HMQPZAU425+=k^aL%<(AFImB)D>gm56<7uw!@j@+P!_=P{i{M z_`sVzeeJ(EzDO*{c4Wc|r!XcOuTL&KcrV2A+os+x0AX}_NLGH3v$PVAC@dBd##iLn zzSz>GO994n934jv2XBlWt#I%3=}xiN(Z7#;pMO0y5;$h%d%I0i-OO@=YQltOEz_DZ zrLb-krGR_L;IH}@&iFYBA+L!tw^CWXQg*XjvsrrnUj7E6!P5CoiP#azCa6~>Un)?a z4~lnmYxQ==nZN$sB>_b!DAT$9eeRMTGF0u+P&n}6rY3R%{`;t+`2X4a68NgBYyCZ( z>CQLv96|_Fmb+m(ReYU>ZTAu^8?XxP1 zqJT1~K?aEe0YVZo4>!5fna}^*7ZL+f+kZ*lgOKx6?#;dDoV)kh`>eh8THpHCNfqyj z7W2Wn=Mqr738Bn{G(;us(_Qh3c5dC@6VjsEjV^O(9$}A9tFC_Ab0G|#`bX~BQ!y#$ zfasHW_QHta1-bNWC+Xd)THZe3?ayI(QjS?UbIGRmcpe?bZSl>;7ma&Yu=Jn*(9oVY zC?E0LW5>u!U__N~P>J?}x$F&`VL z-A;-~e^u3Aw{8bpEEMnbWy`1bgRmT$mbMV$t66gCoZUVBOI}4q3-SHnxu^SUKj^7| zNZ9N1lwaPT>+gVBdQ!_&1j_~!QpB?d41OH|69o|EEZ5Jkj~Ukfc*^cU^550nx^eSU zAN%BZ^gR=RKjOH>Dcks*VI4Gfte_uyn7>$|XZ(yL-2-5%2pH@cn`h|~?kuBEM z9s4A3=XYyN{tl?aemL#?pPqfsBS*c<%8Cp=p zvzN5e_aNI!5oF1d{_qAd~m9Ll#4z#Il>lJX{w)Y!Iw#YkY$n~(GG~BzWJaW+3UDpuxNmev0 zO##p9Bw&4{mR-As&Rf2GxnVob4w<=!EX&+$a<7t%r5XQyBRmSOk`zbHc&!M1uC2A< z&ENcZ)qudW3S}dXlXdZ6cmEuxYv2=t`v9clGWOn8CXh2~Yj1(feEyo*PLq+5T8s;`rVz6{AUZy zYW#Q9pZ^{vg3$i&CRCy@q%m;=~z6H{ApjfMhLO`Dx29J9b-JJQIlRK%JpIcDGxAime6F{P%)@X5Nwh ziU(%r##kq;V8)c79c0tEzRwU-&|Oyti+mfm&T&zSfH*COGTdDYLT5_T#$l!E3LXPyjKI&UKxg1=Qp>tK6QG zX8c-dTlQZ6qWHHGra4t&$|sP+yH<+hOYS+kFG*i~no1gNB<0t&T>^U(p5-^+>io3JGR zCLr_sBoe&0-$#ED)7a1aVdbvb*~LF7&MvqMSlAB%dE+G;>*&A$MX?9k4c&YVQfzN# z)QfXw9I2_S9553sDfZ`Won0A4bEe1!)-H8T#j|X(D4nnuaaA3!_%u|r^RlQzOVEF9 zz_trl4Rfc6e_jc#^In?_(lKKBzu}K?>ak@RAg+-aAmGDqJNtyGMYm!@T`u5V| zCAj99o$Y>Lg}+ICY`J)T&lDM*?(H0L%NGMovS!U{Pz_&;?K60+t-bTd2M@Zd=gku^ zAg?SEL1wWXP#M2RQCV8@*hi2|(St&N6G5CEy7SIeP_TD+1B`adjGvPmJ8_ z`)tcCv?<@DaLMgW4jc+_HQ>XRAntQo?>OeO6-xK=PyPF{B~#BilG{_ix4NWy=fivL zP9D_zkkTA*RONG$OC&v=`nAk`DIOp<8VaBKiIQnKt+Er#W71}Y1`lT8Stj7#6o+to zfVM`W%|MBqCPmck=|G$TKbX;Il zBa9UpJPZPz#~sgk0UPdZ*m57Bgxi$3Dbp$P zszEvZ%AX#1H63GbfY3?O6KdH_k#`~Rp@MruU=e!NFb-GJ0e&|pDfy7=cL>nxw zzhbb|_6x=rHKoh5_sz@=?LRN~Ef)cvD;UMrWq#s)KUGw*R1oO=%jDcvk-(8wIPwGe zVNGGpK~?p90qH{1@j?kZ(Lo(@zR#fFM7H3D812}D;N=jMFS_u;88z2@YQj}lojGWc z(`igc-0~Fxko(?zljjsoR-gIyJ$b6?Oi!e(3uTFJ+tNZFUr)-eJ&)+`F=M$U{XXK75XZ-kBZ#@2DeoiZlNuypV2POs#axTh;*0+E+e<$Kxef*A#CK!IUzfm$DPN&FcN2HGEXB?K9xks7I3P^nT1j!~#B?JeuUB9q z9kYOwV!MR*f4wJTXN+1203RMfO+U(1IzFZATNV{Wk|ZlM)Za29!T~sHMYhvBq4CsEGzi&en|hcV?GXKEI}+_sJDi*#iVt@WXqXAI?$2(6=ym;HI?*8_8GZ={Uw?GZ+t#t zZihz!b6GL(ysDqBS)*{`8JffgY{UK^;%Xb1Z8p0>&y`sY2A)u{9SXYox(V&cgG$>8m-VG&k z=z~bz3A|Hl#n#wNr+&(1^}*#;#1!eWCS$V!Wq2T*9ee^1$Uno0{GcK$Q_Pl@5i>yE zsZIae-wC7T*l+TwOD>wD29;IRqcaeA*#>fqAHsu<3}g9@!l?GaK}g!aOfG9ZLi;x6 zM{_s!C7ttJ?q2Vg`S0}}`bL5$Kc5{6d<@aHi@*naZF_(07Erm!0ZqF5zAalaDcWMJ z*6!*Eh``?ec7M;r-y0mDQ*L|2Fehd)?Yt?|7w05w^Hs<8+Q5xhXt?R+CTG`igDrGb zR`b;Uh76vvuC>*QPN!xB9B95QGEv#<3%P zU0uwuZ7?$05zsZ7MRw_^v)F_ZT>8;ZR=jy6p0mB9_Z-ucz5&I%5GmTb!KwXKlN$en za?UR@=9Sp4dr=xlY`Bhb!ZQzW80t8pd97F-01%&fqtGDSHDoejc8fmVi z1edh6^?laj+IJj}F5}FZG9N(szt)+t??;~m1VTntr2E0mdka#|KMUTEpLHIpkHdF) zQl%*@zKqoe?JTYp5)rS=^MgDYaQ3w)Y$k}Gv&@Wp}x@B8Bi4e z=aP#1bHbFnK&r9`+^lbMqTb$F7~OnsHrx!HHlqN-E4E|*Hm&qq!Zjh3_OZ^opO{r% zZ(HQMmaV_&(|8$}(mGOl@)hV;4+?vpO38yMLbsC28sGiv8?mFMbkD(qK2#66IAK{! zNzGeaEoYnn|Cbs(VytGX!4j1xlMibuj}5e^|6~VArNUiXie#nt^|tn{1f~+!tTK@j z6gtd2IBa9iU(Ad1NJ&vnNP_#|l=2tPgul7|jtA9bSa{N9vBb|f9L;r-*I&5%h( zasMyLX6$E96AA1FCqr8|m-7mU=R-{4kx;1bj+4vgT`#vQc1L{riciL zB6%6bBmMsH$B=2U1OX&e)^VS9q~HTHBBbv$>E?(vLu~u~ljmL~%k)bC#SK)-PDFAd{#hWSTa-^3*`GCo4CQXU3UiN=ipTk=Bv%B@b)O`o^9qapv7Bd-4I@ zHftp=9ZnGX-R^wU{WvGyKG!hgWI31tUUT--fOE@+dJDc(Gju2y0Xu?7dZHFfCT+W z;%pV%4l_`_c(u*#AEiz6tB&gakj9M19)9)N7IFTuHjllWWfG80a(!8r_bsA(Ur(mo zE3514PwG?D^2^FS$^K89@!tg$={`-1&k+`#5ZB?&n`4-q57{R961LA@>g$jH=}`M# z;m;W2Ko~7z6=(#WcAf+e_SbCUHX=c#&0&1|pyCz%+=8OFwkPF;vclhVZGNxp=XVEW z>*rmq{Wov!NaPQ*g2}#~#sfiz+yy*za|$kpK0EnH$TnvBWa;CU&34F~H0hrCnIti< z@oeYkIv?0Rrg;DQY#K4;dj|&GudsfZyG-_I7!;89Rq(ahL^|YxmI}eMaPhfIr}WzW zAAV?G8^H27lF1{2gcaVq-QcMDMZ!pZHJlaxL5Ns?ykK1J_S)?1mWyTPUoZLW! zxMki|IsIZEbHB?y`hK|fLg2V~&ZFcdl}i^h&%N!1L*P3;b_h7y1(kXGym-r6r2GH0 zzt<6E=ilp-yjOxA{X=VC{NYo`X=vpfA;I~~)K0qy2J1yV01>7RsS2_8| z3gy+{KWNLu6*vpo4(k@VaGq}7ICWSZ!BHbTbYy*3S1_LhCU%nK{@T2w_2G7ObxpKA z?S9VbJkWH4V9?#+SnS4jGud9`Q!YUvgRfIbSsB~8W$hrQcTgsG)wg6HI$WB(Xll^{$;uAuwt;lO%d6LvlPm>OV6L_b~HJNZ(4sk;R9tO|X*l{*VAiJtc z#o~=jHqvQ2B8lqg0n(hcAZ1z)D;5Q_>92O}-`OXW^5FOQT<0M!$^v;gg@rbhuABB) zlbjEQ#)*ob7dWI|nHnqjqi$rtAyZ2zLNFD##g7^MJ0l+~jX{+x?Er z`CMw5-;@dcID{x!9_=$h1TZMi6JKBa#g7hQ7yFpz(RE=>+M5?3orY=Ls+&$Rw$n{A zBId5`T^ruAMHP7cqd$A%UC!>mM^yPD0oYN9zDt#P+Hxi3i5lYc>%0?g2Gel8W|z8o zw&HyX2FYCo6DH4jr{swVNc$Yvx94?4r3V0-V1UImAqtxTe4gDyc6Rl?yR8lM4YtN9 z*Mp&uwz`G*1V^u(t>*3j_htZyfe=*1UG}Q)BxdiLcmR>vu!CUr1kW@_a+?2 zdxObzcFK@;aD)7!JQ9fs-zT7uK2=egp^eU(fG!|*AQ7Ud!RdB2b$9m}vhin>{rV<| zizb8Tc)8<}R>iV5rmKl35F|q0;#nQ8K!rlT1cnpfXlwkc`mKPgx^OR+?)Kx>*01V#` zg}wguyu5=#!H;Bn$L@7{OQ1WID(*1IW(3fB9M^jh?t(aK;>2N^B^;TakM!1wx<|hs zu&n_(sHDtz+M%lQNfh>-A5_WtY zI4sJ`pB2Ze?fw76z$yq9xRm8hpS^_D)rN@_Duk zjx*al#3kjq$A9*{_TiSa5ZG!gc)q$i)ygXSI(E5niAd`unN>-|-qWQs3mEO+Woz=E zbX|Y4i6vjY8ScijLePJulOwKt#VVO1*|o^4pR363qXS#kAIf>xvX!<+%CK~9b7gmb zC?IznZf-VE=ucQCnFpaow-@&&Q{%we`urcWvoln92h!;>z>97$K!XPIz~Ai53GE+K zzJFyzN7>hhb$vYa=|-eUWK-g#iAOCs2y9rkFwndZl&|6vw;lUw8)h%^Fi;MC7(}M=U@Xtr{`?@|7arFMrOOo_K7&hAhzkk#=^!f% zn7`{n_2hs=zvVbC^D+KtnmF&nb9`UAE^iBXvd(%VYDC=*VgQy@R2V+VOluOTOeB_7 zI_*6X>XV*_-+enA^Rt-Amh~P7h#}1TX=uV?}Chobw18bb)w=G@_k~}D(Bw1Hvcgf*xLe3qj3z7KHQHm%H8mre-DdT ziR3w7l2z$nAWv^gQDhh5HkX5W^D_v<&A=1)29)P7ZI`SC_3J`70B_l}cQ1-vy!}{F znbl@m-=AK`v2pL_OySdLu@qLv@^#OX%v(lC>xMZq}@8JxCo`;11I-`gc~@=s0* zRzLZvf7_>c2)t*&fg&TJGoTYIP#rdm_3IC$D`wRqbL}-*=9ZkLP6xeYhoF}fcNGuC z=0sv!w(c!svNDO5R5q>MwaY6g8~*_lcoy5T6V%Zu37&M!|F(vWopokPK$RlE?z+@= zzoau~FNaHtQMiGbEyD_v6wUKXR8|bn3L1_VX0~05>e!!vL(>wFn(DvO>kB`2>-d=G zT@&;wHm+~#Gu85}x&@=WT$NCI)hkx?l#=knyfEiAi90hq&R_fEUH2FZK5|2i5)9l7 ze{eY-z5jMh=PqytAL(&TU!la`;Dq0zgbOa*)J>i}FPFqm6z|p3^F_(>j!k`fIn@mT z9eo7^JH(Mi0+IVC!JL!IcJngxMX+}5ZfeR}y`{x> zqNU|1Y&u9oD3_#M#%ZONNCXGx^k|f)=-c@pzp1FIxO@SR(A*t|>RV8j2PD?T^y`;e$1_#U6nK^LnARF4rBYR|RZ+H7Pk&q?Y!EJA$W1EZhtkUz=tXTuw?Cd&DRr%6g zu~>9{j6}C0C9&fCrBlR!^czhvqzm_?U8}-ZI4J`u3&!-9JjxHro*P$Gza$EkKx@-g zIG-1c!A!zx_nuMGGHmHXK>L}zzzlX}kquxR~gL22p9 zI2b%wac*^WwONXa4CkS^vEDj=ECKoi2H zWX-g0z(q06rrvvy;Z`XNR;&w*>LEi1LZ49Z%zW~ z4tR*X2c<$~zX+}gMY*{-Rm@d#D`Gc=kP~&8bFea<_Ey)EAx5;}auQi_|6Emepj*** zS)REK%mV`;X@5qlnUopFJ)y4CE?xmD!K0S;@PQtCKYArFuednfmgww7 zAXgQzr{Uv|%r*J)PkpBH2_R)40WRi{*iy&!L>>HIPYnjrV#G?L%9{oDyl9AokM7bC zWfF$Mfa7j=UHS|X4)&O~v1yIdBZ9!A_7Dq;?8-YW$Lw%8ogCDPCZn?%JpkqFp*lqu z{($b4O(9>gQ+T?ut58*~FQU}fdqEZ3p46SMN{Y9MPncFxmeqSLT!`7j$7Q(it^-9S zHQ5Wqvp;93%Fe3*vKIdJ826TN+4Et8Y$$QMYe{YGnDQMixWC~&;J5ah0K5C)yXglU zXm?g+R%UXiZKG8pT<65^TfBaKW^kj7ha{W4A**yB0%?oWY2!xIrgItX%jDp`y@Q%8t<@+$&}#RMdaLAbkoj44im;HfL;0UA%`W_cl~;7j zn@!0g2q5F?$|;|cgW%A2riXH`+20mBZ_%Zf&idyY{(TxFzPzV@`qLpFVs?-x9h(op z%8GRLHoAuo$D&!yy&sMQi?5EVzGYOV*r>6Nh~Gc{a#B3%sY{9Zb|L+WqnJ-;Xesq%9yonJ ztMJ$zFCLDK9~_qxC+Ldmyu^wLMX#+W4fl!Pm|8M93jF>gk4Ql(JKGPw+?dH)Jn_2G z{`aX`zQne;pK(@+{X?I{$mCR~kt@cq(C>SbGdtC#rz;vODl&mRpTz1h(dQdF6s|f+|s1Qa#NP6I-0lTiJ$!BZ4Um?K0s%C|I(_r zRN~(Cw&Pw;9QRXhST2N2GRhZ~*fhY*&Pkc(7cEWxSCXUpV1;Cz4n!CP;w7(6f5 z*@-pZEX8KInfYdTwR}a|UJIGJCFT~e8W$5#h018^BV`*G|t-gM>Ey&l;woJHFR=xmIANFjo#gS;t z)Ae=ji6h+*u(!7$*rS{76HZ(5)x#AP8Q)uvr?;a}>MH@t*Adrh5BdW6lv{;q*DVb; z9EG0@8$6FWG+`4w5H6MDeSv`!!QI2o9eXBp&T|;p-9LJ6A~U~4 zcL!{EAwAemgaiR4&Bk8241uC42+%D}Snj#&@t}+ISg_0|b3p%5%c5cb8WL2~ogigj zE$itumh0?*_3YPOQ)FkX3C8zk=Bl`EG#^(@o0c?ngw9meG}3wSdlTV5XTonD?>({x zDc%18w;uq$v?48YFuh+5r`gn=$4DxHgkCHfDBQ39|!K1(t;wX%K* zY45U&1~kGJ%8KI{HqC~nkM0YFGUnN6m#^6VrxOF4a%}IppKMz_AQZ1CJ@i0FM<66+ zX-GbPKjLmv9Gi3@G4ShDi#`r`*nse>hpOjXphz4w;k@aVOSX76xxLrUer?)<_e4r_ zVjmX4xf6rq>$|W(I`+kWDVgsB^O0YndeU-Ut0~GsVQuM|OpL(q8VkzM2|@>CTelqRC}#Q@|1@XdhR6 z{4-Hm@!dIjKL*SPwW;&%fE9U6BxVS)OjvZ$Y=Erq#FkT*y=vGqo9^$8!tWsYrAUmTz8JUps+<;}D zrw}?$a%l?;GPkUzhOJw9TbUr>qN7v^pRz*p6@>;=SQ^}Q6u;^_Z(3<-CKKW!UgtXYZ|iV z6fs-r!c%vgWjuJlvDeWn0kgA%17@fobFHm2nH*BOgFSZL{%+3P`}?wey~~#`H%gHl zv1fk=f;vtd+vOlB7O^=vA_KC4>J<{wlQUMWXIj+=2485n)_tKc5EbJeOLEC1z&?J3lA+l;L3Wzdc9b(yCjv+alWK>CsDQZmGQ z!DLvcoX>`kn$nj34J|Kg8ww|w!qotOLI4Y29>SNG6P0{u!LD5yn5mGV&uov$F4e$P zco~9ic;xMUwa46%%U0f2>QcVaB+g4_+Wv8FC}%^*`t>wrB(KjeufFy7*L-TiiI((e z7?)F4zU$Vkp@!`q(9`xGsBLXj!r=^5NAk(V7I9Z5O#bD<(#X{>9_}s@mTd)~C&Xk( z2_=V_?L9&)>$i$#Z?#<~l8Px|2sJYy4s|A@Ch-w&yA4#aXsGU3kYzMTeXI|ElLY(| z3Uv5%`!Gqz$spE0@uFZ(&LP>P_ib6VDw8QGzOSzEsT@@@QJ6JR;2c$?slJ2;Hvd=> zyj!G}kly$}y-y;)wLIk^lv8<#dGcn-v!8QR^+?z1)uUed2I?F0Ci0GJ&iU6Hs{nL> zkqZ)s+FCvAut)GL|G;#-m~GSMw2^*v;e>*&M~?z_zjm-qt2ThkFOu~55-{L$6k+0;K9|ab(A;Xfl0A2$kkc(z~v5?O$8zP zgo0q_U?5glM}3BLE;dPd%p?1D>kXM$gC(=^2n;dVfyIU63T(IM=7k%w5r2?aV7V;W z*QQ+d0W)P!sjOUa%Fo2`HlB{}m8?Pqe)NwRDLyHD(H2$EJ^pk}gF2%;(x+3(7g|MvjFkam5_y-Aje?dku3 z#@k1FKe?f?vAv>D`%s~*L>$5nd8D^NH?*Av6YVG!M5HK> zDc@5Vq%-l-J65bPU++ph#$>jPINqJKsPwLF4ZSZmne93fd4}9jm@gC=ZhY1ymrOAX z@@ZsYf#=(PIZ;)WaaG8ahbdfLNbpG^R1Y)k=v+!VR)Jp<7+|;8Axg^eOy5CYJ+1kx zj1Xs{G~a5om5c>Ls%!^5Z|9V}$#R;x|2A$)XdNW;RBcvPe;w(2%!|9t!7PTV7Er^( z68;=U9%7cegfQ9(Y5Ppi879jM(Sk<5vUC<@+lt zMmKxj?wC;lH_W_5I`x<@pnU+AXQ5}?&)#>#n(h@Vrg_CapAm~_EwfZLA+p|vPmorh z(hK0oUdM1XR0}C0j9m%!yaXA7a1YThce|;ziA1}kTKt24E`QFY(!T(Zaxs$wi_)Q7 z_vyX68*7T;kvw)B?oXDXvho#<SNN={S4k*i|LN##d}}ffB|0N zh?LM|Li%Sw&*u9Tsa0m~vsoqCb+DkVhIo1b_#NIuJSmsRZheFWe_s$J!uK@tjUDmc zkKJ&jC9p4+oMk)SU3k#n1=1xQK+*w_`le827rRpUtB9w7Hzb6~9JQIZS)%mwQzQNz zXotJ8ErD&VHOr^@{CXm>b5VKjYY*>jE-VfDg0>}@DoJZ;)t8o0Op+V!-e=3Q>S14v zYU7-2?zy8L?PFys?@PPVhZT)oXDWfKfu~xTO1tmZxfiZpeY}tJ3=`Gg)LfUEw!)=66eaab zs`PL&os>Fl>sHm$uQ;crqvrngjar8AN^B8MatQ90bF+OduRrtnK^Si@V;_;^E=x6; z^*W)UOHrHY6L9^MV0!^*ud^ST{2?$C-i_Vxn=a$GRLv_c!i_WcKDRX73n<~Y!2$dy zBw%52^{&Fe?NI&Rcl))=q^qS>R60J%JQ(}v?2!Pk@6$^h!<+`V(@dnR zm7AuMErFN?i53Te2Yx-Mxx3cw4B96=c~SG{qhpuk%t;O5nv&ARJc+WKO2|%|$K9{rh zP51t!<$@dk?R>?SSdX5#Q&ssd!QTwJS$mu8NGZa-QXmHt`+5!>5e#Z);$v{i%J-$F z&Ppcx-p4uB?PPj)*7&?_0)8Td@p8w&)u1M;CbIf~7whXIKJ8Mhk|ny|y?YrP`)iR+ zqq*97ro)!FmYKlH_`GTL?vbV3xw@Ht{jy0#nE>3GDA=(!|6?Qo{s4u=;~}KG2EbD$ zIbqfHVzFdaVPU)i=!X-3bzM{Mg{sC6m;(be(UI!B;=IZ6jUD~t^@Noz+iId$)+hKV zNqC%239--%FWmPZ2L=_(iT4^!f7b)&TMc}!=+k2jovUn04yYdNSLXGhXjeWgqFEAk zy16Yqo|K&KlFq|jNIF3_> zlZI4)0N+QcW~>-6NYLl;FC)VaYp$=|ySEjiBOgc{4?U0frD`8t%uH%G3 zo^HVzGvt?bTnbh*HyDwDI?MCfAgczuH4-e2F|%G&Pyl`n4}~?Itte|!hZXNaMaj8Q zQR#c4QGa;V2{4y4Nw8yW{l`c^EbLQ4p&q0eH_gb89GtCbjdKbMyDCnicts0P{(RfI z{7B;k;FcANSA3>XDI5+-cXYa*}48L zzqacB-e zoX!D+7Re-&C|HQh0?sao)QJt4Fvy zi}w3X-%F{K^ipe|RVp*dfHL0En&>$IQwhv*gj%V%RggP=rorfB5Mk^lwFHLiQ5VFZ ztojRseU@!Mi6jahPnjQBAB#oK%@1_PQmJ3U==v3sGq&q#{fCL9srj?D+X?aC8&=d` z=5+L|6h%D;?;~EZ0(!Pdo`J9UAuw$1MB4K0R*vjPYQV*)1$hqwc)6-VmO+6{P*@-n zM;dS73Ry67iHa9q(wGqBq3gnoUXKKUS}ivhRJOzKIQrfz%!+WT zOmkgxR`{HAGD$1K_j=i+Q1{B3qHfvZFN4bLhgXcx?~737yr`-)ckAw)*6x8uWT(xv zOv9&1S+;qgbN=#w{cP262C`Z_*PhLP4%peVa9?~hV6atN+xsp;Wn4vf>94@2^XUO2 z@8wWfy;PFuwU+DU!e4(_rs@jE=)b=1C@KDs(3e+b4|K$uU*(qcSgYIrtk1M{1m&PvZ%$eHo>SqsVYL#$*ykTWKK(IQ!S?R{*@fpX&pxLx z_@+nXjrhD7iTV{%L1@Y57J`DNM~{6yO+9x1z@HJ7PG{3BDpC?3MHZZRa0bsAdp$cP zATYXD+;U6VdIX4s@)YmuTUt;xfl%@HVU)0fuFR;*Euat3UGEm`c+d9~TAr1^hfRC{ z06+jqL_t(l=?^P;*AdtLwd{KLO)H;#<)=@m6J|Kp1lQppdZGi^y`{|F=2LziPWO@G zn%(cYMjr}U9@+j%EaUSX-sX?aDeQ7zeQpodH46dBS0UYIr2XzJD2f(`WpZ9b^Q~)2 z4}2s^rO63mE>zUqc_#MtYJ5(q^i`Ld^-A1u#ZHn*hmLeG-kmC~Xso@zw3N9Z*O@@->jm621349Ag!U-M z(vwN1JQ?G^!N2hT+Tk_>j$n*P| zLr~#DCOLn6^UW+w=qhY6n!rHyd(7=%R3e$;nZt#Z>`gD=Oq;&2m&d#<>mq0KN~dQ^8s6`I^5fv*+p?pEyqJ0 zlw7?{rtzs*vOfd(S+iqVw_3Wh3US^t>SIGyFoc!nmq=KFUyRxD-y6*P1+S~i&?b&% z$sX$NOC{0<^_Z(x$fZME8N%o4_Z41gzTUa?`g`vk^qITwLG8sWClqvQw!Rq%leOD9b7RVWVW<}&#STt92`{Nu7;ZS7g|Vnci0mX>r$Wqbd} zr(AUD>_G**6jrcl^lkJU*VZq#+_P0w;VNx(1Pd4fgjIi8?Qxdi z-_z#P|C&I;m3Ye59acTFV5q;HJsnduO}m1rWaZ_TT#_$bJIDG0`pJ65RZRQo@3%Iz zR~7bJcKi{~ae7qQ_Yi2jOAGvRAQTSP#cg+z>+zgb$7(hTWXiel+jin_DBGC;3m0q5 z>i=F|UKem-Phv-0YT4RFR?=z)wb?J`owj1d zkTqNjX2gX>+TjIA&=@Z7_4QrB3`A}Cho|xTQ&zrWdjfhn_H>#q)uDcsHua?)BOST3yH3 zeAM^D-=qdIdJ z+i7F+;`MLAr$(!2>#Orpo}PG)$==5MR;?OJTyhm-FD2%WKnIY3LcILfFRahRT-G&p zj;rMPGYvEH{+qvEmVFg)WFMP8p~X&+`oy|Nb~&Du2B*X3bRs?nG@eDWAN3rPAlx_kYXm$Sw$%lOG@#Gi*`|_Z&ICIhWbO^5ohD#+dpzp>6DsUo0sqGl zJpF6oes~K-b;95X*MxAH1ap-ItZLf$6TUue|2$>oo9US%w<4e33i*AZn0^#^J%4MH)cLD0KcH8@XsRpK3Ddn`_#_uLxGvna2TB6d?pkJV5`o5 zark~~Yezp}{~b`CmjQqKLUHzy!G%aziz&O@wQM(nWQM}H;ap1!rj=ouQAd)eMHDiJ zNfvL2JEg*4mXY2F3d$!C%dvx+v}K^f=n&tZFh#vo!g>mI=^vE1`|(sq z;&xS%uK`iMjo)j^D?O?+M36n`(`rybxf=d>AC*0DYmpe@MLi-R<1Zbhhr6cDD6W_Q z*HUdz)}{2!Iqp*!+Qd9 zTY0YEXivK)$^@ZMvAa%7yXzLsojL?xT(f^a&GzT|VE%UJYDy=S6>aNN|I>9g%ro)q z<u2wo0B`u%q?h_IwsJ4JJ(mxr&-}Y4quq z1A7-16bz;FPRk3YXXJ!ZZ|J>UazA_XgXfkG+po(DJ4 z3)nQ@`^O*nI`eCZ9Q4%l;rCsIH0LTLQLJ}u_84~53(~HYT{>;@D;3e`+cFr%Pqph# znM;%lM649PV-&a!(fxH>J1Z(GhTJMAY104sMz0G31;1OTBBr znI-R9Tmf%6l4syuI(8tXCFwB7g&%GMmC0+esrSbH{;o4Ib#SmxW3PX|1nM^(Oq-0= z$92-{*Ek-$^+Pv%=5Tku6J(!g(h4pC$Lytep!L446w3p*FHXuV>P_8;lYmrJ`4#N| zc3XmXWatpVCU6YQLSXoxbk~ImXPr-`z2#RNI&l=f3C>xb?I`4`fwJmzi*h43*_3|= z5w-<>#-B0u>XvgS#%nt0KVN~e|K>nC z^`D>t{Vy!68t}Yt`^~`{2e|Qut$J&bWxEGesYU6c+3JiL1%uz8uCITwL;pVw4#--c zY+JkF^D%-2nQW6Gg?h64q&xdd{C?k8og@91zTDGOIvB|;kI**^!W+`I5k)b*OLUJd zOw`qlxg1U=#IxWtBYO_*iKQz6tdAfOr)=A9BvT$cvNT8YJT@CuR=+BHWC3Umqgk?a z1)ni@?z4Nliv^^qL@6miy}OG}4bMkmU@IxjELzHZH4DziPPcuu*@iyhNfC~)E@o_jj|JtE3i z?B!fZslXmDLR}>rKBS?EE)?sgz1^X~D5UV~n6hib`948bQ3cgjZm(Q&DWa>CPRPz4 zFm)qhd&(pb?HojOJIfa+94f+hItgy9E&q54G>|nUVObztGL7!EWtJ_>AKFHy(gkgZ z^nM>(>ifVB&nV$(u;sg zMQw{U`A~`FAK$D0`(JpuswxrU@)KMHCy9M;BF*m#PRV^qK+1&WsxbLYuK90k?(2KA zwwBo3lwt|f?hjIL4fp8ynpqc%T8pQ0cp@l2cmtQqOC*V(3nF~{zVfa9J+YSsQbK$$ zeES!a`xVjpYS-z0(F8BT6M>uye1PAcsfb;A^BErM=~N}{`uGB(5%_}+yo^H!irN-cs&JKdzW6&2x+igiKO;>eUn$OK7J>0Dl!1)Lmu+F71*pVr~8{{k~hVeP*9ifSjL6VCkP=Td0Xk z==zR{IR|jzar0->!M0*c(ar}vP1JC!RH(EsZ$0K&?TJ4zI{&_6^5ppAd%N%T+x&cyt{p=2L~8Ft``U`RCQZ;Cd#CP7WeueM71*Ui z*6XQA3FEibH0?Gn(eZ&u&XhH48U~gx|BL+Hd|X!9xvzF58=!=HMAqBz-^LyY>Cj{; z$`HM%DFkgieUA~be0ee%cMYo-Yhj-qi41KGMYn6RvJ#YgPh*QyBXf4K>$*Mg*Z&Bd zMYpBo@Ndp33GBSArbb{jGsl*D?nyH$J>j74yvpSnxs+b`WK$CW6l2HPCV@S7tgtwD z>k5Oux`;;h7T44q_pq-9pMaevb$vAX=jyyb$GanuLs>nC>ROt+|Htvz&%AW{o=91A z3%0n&J)lC4Eac0PJgJM?=~swEHy~i#*3~uo%f=6%Lsv}B%@iqk@bKZNu4}g;LE>{* zh~8~@?sVC&eEfF}y`jPLQS)sdh}QS0l0T+$T3I`6?$wNrT*rS`)*JIp#aBhB9eYV+Uy?o?I zc74l0wjfp)LKN7;i6N^C+Xi5)ohO083>T=OlJ)D!h*;^T;~Esfqx%Xa4<5Hzz z8v#%_Ox!ap&?B~;*`dC3WzD-a==t9TkiGOc9z?r*V#WBXoSXw4!Q?{tFa<7n_}xVe zLd-KG?FG2Xrbvo^F^FT9g83AIU8w1^@ThO0GvKaS5|ow49nTq{lJqnZAZj3x(m}v#8bgJ@?wCDuF*bdFs?e{}G>Qx(}v@On#X^bf>He;y?4zsX7X`-H`Pvir)jD;!Q( z6nqAYCCb0=bJZIbUw=!vP{I>(O6S7)OBUq#jtEAylU;v0eKsP>S9Ayi)rjh`{kmsN zDs5@OpKl$%{l)jY9NYRMsOnnsqUyn}*4EUf*6s-|3w}(N!vEn0lyBrqfsb5y!)@a* zpb|Kfkyc9ER9$a%De3POAfei~?l<=C?2tfD+eLtF(kUwALC2zbg~8y^qW(gs7eEH( z4WK5hMx}&EStJsT80=qD2)G$(`m=G_UH`Hkyy+)ODreROh09icx>R<+MdG9$cd53P z7|xRh8R)=sd)%(=#A`?E-(kg8159ppROumLJ$LD869@=F+4JXTcpijN3pHbYD6$Q6cz@Pt@WG%IH72-$1lt{nk5U&rs!yl6! z`h9N6H^XXvZ}M1bvrxt_H8o|e-qLc~`h7=4cyQ|?6@{Cycl%Uo2Rdx~I66o%Z~)0a zrc&qbKRxx-KxIzpoS>$C4KSY@;d5ICApAwBKzk!moK^4t>=&!{{qc`Rny^=h>N_vt z+UGIZottcG8rlN=rGXjyd~``*M$PLC8K^-|0^bz}@71s)Wb{nKcGKtx(#2S z)Y=l)jpn0Jmg|A|=>R}Ghlm@Y~nqM7+9)$`uv&TYRK$5S%CFRrtC$ieW)ro zuV7$rb3Wv9oP7UYISDk>Wp#CY-l|o{xt0d+Cs0y#psw^J#{%N_$31atb=uZHhe|k) zp>XQ?hDWZkJ@@(RkjgBsK|9~&_hr4Wq#|_w;5|;a*AY>^na*<8r!eLRu2LVTRfyH{ zxIuZkB)c`86MpTz%a)4gs19|`V)S`5jb z)&~rqT2!XL9dq#abuMdqO{H(blxcgfUimhE?{i>Wi}38lPJn+-l0u&XPt3S{@LMh^ z$={VunLmU3p#jF*+P=2bCj7c5cpgyoI1gB5B-GN=HrZmtlrM_nwE-+ud{%{C(4S^jx)jU!8L}o;r+-sIrJJl%q(>f~!`qKB|CSx79{r zFH}hQ8qzDjfqCO0M*H2~B&p*}X$Q%6e||_AfO0ubAgV<7VO~8=h_N}9G+#1p?^(ah zlf|+Fzw+@0!*Lgml0Ss9UAwCzAQ-^*4h-b1_|(7W!_)0bQTqj;Js&fYeL;nRUC_3c zhgJ4T>hYOzV$2cDW&&BzYO+xCKzyODN$%D^+@Bf={$rsdJY*um06RY((x#x8!jN9t z*3cVv`+Ghq2b9|YAzY4%H&>#9#VRYnZbTA=+RpVDO4V;gUAJFc2EGX~Ol5iFCxQ`i z656&;CG@mKJPdX6ROgoJ^8?-C1(8TUj*y}ZcS9q5Ztt#~GNrk>Uy!P2EMb|`LRsPg zsOD!4mDkUXl2|FAEOX2A+Ca1M20R$KG7A*uW1xMCROc%CtIV?Q>vD1qkEI!(>L{IA z*Vh5qn?l+!vz@iN7hl&B*AYaSmg&SmOK&}@8vL5Mo=>K%8}sTLIB#D_WebtPv(hEr zYi80JB99sBx#KMKhv-GrugzAOGTrm+8F+XLEhKT^oQ%ORbnYm8bJ?fw2t^757i)R` ze~Kl%YvQh13o%PqptMq%%6d``yG{3;?}TJ_k;eI2O6~g@mv5S-El$H<)B=BH@x&sVlfb+7nV5x6UxSa0WPJx@MQuO zGH#N6*|PzIgPXzqY0Lj(?@QpMsIK*I?Nz<^?E4Hb>^q|%L}75pEt&+AxaYY>Me|pa zmzSpx&AzEIK7DGQCK@#g@fj4w9aI!S5o8&*0fyOUdZzd8+N=KGZRp8>XiS(GROcv&pr2??|i4+zk}XG7Z;gjD5*mcgl|X_^bY#{;dl(0@O44S0EQwYEoJ>F zKEh5#3L!I|o=%yD?2tIQxtaRpkYd&(QO9->vd?%bEK8=HMOVJnKs>;%HxVi5fz8Zj z!7t~Omie01RaJXVA5pq_eia!YML8Th@<^Tj2|e(@m3FPdZmcd<`gV7*-l)QEUNnC% zuCsPVz2+gtBfxb2d)BgV)HwBdXux$Q_+L4POhla4LwfUHSc*V+A>{TA(10k$XJEr zex)elBR-G(tim#rQzn^(G)&@Kx80WFkQbsYdwQF|mAYU3ys0~m~^&c(!lv>4x1sB`P^{qSghKf%bI`6W0-vi3w+?cpNG#&TU#@yjm74YWm=Je zqF2?m=JdS!Tt8hnX;Nm{&e(%UemP1tk}b)oR$>dnxj>Z|;D0!Ma_PiXmoA*Q^AB6& zyLnZQCMEa7JBz=wlc;jLr9V{*fypIYrgi?fzd{33dLZEQy7j7Qzo2F#iALhJNL?u2 z6d}vgPO=9fanw|lJMxRbSqpBc*9{~$B5u1*45|$uwg{FX6TX~drA$tYGODp{`MWRc zr_7(9CB2!qh(u1ZZGL*i^vXTxh|bMmv9Igt`)`I*Zid6;dl1ws!yzQru?MHsiV8X-mCs=aPQe-!7p^y}x&ewZn1sU$$fq?T< z&tKr{&)b(}f(0c-yloiL1Bgtbjht0otw}8N z7Pf*K=MhRg9#1`t`@*DBk_jdwuV+~6XD-8Ck8s9okXHWJz!th8k-wC8#an`5=_=p& z=@Vbtuwh_paX59{k-GeId!Q{H;0<6A3%s75FzbeaIIDs1go#p`1xbPQKje<2fT00||L1jFujY!MmNxzSnOIFq6u@@9$r^@qdxkcIDJ*vt3qiO(tx zhZkrm_1d%{Unv}W)=2t2-BR=fq&*qKdL5*G+hEO^DvcopXgFU-li-CydzRkwtMvs% z!R^d%L?8RN378q=J0jo49fJW|0sj_LYE6Rz@s%F+ovvZ(rK zixqy1#IEPTb3O~ZuG4r<{JtZ4uYaSfJBMQ(xZ!jB2ki@?P=eLKE))ch%Io696$fw~ zH$bxu>)LbOS$(dm8@G!HfMQ*Co5G2|oFmIZ&#(!TZo6&h1Td8o=yGg+8Rog&=~AuK z^Gz-&E?C*xH-+@JwQ@ZFGejYNACzhjBeLz+u+IN*cLf;j_THC!Ta$ys6!@D)2t3vQ z>{ioGiU0tVXKkw;IJPl!F24RyX)uR;R8ShnLn`#qEqluS-oV8&ad$B$X*WgUPw0A4 zm`Km&mn+2 zKJy*)HyS{>Eh=Z8aJDRoQ(;e!JDk~8nJHLO_u-IU=KgKnm-rd>Do{`UR^**k@vdYa zUl=$hW4f1y3&?_VN=pGs8oGnNCsT{xf~*yd&~QUoAwU-zxT2$!^jEd_mqAB&x=b^1 zM)_VmFMTo<*i;Cwgc(Ut*-IfSB(KOn)vwxRkS8ZQ#C(T!g-xCDJw4Q+ICQl=^pXx) z^Q{sK3uV{bAqfSqi6aWuka&7cu_T9(5y(gThE@?E2i-&T&AMxM+MHMc4io=&jjCCg?1%A_$>|PW%oQGVCxf=l)qj_GOJG$q?ar%ee{39iN)_TCh zXV);G3k1&dZ0HS**dCV%ymYnSBmV+XZs!0i{zAp@@P4pF-il3xcV;ZO0Dh$@&ZEJ& z@_a!>_|dGI`A_&Bb^-c$X1{z4=Cpoe>5_&(_uE6?YQhifL}a| zcQc#G_Sc2BwUvTw{XaBIoVxab2lnHAc)P7FObL~+jLZUY7v3S?v!`V{MRJq^H5Y?< zB@(R(?t$RV1z>#Q%6tkJ964g=%)avqZxWl%Y z6bG|T%I3z-KD~@?1DO7Tdb~h(B=#IFye+>tHAGrZ96mfj(~SrSq5%TM&`lWJ<-`|a za(oK!{4@IqDj;T7RmEJ6{FHZ@#Wr|+F@VD&<9tnE*e@Z8>zhai|7SRR_tRiqMyYjN z%Z{t|mYIP-lxlgy2}#VVF5Qd>x3@W#DbpnBkmtf@rDHYCc~=zdHgKzip*L!r;}+0l z2Ko9^qzP=~K7TP2GaTuy?o@huZLRpDjKN-szS^v??&GGzUR^z*p$hj5+?T)qA2~OH zn@i`I^DM)8H=ap9#aPTD@Zz(aUnwaF2Nc64XCap01Q4bVJTI4iObDM^VoF~ZJz_l+ z;BHnR-D-F^ocMg1X<6M-)o9+gV;+!2|49A*8TTBr;XGrOcDig#^Lfdc1|Qz-GVx7@ zEk9WqEsc_4`&HR)PGr`N3;F}9ZL^{;JZ#mfRasJ9Z8~x=i4@T=L}_^MKc}6`%(-}> z=<_-gMVnOwo@DxC&~t+Om3QRB7pn)j7d!eZ*2hUI(r>j0vUVSw+Dkc+ACAb%AS<$6 z*{t=!@7KK1I=goMeiKd8)TpEgVaaih!}jzAD;CRX#-UJ1W*lZ1f-`m%j~<=Fx_%M5 zl$vCknvz^91t9&7fn^{P`YFp!{q-VVRjw|50Zzi*_?S-Ya$MzVZNpd+CfI%nyAO=OSaSWe@J zy<~8dYi#&%w!+bo_mgi!O5`$~VMf`S@uJ{li&98r@8wvlxP5Dk)R%qabN`h;9P(=4 zmRRd}BsPGR#!?MyEFs?VuDV`YQ%hfJZZ=us zz6>WwDLQffsS6g2Ip?bH)cW}7=}0xS01gu5E+H$c>O%W%CI6M3cI4Tgeh<91y}e9u zn4e_S%39>btnQl8&SV6F<3oM{LAKXJ!)umh`FkFnyROJn zIIlERa;jr7KVW&TL2wyRN`82}e)_Ha^q#o5_x&VzE`SPfE7OiKfijBapPun}K#kx| zaRv5=h!U#?YVMO$x5$rfu=4tZ56#K$?yW+TG^w|A)?DUgKE%1(^|?(ZdBc zo$b#+7f+ikI}yAMm*X*u!sVgWgUg;!F}?z31;_4*ugO~0I)-(3wy%1vJ1B=Ii;Ufd zG*asu4-#lIxQu`0-}|k2|42o`ih`V3mZtDbmt`{JM33YLx9UNKI@A%D!2Hmf(z~$T z%YZ7-Ag{mf2;-(c4#&qNWZyau)S@RK_{iZnv66R5F+6hrhtyGT*MC7Gkp7W(@I|=C z7cLs^IK~OUg6oXTACHt(*>X|b<>>Avm2*lE)OK{`$bxmST*5*7S92Hbn_Wdky<;NT zpPR(K1q=E&;H5bSS$h#(%XwIbc}laL*BggGd;2WC2O4a#sy*5(h(fDH#L&i6A(tnf zzof2Ce`ZhLb4ksa6_U9-qA{&W^mwWeN%#%k%O009tt#-dEQv<-=BjK&VaAtasMG6E z+W-2!_h!Mfe7odo%F`94>KSXhT&u!ODl-2~%W`+IHuFcwfE-<9{yAA&4FnkE+$b-S zHA5@S8e~V)`1#yet-CxViAqvUtwcyh{_?}_e8^}A+M?wh@yQ4cKE-AM2r}HIiMqla zABRj>+STQmg23ztL5i_(FXDkg`siAMP)0<(orI{R3fOzPY=(QrNoN)})D{iIdmnZ^ zc%(v~N)N2+%A98t^AeEfJZ`YU2$2PsFLA~oLf8*HFsnQ6`ivF-+Szq=2TdIVk{%&b zQCD9eD!{ARQ{d^(Kw)^pDD*Ff&>28Rd@7AOOdisdOx7pU>^$2w{kkhZe0-JCiK^v< z=xtz+n{@QtndxNbdlV(G0)Z*(dz16L0$U6;Qk(18MIhf90nM{r;DjY-)&^Vh_YPI_ z*7T$vMC9w4HYfaBcGH>{4%A{P1eN3b7YRI5Y}igm4{!ZhOgY0`hCf@9xGsy;9zPjb zGKQ)?LtW002;Z06q9?<`Jscs`bRwjDe)rMF;$oVA;u9ZW{$o$Prq3JndP8PH^;;Q| z$^`5f%AuWiNUQ26uEdcG56d2)j<)5UorNLW8*b+U9mP_VXOp(aVEK9?GT&!iMo(tU zpS)9Fw0_@WzF#|NfW@fNVZ|V3A7Sa~5HEW=QchqdS#_trd-KM@0*>}uK0zgY2Y0l!$wg7Y@5<~gwqwr%lgoR>qSy~MPbCpIe|F)d zp@p8Ui9@MVnWM8YRuGBQJRAr+sDx^*K8_wJ&SV6d;(op>R)WP@aIms@Fem$GflC zPkhR*4{i33COaC(kpY+(_?)@Q=_3&($<=8&nb-|0{azV;P{}BhmIlU!Puy{;GO)YCMvUGB7q`AAh zZQYyE#C*^Nq6cXw<~s5tia7H17pw;&tnX`xFFxDqNv|J2s_>nc+IqLHSlbnoN^K|Y z`0Bj^8R^kYa7KHnAl+~5NcEG)BI)BVp!NDS2Sn>21>hNGJkPpUWV4RDc3amk8upQJ zf5Ezy1}M01btelvyt@%TmL2^&-b|AEc^}&iNj=RR+G;5vm0aR;IzHBk7U3MsNn_nOq!yPx2tc-6Jn+;joJ_pWVN*9Z*1B>^TU z5GvLo$lwNn<1RLb`JcpnTMoSUQ)=43^f6SMeP!*g6){Cq$B!FFrq7%AH)%CPMKv3` zdb{`V#JZ>glf75dPXfleGi-1hvvr{{z}_}tC;Kqv%57l9#L2o6@?L;dcc zsD!__P$TMW;oR&Knyzh(rPDKFeBmiix3ui1^1s@VDjX(~8+F%Rh#4{q^8_dt_Z`E; zstfGACD^yi)%COguwsS$QO~Da$YzlEQ2WzIU3297&sPsLL8Ae0tIuVezgV{YC{oEZ z`6Qu;W7$T9Vdh~ZsF0_3y&j1~4{Z7Ji!VklNM;r$>XPE}(7RL1d=VPF)3<5O`ef4l z89@7zV!9(Rt_EOM2bS@9|9oP@@B}q4EWYhFi_S0Vn51*8rKLrtpAT&RX@yLR)vUWE znR6u8t@8=ol4Mr>E+RFQtYiMzJ8Nzo?)iu2rxGk0D+Hyl99=th^4jfPMT=q2%|E}Q zV8l4nwQhrbx>{gJZ@+3rlZF%IIWA`=Nvj+Z-;nFQ0)O+}%bJHY@-AP8p*|0Z@J)v; z%nA84uSfn;Se8Zz0;!)+RD>wRy+V?P5SMw#r3}Me0=|Y)mu0pipys=N*>|0xxC=O! z`~b>H0Q-xDvnS6QnTuRGSWD}sRP}VkXYGu-fqdBqyX?q!pPwFBsj5g&Y~ISL<}Iq` z{*+keCB(EY0zv;m!zRDrc;*s~RhKM;06DPL;0R!ll~-~Z+i%2T0}+yQ=gze`g(N`a zlmTs}8fk3YPh&o?oX`8`S7Nc?N#Fj?Fz@)ICverHj*fT7PU!I!ogElE?Wla_;(Q-a zZF$d>s+~Sgy;nF=#fY&}P~_Yf&Nt+pW$ zqTe0v$B+nLCn->>sSiN7{IqSTe^fQ|sn@po;`Bw)4lw;CQ&~p17@`<>OtQyeoLhuQ zW?7w=bWivD`-g{==YS!kUdhoGV|``P-1(jZJB|uuR$ve}$kjybME%pdV?nx2Iq9vTgx?X&?BiZG@W1M2|2rQACwd7`Muh0R z#}M}XK9l+KyE~)RAmlDu8I4v4B<@m%IO+aWs`*^N9dCn);4IZ(Bdj>dY^6VmfKV`W@nt zuayf&O}K4P5={C|sYX4xjD!6>yR@{`Q8Vq1h^*3xIy6P6EwXrVj_oY2T2tZmYXz^z zug)4)vMDU^tjK}hjuUgjcR_u<9yiRDfFFj$Y(P0!`G?zgLo(pb8eNp`wk+gxVXij? z@oNHN0mBV@8Ox~M%@KvOiDT8kB8SP~-tBY!ua6#Glp53-25_6mLdt-Kl9|(|_W9|S z=dC{C&rgbT&pj6wrtD6WxZ8Bo98;x`IfdSeiIf-{186|28C2$xKmSG#P|xw-xa3p& znF;|)pufB`S%`<`wyhXDOhzZynvC-}OO)HkPVt^!dE6<}?A@8dU8*c}q?)a{bD06V z9(BkJCeEfp!az(GP|Fny*F3CUl zQ)~2L@F19VT*5O6Y+Zx4!TiZ}%{yRQj{=6DFkE|4Nwzq8z=D{ob<2`=AiPM7=g4$0 zp55!$?3)}>x-TFQ*5Iv)hv^ zY8OR9Q6)~ZE&T3~fb6C3+d=eOHGHS4YWMZg$I$x$bA`ssS^+oqnb^q;3C@=`enm(8 zVhC2e1E&u% zrsK?-#~w2mE?k(Ss-*S$v=2H^moHz=4aQ3jssj}&1MMF4eJVp#m>SQxTfuVmu4ysr zRl(gF>`z7ah#m_xR;MG%ef?`?1zV}$rO%?|{k{>}vASbj3zOG0EbbgcJDhXQB25k4 zJF|*JS9hh3m3UJB=6fsuv|!%6kF4WI)%~;n{R-GtJ+Az1aLP|X-(q)9#v_;WFQECp zWmtw-;$eU#Ws<|f;Y5BnOWXYP&e#NGi@sQQ$WPCx4aMoW-(56rdS#)cbBT1ziDRpx z^!hcushQaIHi`oGg14`;>9K>%(KYZ1mH8Z}C(BJ_IqPV{VmrmIz@PIp1%KHYxfyf) zzfxK4zlwa~?=6@8onbkXf-FBN9m$$pkyzfP+kf%#Bs{9ZJDCzF9q#XCNHkyCNU+TJ zAv%617QV0|b8&CBDL43kyR4aTysP0sH0pCJ)C<}vg9Q-yH_10RmubLKiG&u+dtr{t zrcG-W!auL!W#t!>%d4lus_vs0!f^;Q&M@gV+he_E7+M!$^ccvG#(4t%xqg3m%E_00 zulx`DR#<3GL)N~7y+|r(sc@v{mjI#bLge074CInAlSVZSifg1!qm>(5!y8n!@`L{V z%E!RckMeTb9ld{LYa(3Jc*^i8^X83O(UK|4n^R~6&%{&D8sUbkj~j}N9<-h_$|sCq z`1s6XJ2X=k$r*vV(Y3ej+al86_h}%xx*e>|pY*#wA{)U2&_qi^7314l`i^1v?wMCU z@W4lIJ-fTRb;jfcogw|(x4&f5dK3wsOF^h#LR|A|*0yf8ZF`ArJ8_=lXF^ci0Ilmj zL3f_CLcU*BiQEk!g}jZi_! zHTdtW>WbB(u>*Y@Zu?-XqietFh%~2P?u=Gzwe{nIoVuIX&evj+a{c|=w$WV-G7&jm z+hWShj5Q}yR5b4Em)#IXAp_;mYkZ<6^c&1E;4zzKJJ7vZXB7l5+(3JexK5S=CfLV{ z!uKtkeE?Zgr>eG-?KiC%c8T;pH0S$-pOLtSxHNpLs)9`-lI=L?=>n(oig zHKP$yya&vG7mq9Uui6wze-#Nq?vXgw=h|d5FS@_&jrBisY)wr*bqKqxIqEA(?6!>3 zx^Qzw8(l1WWRFO~4zaf>8Fstj1Fz-Q_hKKRh0-egJqLqr6Bmg@+|jdV%Yo8T+fJtg z3DpUdNF=H|!W@J`28cjD$QaCJhL-r%)bjFHJiz^+Gd|T-_^%Sf^&lFk5=0?u+;rr@ zDF}t#l1z>SchjYuKok&zWA|qVSlfB3-PKmqNd?DRws93CLYeFrzByKw_v9-~cNFbS z5g`fDUvKy7pxPU7I8lLq^wCEPn?RH3({(5R9)A7O2JhstRaZiAsR)B{BqD;WD!aO~ zrg#-y*jnQ8P;b;KF~dF$p3^p3cUsng+BmHP6^y~w=3xmXY9WmV-ALtiyPlP!~Z&I?`;50Eo`ys4fzv#x9p`pl&s`=nA~zcAg`o}FD^-$$>*cI-?UoC0ET!IJs|_yh9&{dqpqe(G0>{~M0U z8Zex764!nRshw>_WF7)iGVJ*;@a z*Xlzn=zUb+ZrBqKB^gukhh=NhVHQv`WSSwOD~3su_v`47lVE7*);CDYUaOw@! zdPGkcw_|`uh#)Pj6@;-Sc7SMUnqe7#CJDCFz3tA`QSHl42$d=q)fOw4>m@Za^V%}~ zaqOIv>(pS;+4V|beo?6C7@A3he!t%KaDA$~%gfqrh=qAIqsE`?SEC=~qmp8_aFi#k z+~;$w??LO9X_>=jpE#BV#?breLHnc$W}>fO|L-+xwssWWjceXU9yBUE_g;Yvxb5wB zMzydGGPQ!ucl*5DdN@m;9c?t{Le9ezXOwyhm5Tzba#=I%f%Lv!J{)jO-p30$n}S?lZ$EhFPUL%k4Bd^iUgQqPv@f)Xb1$+R^8h!jPAW`< z3RJo{w0T0OI;{Ce&NQAVV7(V8Jp4t1(|&L^!&imrFO#ZzaLC$-^c4+D{^dafg+?Ne4D~C&uhf`qh&FjEJfYz zJF1I{x;0(@Av$e2(iER>n(V_FNB9%0azBA7Vg%!|z3@o?)|TDJ8p7cuwH+ZB(`1%q zzk>~>q8+1BUX*wJ4_SL=NF3#lN6#xieeSzlqo|1K-2E1=RF9HX?$ zql+~9lA}=)C>%Gnrv8L^qoHws2u<-`GFeEScT|W_8<@`1w2D`Wp$7Ya9kzsoo(sv8!~TfJEp#%1Kh<+A-tu)^*zLCh>=Q8eYHovzxsop4l90 zMhT!boXd@kI5b%<7Drb%ZI%r>5?`KgL%vVG2xt1f95cCO=j+dJ9#iS>pATUvNK7~8 zP)EF{*XE{8^ zI%B*5R)ULI#<>9A`I|J;{(+xkuH+o~LPIKlDsQu=zJ?EaFoJBS$hK^a3TV_L_bmFn z!{Hb*;hREotdwP+X&Br$Y*6EO^(1m8hkcE78cM@WreS?gV4eFgi{V{${V)o5Lr^Va zIr~OIur;n>eE=}o;mH9!qkWoej;32CyaKU57mp zyVz)u7%`2wr@|(=$F$rsh2ermr2Q%Ebqw8h`qH>L|AMigB*spt0x6gE1Iw~cw_Rsl zslOadhyu7jkobn>JY9w?y|;EP55x}K8x)@E8l91dMHuMJ4&nNE)Z50R>hO!q0?I;z>PL!v;07S4o&)9bqIEPt{iu4>xE#IqR~o&O%%J9FYk3YcQs5=TtVI5a8#dL z)JX$%UahIgZD||ABnhY51L&MA#>;n;ruJya>$$5S==}{Atbd2UyFG0fE${)D5i5pl ziOx?we&>~@#fx)WS=bq-02^Ax+WP;4x_ub}G-9^tETAh5F9I71VLe+UY3yC1si zp?!2bcAg&Bx&y1`bvDNykAe0uXm+nk_I9rI7299+e`#(#CC-{&>8B33CltkwV|kq} zic-T0Y#Hh95;ok4B(09;1dlU+cz8e|pylg@;>pOE`bXR1AC~>ncMH3AcSmETZ)Es_ zCq$3=N7sx!f7Xb=&P(>?p`(JgzNosPvRG z8|hK_qlE6W9ou?4%NN8Nv2vn($Uhqx2WE!B@xSbhWzTz|B~pg_280s+CX}QH&J=at zUeC28@$8kZVJ71`|1xJe_o_Y;(fhLFV)4w`LV&$->Iw5kay%)ACI)AeW5BM_2vqkN zxCr}QQ|pAm>LE^Gz6^VG7Im7!vO<4XnoWv~_&&HsGhRu0Bu+BP3co*#DsJ+y z8?a~5I!WoY397r-0a|<;h`I}sYWnzCy1x%x#ecy-J;}$|#|l*=r$M{;CLlBKBY0pn z^4XPxr*^8#u)ZM6&6;<^4Pg|OYb(_vu{il5Xy2!nVw5q9j2|z%GC$eLnU0;!00)3) zna|=Lmk!8x`m>cnZT-Z`Stm{0m>@_YlQZ8i8(Uk$Cti8u*ex(60M{eK=4|eQsvwzq z5z9(j!oi?MuY_$cgvD~K#n}CYyc}KqfAKvkCuqdTE(&VFS3;k zY~`s2v>IlHnyDvvB^O9i%s?%^m&AO036q&#S=m2G__=z2+mkQ|+=Ldt1MRl}2u7eG z*x5iF^>QNjBUMYZ(Y8?}jA@4dvg5_TuDp=fIer*qdv|Zf-sm>@--T25K9qBmM+hE+ zB(3H6drNPoJhwoaJi~z=xO-J3@^>-u;n|E(qrcLMa?u0q7cFqcF zmU0^qVQ5)Ab}8h|6wklk2)VFhU30*ItslTkHJFxjjAPLuQ10d0RU=K={Lq z$K>sTZJEJ#d`zJ&oX$wB0^5^{qNZX7j+Z(271&AaHeDxa%iYN{Rn%oLTI|dk z;6>jAZRVfThWZng(Vm%E6-c;t>XDXM_oKso;`0wIUfkt`iYGG;+tH;)cfbKQV0NH& zsRKc^WyJo^=GmXv4mN3yJ*6%*9N`0*UnA-HpS=oqEX)>2!{9V$EpoHDXZtSIG$%sK z1H8w6ik0|ELshAbGS{C}a6%v!8ZNbwbL*i^&lNzeXYu5C!(`TDvTVn_N?Ox$=ATjJ z!>4;3{pXI&UJ&p~HyC>I=FQ0Xx;;e-H6^sr&yjmn$NVLnqLUbrPmj!Gc!TH{fJ@;K zGix8a96R-(UpNzCi!+fmxHjY1-#X^21tn#ieJ!Ub_i(ar@?vN)RPYMJMmBJ!cxAtt zzFr(T>6~pya7Z=ls+lK_^Ah25IUrpIBBp6j>#Yu}FDC%$!kT|)RD<6L1V|z@EX;V^ zcg$Gwdk~}-S*~>hG}s$lTl-cow#oY@UG6rz*zSwx&wcg_9Bc}1SxUjm%mi(ARAI38-%@7w9v}pf=7%{6X1rl2 zIX8Bvmj+ucC;ZBN-@dpTeg!d9S#&JNk&}UvD2!*UO@kfe6dGwUP8rlL)(eG;7o8t3 zJV(WFDBr8aCujJ>&l~!+D{=GXz9P`k0BGj)VnUq z@@UR>w#p*^ypMI>>u*WZ?Zd#N8q`Mmor9H#8|)~eztgyEC|#3b(}TU2ZDs`0W^C9X zO+9YjXm42hKD5&RfTAbiEy5nitacbr^ zl3NPin-mQ0LE-jjc7-Y|zUJ9ia4l30nGiMG_}sk!cW!`8qVc2te||pw8y{*tX2?BUM)l?9;zC zbaF4Y^yj2CaTwyW~IuCm4xu=^Fb@M1U82eaJkaDZ-u zP;2HG?RmCK?+0nktJsoGg%LMIH+9O0G2<GMH}zM!Z8j|wPKxP+ZxfNS>Et4#A^lo ztku(X#T5glqQPZ-QU6|u{(<=?al(XQJ!(%+3S1bcJEpss_t>|KcJNVbw_dRj>RW~6 zm$Z+x`m(LSIgLb)#{UI}%Y~xDz9cEkr3gFrXD#mXyH_{+_w}XS2ou4--+#Zaq_FB} zaCE>D#Ky4ei-byjlpyJ^2QuA6b-O)f*k(%2F7c=+?@FNwOn|$N(}XJ-j=L1DxM!nT<0aVHsK)P7 z;`!-gD*Gwv3mr7Yp3=Hg$4?uU_0;;-;{tnUm$4+mMv2?U|c5YJY_v<=k4ZwQ=N&KD`Tb^^l+8#eT|^a*-o8?mYV#;P_McZy|OJ(+m= z>CHW@D5&_@#w^uzWn2I&!oH^#I}Bj z!cKvS2eco~G@D~4v%Qw3s?ITT!2iu3k&oW#;G^2Zr90NWvK42u^7^E=o&G7 z3Cr@2!l1q`or&ibNX8Z{o45PphT|Q#c&}!bFXPDhuRXA9*DeQ+v?R=I8(LGTbZdJT ze4K6}aroi8nwxX@Z7janfU$qZNJw}^_D4!kxtpwBz8)EymO>yGdV>Yy^K+95PScR) zIJ){}!V0FtS&pD)$8Jn!PpAr)Tv%38@B`W8?!h+v^h7K+;L6Xq>PW;6Lgo=9 zB?9J^VSWw?bsL;mBar%KImcU9tAg{RF(v-hPM|pJHP~t0Z1%e~VehXp9{!hAg6E1c zb5EZF&zGG?;z=RG|6i-lySq9SUnqun1;WA7y__4Z^Y{;+BJ9{=AvZi`I>hIlh+ zPoLk|$hEInLA|3E%J_TU&wfHTe`9w#vcoi(w=7280*vo(;d}q_tnyGRe*e(*w9W~} z9vfH9nX+J>8cOmf#!5Z&j+P5YuYj+JmPxNEnLN1ynn*PxGC=r=Ezs^aVep+|JMKs3mD6f!Lb_5^M|qc#JaRXjXAwB~g2?1sI=XUrCE$KF z793C+!w@wd9L=`N8IC4YH8)cY8XVf3AJJfpoQ&<&JW@Rp&;$Vda1;lPwS_IjLIB96 z1&fB;i5YVzG0mm;-p2rI7z)ZAE8E(z9=gb*inOT*935Kn7{YNApjF+(nC9i^qCJRa zTCMU)11Oph9A)2cZ;n>eZaCo9{XSlCMT5D$xOf9w<$D`HI#j5N2;1DUzwgRO>EfBpjBN;qFOAHc1yVK{ak%aW5&15EWDuXPEITYvp9% zbE?k&<=@p)`X~~|{$-YsOXPk1ywWC*ds@2e@ z%iiox7q37N0-8B6>8V4J+9&BVX!}3vXe=-9_m_Cz46Esv-f3xizhRJb@xXg1%+4_# z0YW-~RN1yDI;zOKUY_xWRR}G#6icjUVj}}RKXEGKnFNdKoa0RAGqhhKFoinnizI0`)N|cfwWnC@147#VdVry zBgnde2Fd0>=kGmo{%{ZMiM2n2x%Mv4$``@`eHljm+a#O$-$wW|@>f&y8|&4dYnoiu zh3|5tIGo^27I^nd_&B1!o3FnOgHuf;%yiaz(l)I(u)7_hbDm=&O-&z?M@%AFporY@ z5UeVKI8p%t{&vUld3d4JC-LLqj6N|aabHFbKPhD!yYuy+=cFC*qXvShnh-#wcJOb9 z!{69Bx^ZAUz@)Kf_*g4k{`9~Y5vp6A3@Mg?aR}rl$>kWZdQ$IFPuE7 zH1mF6=9ln>od$rHm*JT-pkOh8&l9K%BA%U{$Yg&fAT#|`@J=p+V@W2CM~djgo9{ zFiOK)8zC$}GQ{2TKM#(yO~dc?1lW%5-8((y<*q;N)UxHl9etgh6&}u8#VH8a)d3z$ z^{rC;!|gPenh1O=OaW#p9Z@&Tt}p9*Z!I#fR3#fEj+wwZd`va?P~*JfO& zTo&8~SbA<0GZ3ffqWqO$6OaaTc>JNMrA!jMkAOI< zlUU4u>!KME0AU22j6w>z6)i;YGksx;XSo@b1s(WqKY_GlYiyz+I4>xo^N>uWl?h!B zs-|05tVuKl!6Q~S{(*?urFv@(b&`L7+e&D67>r2mpz`Y*|=SZx?2F{(Dqu(ft zp*P3)NoHr-nrWKGBM_KTUUR?kc)4%eLTF6;-bwC6wbk`qDG)R}({$cs9b0<-xH4bg z=KkbSx<$T(%*mG_N_rI7TyUp@XOJGmjj!hH%y%Z0miAIIAOuAG5$zlX%{+tcehh@) zG3bdgWY|r>j`c=7-TSJf6psX&w-lyal#dvnJO=Lu8OJc5OkY7W#s%Tq04JQ+vC zafG3^YT9UOcQ+8~8BA8$NL(3x z2z@s~I7WolM4X9aJ1sJ9oH{0v2vA610+#N$7Rbx z$s^f->u|v$)ix4^30OA`8qYY2@o1!RaQt&;c-YG;NYpxv6G5%e;1~RX1l&Dz#o)yVeQAZaEms zO6&GUPvUsq;Cg3O`uOMpevJA0WL4$RXYO#r){-%e4TFAu$pdjW}>|_QD@%&StYwaDLG|1JGO769} zzV?jA!x{*iug*w(+Q{;@#Cj1@d^55apIu5k6OOCT#R}wZr~V&g`@9lnv_eG0Jvp_+ z`$4|k!{I!n!uQpV=uFsWs7+^mrX`ZDst?%BQGvZ^QfU@%$$ht1oh5VTZz%X5c+wV; z=dp!$iX7s+1Jd6=O)AX^*<8P1CO~fFwv79|V7kpOn{?E@*h`*|Ed?Us*yj)(GafiT z5L~%;_ZpdJN6NCd-<6hOE{*W>{8ylozJv?RTM?v?7i^SH@jp(OcQ(??^gBfKS{div zmmj^o?~H{DIiUy9Ib8b3(5x?qE!B$(t;LgWakS{>T`S8wsBMr&$uWZW9LMHP=XmFI zhGAvcV1X9n5`aU|9rMFJHt?GiN!<+N%KV%ZB5WKHaede~#aW)|1=g|Kuh8*yj` z3Ck3c&^i;Uo=IB)Q7<)3{b2+?RDa_duOA&Ye_XKzJ2Vv#Hl^6aGxqD7T*v*B{#!vK z3bFM+6b+F^B|iM{!$$rcpsIVjW#1>4+~KNE|aloDI$RW?+~{!u&*?ZVA|oVT1ux zkT}T37LqNyJrd$dOYFr6{CT`R*7b+H_We)l!q0uNbd0nao?jziYPHW6+Dy(JpE2C; z!gSaYmc_TavuZe@Sx4z6Nscd5EdTKR^*@yV?Ql2`sqlTFGc_94+H*k=^(r*p zUH87bB0?Q;`G#&xWE%iQqua`M7zF`WVp<(7j`(bj-%wQ4Lv3{VYd`9oPNz#+EoK?C z&B>UIAAnHhA~0RmBDnh|>|E-g`aR4Ly90K|AG<6U;&kntab@9Gz$X#3G&eM^IM4+} zjRr%w&urV~5BN$)cm(cbSGTu$3xs!``n5L#8SSo%ByUF@u7I}dMVUX!YVqH|c8s97 zfrDAqPULVfRc#pjGcL4b$Obslo!<+Lv)IkJ+n?F9zT>QMM?C^t^f{;>_4{W4dCOwB zIglklB`~~!P&SBc4WEG*aCJDpkd4KLeT$2`j`hAFi1#Rnq(d7OgE1qVC3GXQEMW?4 zDw*XolAbV0iDE?n6Bu5h=H%stSj&MS@V6@+;|z- zL*~OS{J6_|o?HCu+j{9rx)yQcRJh|DFb z8e2raKk!9uimKJ%ihUUXfKm9Z!y@*Vdy9gb=fRM$OZDYJ zZA~w3x7!3HXD%KQrqOJ5Wi6gv|>@(GAj_khRMeO{gOk_ zfa3>@O+aU$4AFLMaJ7>;FIR>(e9|$PZ}XA!hkIag6A=xuqr`9pQAqZyY;lE_j`TZq zRbHzT)aQG!dm2~L$j%?P|E_i^a<;LGaz<}F6|X3%e)(kVg6V=h>4K|@WY_DaWw3i| z9{7h%yU0^&wI%kNk!TIvX>)%76JX|?+M+uK)ri^HpE?V%pCCT7dQ_KprWSe+E8&}i zE)A;iA^pjY9xaGjHiqrqI{=3k@!4$o18bXl@|j3!MAD}Il4#3w*>(tJ{k*#*Iy z_x^%Tg7H8wq;*64rOKL1W|fvEp&>Hz$nb0whc>$ev;Psn)R%Gr_}Q|?JcMXpjc1p@ zS{g~E4H!|RFm?-oS+Qw27GO7a0H0;cpczkq0}Zu=)@<3}WCdFUzM$^?teRD!Wvh(J z-8J*7s~+v?N=-$ePh+{@>DG$9?Vh6QL>0qnrBja8-i@khR*$Q_8Oc!EH|&QercL$< z4$u~^4zxj+X(0joL5>0?I@Mw1pA(R*)CPkW;*$RSWulsF*F3%qXIV3m?6U3L`SZb4 zxc>oDUOA{Z$HFAD658rht?Sll^XJ#YF71bw3yU<4ae=^oH(@bH08`=HfYrUqIjNP5 zt~mUpk-*8*yXpa_~6A; z7cBJjr{X`&*x8_DDG|WNB86eGJ8ATG6Qv^JIc9h+jsRZbuYo!Bslf%qzrj1zqegN- z?pRL;NK62zlT)#u2~(`$4x2flc4gZRii8`l2}(m!+t``VOdim+_H>i_JF5h zuF2_zLD0_8&Y<4O8GLXOahpH z7C3)Td#5M81h(LQY#X~)N6DQ&F4L;IOrxzjk2coTQKA@kS1h(cCmb~!fHBOSrg#b_ zZHdM9tc}HDlu50rGVuOpm_6nHGK}k32)Z=j46&-V1V=H;2ijWUX~oEL zKyZfP#fKx=E9@KIpdJ|^?XDJ7ShV0!&TO{H8$i;OOBngMPHpdu%)pslpEOe+V0w--ikf3&8lEdU|E?-``3yHxqt?~_gD)1cQLOZ?9_z^ z+PSyCvU0#IK(BaiS5xb0V;ja>Kwe~#<^|Q+J1O_wPftCuWYX{T^}+M`n5y$Do5qhH z_;p27#Fthr=L`gO419?{=wgnA^Ji}_Tc+jNtOkAuM;alyNl(WQxSyLha9PlU!^Wi8 z9H(<;F$g>F16^q-BIWom25vp_$D#MY!QSyll?o+@`OB_Ior%>OT)jLmIK}1uMy9iU zr)+wHGl52`)v;Gp1Qr9;a)D%`JhQrHSTWe@Y-G0{XAAt#2bWJxU)YsCiMjG+u=}Zo z%TVasVRht$-cZ$O!91k8F`OQ-(06$Rc5Q*lmrvgp{gQu%-`3U{#QGATq1Q32G#WZh z*AD6@#W6~$ku1Z$%oe3zf&ZY+^2y%2mo1ZMZm+Yi{a*Rvy&itJU|>@v?h_ziHXt2u z4oS{UCF2`rw(6wL-`xaq1n(kn7u+g?RcTOt!tNCTTuT}9jrBbP+lFCd%AV5NeU;se|F;PSXML|FiMQGdB4)L|xZQJ;4 z+it+2MZ|&FA|L``NWv5{Poy$cswy?#x)tc$ zv(Gteuf5j4{=e3986BMI_7fMNC^DxQ-iaHWF!Uxh)D%j1J^J^8M&IbGKM8jt?DGO^ zvWIfn-thtRo3s*)H%{P@>@jOevPo;Pn0~j5yUx0*yrPrymsOV??pajdr3DB z!EOu-ET{NHRs>YFmT*ifeSTc5);qbas;*$R2rYHoU8Gdbo&5WI{r740eCfr2GUhCl zm^L7G2TpMIsL8eL!q=5)I5{2;9}6UtkIJGWs%e(9$e0(lgr`WDCLCyAw5ae}fA)14 zlxs1$4Is(%<+l0DpI}(SUsSYE?cu_&*6-U`bT3?32ztH$1jCu&1?giJR1G-6zEUd{ z<5G3g_f~}nkPbD}%PhmIyv?&#!g$>TgC2ml*bR^QJ1}`cB*DG_AM7qrL-;PdZs^;D zM9X4)R#EUGukMDSM)(?q!M6Q%PL_3GUO|elWi^J)1_G{>Y3BjL5*m>>gTxgB#zo_0U1FJAGjyXRC_rDabl}O+4!&KdLqwdbvEN; zQHX1rcRT%J&fQ<^X}Rn~U+!Q9Kipd6GBCYEyu<)&PrWyo`Y8}!AMVXOWV*P^5!3+r zkeT#=wh=~Z1WFbgKCoz7Cp^?CK)(a6Za=`;{IhuA!-!j4ggUhwOvav$@WxlbmK%7! ztLvMj`2?g3<;o?e$W<$b9jl7Gbv0WNtHn*f9mN;mtg(NTs0|Lio!2AT#iT~uK!gc% zN4yaVA3TPm(6NwocXvyNIdU7?6TnDE6P#^`*E0^E^O(huBY4YJsh09>cr)c0P}}O& zJk^D3nebe_)uOjPQ6@Jxw(A4T?1io_Yhv=3!kS(l;vRRnr9a7A4l88dytj6I|I=T* z2S($^_sy6#?Q1K5IB}?1w0uLO#u_lX&WBu0K z+Kz8OUjzC>b zg2xQvbQCGrRs%u|42s6#5W=DWyomj*MG){}!du-9P#!0h3rs(}`3(V~{OJTzMmL+DCE zcmd&Ud(^>;W!&ysYQ!6T1JK+06$oTn*&=fshL?tZ?2pXwnjlnlU?2kU0z;h59qLZ@ z9ByuFJLF4|ojE%X=nwOcXcKBB$W1{{_<8I{|Ct_M=^7Q~J}cH|%$`4=qD9L6+q~>D zpj*fi&@{f_b}`Q`DJKN6$E1ZOEBEwgUx6R<2@mI*vx$(N_OWgI!eNgmhu4VV{uu~_ zJ{bvHizR3_X$3xWA(D!kEbwpqaUC(Qx#QkiVrfNh9u`~K&@d=HG}h0 zkL#tvQ=C(t!DowG<86@=tglp3G#@x0Hlgxu)T`y+C^_Zn4zOF>h;a&3v3vX65AGTF5jR}w07 zT|Q*|NdV!38j#LZqw4MB5D1+O!wg$$Celi$6$FbG5^Zyfw!X9>>J9b~vY`ppZa98< zaalrGwRI6H_v`A>;zb z6i+h4eqd7%+0@>IGTjQiED)epyWEJ@<*OP|1#vSBxdK$zza8-UGIYm3w*XfX1D$6V zGSflIR0nUfY&ehY*pZvty6~<xjwR-QnFJNDO zOXg|@_0GVCbEK+j2hMd;e3_n`Dj!HS(%m9)+z6^rhhpm`i{mpu+NU6ZG2|_n{g5Kc zgH-9WCePfT?!aAmr-uKz3wr~Q1Z>oHd}UQ=9~NS9oNYic(8JtDSPh6Sa2>zNlgu->v1GQb8kO zaR1K72E1Wfe=J($fthYn7EL5RU#=sb{ei)8Glyc@W0%Z&tH1+>sTN}d2d5H{T5BjE zdj=OkSpnyuV6;E{$91&CHQ z8DCTH-mIT~lK}A$7~UWEC5;`c!(|V9e4EK1Ac&aT9n17XD)>AxR3E_aG=XN{h)O5) z>%L3g@?sSNDnlg6;sI~;2?tAxOXfC~vDrIEjXgvy%wi&~CHsk{g*vu-X$Ua)qKCK}R)WbK5Bc-{hvh|Mz>)10aQJ z<5=T1%Nwlv_e3-U9*``p$Vwgo-VU|3sze1&3=Y%cA{mRyGj!rqK@wROJYMx_X0r_7us@A@!6XqKNu@-|Wzq~(~&sz*#3p!DaKYVQ8 zWa>uh-Pv9l%(5m)z#UwOmu)c+18!KkBT%^gZZYhkQ;)J2h_xX`yc>z0|M4ZW!qkwD z-#H{u8}J7Yj~V6YB%zEX%EzYr(rGR&&254w>uYqu-!ubwmnkY&(OG+xZqo&H0N`$i z3?ejfoITY@o!~pgZ{t#cCMH=KNWUySDmwJ^_X5Z z=OO%jv~4_rM*DTbcIA&WNrub2-_4c&qh&0Km+b4<4e_=EoM|k`%G`%{96NSO-(c{) z6iJ#mquF0(H0x$?0K9-0A)t87vx=#2M>MGy8FWE0$m;^(H{&@x<=m6o^8Vj{`5u7& zT0PF{Gg#*1mV^Cq#4ld|`-8`$75O6CB5FIXUI6jr36_`I64A+g%+QPv0{JsoD3X`V z?9jHq{1v?O8Bd$>%>bg1)YKV_TWo@acL@X-dwqhKsm6b3!npCJ%^BkrIO(M>L%1wJ zs4eztOxag~mGU8p<#z-9z~cbr{gKso3qZZvsxq*2>C#iYMSARA&peZ?cDUC;p6C16 zs|OyBhD=!pF-raI)c#CZ@ItHLX*kIMm4CLGI-C$hNU)?fla#%0>9I}Noz!_eE;KC& z4;lSvWo=CMv7fyf=aDR-7i>-aRxbPM?%|H{kOSRcW=Rc99+=*+7enGBsdQyP7(YB? z_+kJeKx>p_HgDZ`%C%yUs)iB4fCVFIlY~4$Gmai)PE|`|qcidmuOo8KB8W-S?|7CE zAvsa*POzpWGYA^f#31Edpvn=tX?zlf5l}qzoW+90Q{OJu*PmjTs8=os+^vXzhfp-~ zQ*XfC`F5qB^d+D$F-m>|!&knCTD&vQqsk51S`Do=HB~+6j<-WG(Du^h+-mp8{OW3& zJS{&jlq5+7K`Tq7H*LdqWQ0%{zm<6ZTBI#3O~U1J+pu5>}KFJ!XTHuxEfLvD!i2C7=$Zrk_ryVGWTJ;U8G2g56VNxV!F z$V_N$U4V$B(}{Xgyj^v=8@Yw@gnDJ2%aQt#KIJ|USUWC2I%D#&RXgj0cZSMqX6o#P(dfE$Cp~BqxFkXdG@w=vU^m7} z=YTS=9HZ>%@?0H^3}f=#D(CT%57vp%47CbLOjLMdC(Ep^k;EG!obwaaoacJe#e54b zzYEPoLXK5o_g~iv(zA;vd4;r}qElQefSC9Y^wsvTM&Y+pC!r4L`Ds|984~vHh~q{< zbgzcoNEqgqDVH8=YQMoqk&?zvj}473UQj=~zT%x%uA#D~9!;fBE8nFVWv^yx5X&Hp zhtca{+{+2i%I`(XbfH90j>LI^de1UAi!ngCEtlZyc=$l#)&KZj2WCVspnvB~ul7wA zVXYkI{q=j;1N3<)n${j&C+m!C>hnP2+$^!&Yw%>3;5mB0S1o>X$Fka^lM|cj zfFlCN0z{U$8kGHe!0U0Php-J3w>c9&j~j${+Zg&%rAhudrcnGoXL0}P7KMF)l5ugK zUDFb*t)=Rvf9@Py^Wqr{=lwgp1z&Npa?%tX9gw?;gPZmz@mIPAv0vFUL8xM=$>TXn z^}7Dt%UU@{8`L3Y2AmsA%eWhegcoGSnb1J#SowYOnnI=`XDwMMn(}8LV>B0c6Qe2% z1ebxTsU8O<;+eP)_38wPn}+Y)ARx!)cDl&x<6fc*WadB>fQnK-S{I1q(BAUywA~1Y z)WEI-Ck~4U&alZ|E8Wm!G#Q!7JP$)zFys|Wt_Q$lY@a;RE_5&sH1)NNT0sB#*w)P}w*;bSyU-sGCTbSDBbHSPL zH>kf`%d_HlQi?L=KAf9gLh9h|K7luf;d#!qSW(i{5zrE@9UnaC%e1w7r23WPi6~;; zfP$aA#Nh{XhPDgnmJ}d>D=;CNfi1?LFPU(8u+3e?IE_yuZY;4NcE?P?MCsp67k_$j z>)AJdu?}Q=OElY-^o)v-(Vu-RPCoJbfBi*!fK$lt3&q~2R?nX?AnM5%nL_qb@3tre}cNstSihZl2t39<(eLU0(Ug&;Tdysg(sUk+Y0KMmV}L=WSICL7BuWC2iA zy9OEM!0Od+aRi`^^aVBH!AjSI;eIk$|81^8)q9TaIu^ISCWqxMlPDQa+8i<3^9|K_ z0PVhqwga(IWc(mUX_K3LuJEV6eclyOn9p*59fIiO>}4xwBICXWX*pNNifG)P`9$uU0r3iv+^NAZch+Ny_$?>pC1sIePq+7a}M4vyP~YkcsNat{odhX?&M6?7gNXRG}(b zF&Z0l^!PLcLJMkM>`T-NUe|XVvhqeSbMK zdVnfec%9s})zPUT%5U9Cf)XprCa2l2g#`W~Jjc5g)1C$A;cKEuYA3duK3~vAj4kl{ z)3j0(GE%XXP-C9B&6)7|ZDS_ywqZz+b?c)3qM`|UK-5BtRi!PrwAq*<9QjOZYxza1 zoJ05fLH$V@F-_RTT57~#ojYVv{k&b$UXPf@o0aVK-mUWA6*u1rItc!ANZ6HNsQ;tCzMi5?PFf5XpJY_) z6?nB#@N0em1A`0mg0$h2PQp6pdGA5N&QGx|Pj5SnJPr9Y%wGHla&fCq0jqbG*NPZ+k#6^qLV}|64cVr*SUsuvbTk zzDJ&sQ;i1PKAIG+fr5pG(%}Ni#LM@vTvQ{9OkXBb=5DTEdWlbr9nF28LAKxKRaOD#=>bmoc4% ztYvO7n7Kj#P9NIb)O1Rs={h8jP%v?B!EhYR^(Wnz-+EUQ^_V8Mof@Difx3BTHOyG@ z#<0@#;=n-YXRjqZKZgON#(UznmCqx&kv*=Es#pCx=~PJ_kPIdk46_a)PUJY}%f({LFHe=>`U)1`1rGIGAkZ~FC8yhzie^39fRHvk zXT#3mNHFr$q~bp;rpX+E%U;cbW=Eq$xL9Z#fOM`$*ny?9Y#eZyM5Afy9W0sYNm6x= z1%8s=@YA2F*0hCFE|@;2d19!AKG>`v+z9EPneewC&2#z=npmXCGMccXho|T189d$* ztoE!c8Rp;Ex-|qZ6mV!ZUOc>WyvmVocR_^7b4Kpz<+UfuuC?GS3DtEUM?uABaTP0Y zI3zQP2oHT&>o{?3bic91UJ!|N9gh#cwrW*5VS18>zWImW9rRZD|5P-S8^#7-3!Fs{ zVmSLclCMw0Bi@Js_FE&R#3K_)S==I^uM_|yeg3!gNi}N0uIgK1Z0RGUF&wi!lI3e1_tzq z(`jrzLGVqDgW58Kh7ZRVW)mS@Rq(vp!`|_MKpjf3RZtiy=%aAOSSV%PGvHf4wUZ&{hcoKp1j%{udx|zdd}t=J~3~_ z5WX&Z71P2kGeI}}DNN@FO1l2@%@6#0u^lzKGeS+%Bu1S1#v4ap!nWBlDoHJ7T5iO& zU393$;1nNiY&^+8(tCvMDbBaNWDdWq1f%WuiNsm0bIf%}=D(;`s{jB%07*naR9%N| z7f{xm$f4L6_jdAP=6CNVn|NMcf%|7q@O;N+LTU^8v9eGJ^qorN*P}y6o+x>oQcwsO zt6N$q(&7E(4D115I2({G$5>4DHQ5B%@Jt8CG@vk3!qn||$V)t+s9KN9$Clcwn-m)E zbB5~f&8g-zxFrV#19)zREP;_;fI=D??}a%zvzB*le1TGsb<*Qp`5qh7%)m-q6Wm-~F_^GUfNqTB;d4GucMF#io%iXee$KdGDcKbyK8RX8;_ zrM_xEa3SL_W~Iw$Jg`p6C(}fNk8G_e+U&nz!ftycX?8A^dV>UmWdlheD9fiUv^)|M z#_8b!l2(|RE5T|8e7~7sII%aUlCI`Z{UOa@x-R_8=Xr zoDXZeeJrL)4HNyI8jbUTO~yz3E9Zjf_tZ$U^$0gYZl1Yh{(RJdyk*xYsrlu1-u-}3 zVxF)V!^;?~*Ak)NVX+kNTLW45fUc@?Q&STTMFi zi$vQrc+ZQlnTx{A!28o^5dQM7KltG0O+7uG)Ob^`@~CZZzK|i>efONN+lgd^3=B9e6MgN3~Uc(48$F}9er30 z!PB+Sk{gGXdEE||W|_@qRd_erMEj+DOvPgS;5Y{V4~7TA#OkS-+&e5|p^<$Z9Vq0k z%ASX=1OXlf7LFU~A3Ns6-D!I_4rZqVyV17!rrY*o@adw159J=76=vD($52O26usM5 zFy}&}ZA_l2NTM@}+v9}=1+uNJytl|CT4C?_&V5=b>xDWb?KQ=kLqnOW| z&<6ZCo~Dm!CUarIWcT?TOrLnLLnYPKWcKXY`YQvepTnW~G9dI%BB^l<;jZst`-kx^ zQYYe!Ti&(tEsCO-l&tF{je#>o;AKVJDaBF{JCz_{*^f%3U0ZkW%w68zKK`Q9UI>}~ zK#3?xup795%lraJW`+<%*I7gUO;;z;THkV7%AH2T1s}Tg?&}nXYd`pPe+8iDJuc2( z0#7WdYNer6KCGfdw!ExrIjaeC`js#0|3U~(d~?mHGcw%N{Q?f@K%st9CEZ5d9g=yrbh z#Z&7+199RyVp3!|Z`)VHaPKMRitATY`RRaArU0E0X>^D@7?N3?_I@A!m9IS%a0X39 z1HP9fs%DNv>g!JmonX7STd202xiiMrpky0krd@*T9LKEYr2eEk(!m@3@GZpI&o8lL z>uo>y=}q`L;q&k}#5FP#?_=B{y9eZgJSfhs8d=%6DD&b-A>&DooUy#WE1~3nGVjBgaJlc=_@)wx(V)9_0Ck>&Dq}Fs z(MW0k-c1Lp=gm7BR8`}jqEYp+X!}WC&(Gk&)=N4PLV*Vh@%}vMt*1j$;BHLYHB{Pm zL4}Y5axiO8RGLSX<%8>1c=#UsLkI4 z@BP(@N(}Tyr5(YUyk;!-3BGigSbOzqc3SD!$nhoD-!<$K2CJ9ec-PRMuD>O(c-&L~ zR0ZL_B}6csq*CfEa4D=<(N2wF8H(m^ZuU9^)ibp=K;V3>03|P(csIy8%PC4C2lTIV{M(%+21L!{WA6e7hf|i|I-%_77QO(F$hh1ER#>C$;u!u`@0@9W^3d(E- zcLV0v0?Yc}EoBdUBujD7s0+q0D{7ZV9iRO&&US+u*8QYkJ7_JvX&1^rHcQM41 z+^Ad2ac@^)kTo?OiBJ~8ZUwWziA)^$ykybi0T;4$0cK_|vGAtG5BUCvGo}a16>1BY zF&SRzdF_p14LS>wug4+Qd3_KnVG0Us>=Gn?sSchc#P6R$!;jF$Q>O{Dzo!LfAPYIg zuj1`XQ&=h?lRUXIwc|yW@@+xyNtiYB}@8{rIL$k8iU`nV%hJNWchZ{C42!e zfrl*z^Cao_7uzQ&@qX>>v#IpKWiaFn>e9NzY1OHvCqf6KfGqVYiNsD*6H|h1eG_bC zj{upw197E=cuQCNMRqnMfxhhbR8d1Rcv8~=EMz4}u7TOskl2=jZq7r*b)PAkS6Hk& z38m(x1x7gLE-4EXw6br*`ao83g1fLMa?K6&c&9ZXfvWbVwd4 zrL$N^Szqc%QB`=SDEI+I6M4=&6soQ+hf>A{gTZx3l(i8Lb9PUUuszu%MRFacV_pqH z2|*^a>)AY+2IZp(OWxLxGbZx`P~}owuir3$4+cL53|0hQ<9x<2Y9gy?Saz-X&_fS( z&$t<~bz}WcXB(G)@hewCpz;D>fuGH1i_fpD4UA)%bsuzyM^l_zb`PHwIN`N7E?nuV z#5(rStc_qx5lLe4xS2>QLK=sXGkTiNbRS#y{wzw5T_J)F#Y+~T=yL$*ABA#_3lx)wT@K+q9K49yvd!p@a}l!)n7oHQvEj9{9J^b(gj)hZ|?%MM^>@kTn9-0H59 zH^pP=7aT74e=&yotYwuBKHt};9qH|rZvN1R)TRjopVT_HRUq6yA-Q2g;^(U?riMFl zZ^qAYO;oonbGeExmrF_z`4cd(kBCJ7R>?B{05r%SLFDv;$T8cIEk6KyD@wN0 zhNi=Q-Eb|oO9g{nIL3z130{_pLToqYDwVtiw|8V135 zKmgS-a86+SDW#M+JUtjZ$r~9i zXzd`%-HW8u*@zD>7v1v9Oh$ixPRJbx4t3m{s+?*tS;B0bxdN414U~V^=@0Y|W^8>& z*8o@+OYFgctd|qz3tGyTUX(wZ!j72@#-TT$LR}Bk4py( zSL3u5(5Q$>Lg7zp$vNQUnOLS2AFOo+argoP8}c5VgIJ@*Bsh_GF{-|uuzVvB2DNOy z@YIJEG*FytdEp!)*oV|U+vn?PlBA)7yLKFZN#^q#QYp6HFys$Jqgl)6>ihXGUQB)c z>g8IhzFll!^y@(=-PEXNj!bjA^~0kDm&uUZfL!Pj8a_Cn9q*Uk0Ks&}NI{q#aFo5} zxFH8nQE>(9WTwgi{#bWb^=EAbq&yO&gr(AG0YGfo9r8o}m=%Ll4wXjZv%Mz<2nyeG zv(la5fcOB^1b$M8E5DysjoUW1(BGTq&LwjKj>7!XmYuH$U`x!uwy4Lh{wo!LA)6GpxY4r zyw(X3NRe~D7IJY94(5lp(K`H-_rO75&WXu4;$TSTShA|B0?B%5wbvUiU|XBygc_3YQfX<&q-HU7#103*8Rqr2&Vq zq=om7Pt)ogUWgBJA3+gGCkS^QrIJXrpXfrp)Oz04o*&Mx+y~SJT;Z}e>@H<{c*D3? zRQ0#+v2g*c+O{?p4n~PO5zJclfWB=OO)MYaySz2-OOD|EzX|oM=LEQIr%r zAN^t8<*^SV6!JKNJ%g3Ply^Af`#-%0J;3l>HRxH3B4%%oO%~vN_Q&u=ywxL z_K(FkUO0Hu(zX<>s73SWvx4cBbq*?2pA^jO>lXPvCoy?Y2AZ8=^{&$DefzgiHCc>*h}H*~(5Eh$NTif{i&XD8d__XpK*xU}E| zpYQmEY#k`oYpnKnyv@9 znPq;7ec~R@`tZUU|8eXyeoR{UP?e!-5^p5~C-+0;Z^~nF#jTmC3B{P|$`p1kgObIg z-9wE`!K)mZzC|e?}R?pPE-ny<_!Uy2*#s~mQ+l!Dq`BY+bak$jQ-QX zAbD3%=6;=0OH~fR`FyEV`Uo`8v%odH2wls2MBCbpSZg~JGyhR-iR_E?q&*sMf1U@t z0f=H+B{4&_>||auPzcT0dCi1=1Unscn{UdDt`?Jn8!2lTy^hz06Du5y{0+$D{n8l; z{t~IOanG|Cp!E?a`xMmefOAkk0?v&A8keQ-z+;$hU^HN=U}K&5myHpwB&0YeOzoC< zQN0-=n6HnH&T;DeWx$dd>6G%=MKeN!Zw!pyBFR!4W9e?x2fM&E;X}ePh{{JOE@^{o z(f-V$=D;Zxb(KD+>*xCti)CK=jisrd8<`S*dG%OPOl2DiLxYaom5N?C-gT(3t*kj% z`SJbr8PfwV4Q0;i9xYvg^2SF{#gd8 z!E!yKU~hBjs1n9G@={5YHO~{MVpgRbQOt>&5@SyVePmEDNe&NRxlI$dceqqh*O_}wPI_tXtmyWM zzn$ry>A+U$=QF*n@3wu(QQwu;{_(7u z*h`|V|Lwl-)dr`<_*NR9EAibv7uTt1Rx~6AYmhz9Pb^x%q&tOV^LZZDUV?Pe5k_D~ z03Uc5-X_Ai%opLGKB?vNB|97*e{q@vfeikgIK>I+kn(35cW}ZjqGZ2D8j+p}%UbGL z);jt(#q&S)o8*aPk!!FeGYI6U>?Be$ZIpV=q&cv3p117f6;-A*eU?t#i9(j!7lJQzMrhz|4mmDvH?UTRVSrJX=`lJsZDQ_gZ!e)T z?&#)9Y@9NyVPv2S{tG`K$YSPSGq$~!PSeMi_9mjNCxMPNK}v&gQul%pjk^$i z1#}_%((LBuaf3a+^MnuQ-+8B}Fj{Ct_@rZ;V*=wa_!-Kvy2_?)5h@4dCrt}ad_!js zZEFcL8+SR@*~v_vMOF5DQ;4*NMD+tvB4NO^&jpkaH0f;U-2Qm;hBT-80|FvBhQTYS zD|CCRd0r1V4R*QUPQbv2#|P}n@!n}oLqO4U&hajr@}H;l*E{X^ZhxbR(--@S5#U%; zxD>m6f}`=^$62B2ZOlOMi6(_l9#bZ2z&AHk5MWg_Nz6I+b(7rZT!PRXaWD@L<@AtC z5l!6iI~V^gnQyxS#`9?a(# z0~U7=lrq0$s>Ti?avr}d^(ndny-@BvP;ov2z6VWoNFksNib{I0^YXcKPa?9ZwEcg& zrq{S?)lE_{z&J0SSvNitpq}QaC-M{w@**XzU4C9ma4U{^lHsnLgF=4{);b5`o=aIa z^ONHVZ}H|u?;JX)?E61mKi8AI$}4gAYE&i&ir0vE02ya(wHU4sywGt}I}Mt;nPL$% zw*>Zl3{*Gb&EA>m9=i0(^IIv^C)@Uk+nOO+Z@N?Qe99A4>-zTKncD#6KAjEl` zOC5r~{a-e3PE^#8_kr62z-dp(kPR|;B(r$&V&lNbh)aw_bQ-%KJI0?iB(x15{`6hG zToA};O;gSdRXn~Ed+zV;4Gi1PYl-RHqoeS%+R%H_V3v7s|Lf^W8z#2%ulaK)!h4!r zjq6>yEEhy|FBo;_2He~Xi4{5=ikZd`n9&oP zdTLZxj~01YKA#j%>#{fCl?$UO+4OliJ_|v1pR33xi|LByTjhc;{@CrVl#`nRbU@vM zJ9oZBPO!s~L7%V1VcN6dgdZ{88oK=L4KF>8UuNn{tgr%Thiy(dvZNtYOh1>+?!VOM z8!zIhobT<{l#rc%kHL@*lI8n8c{|A7Z%0ARzX0ax0O>7%athq>_G8o_) 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 Q^3R@b2)1Zk0&5+MNyUSR-V=Sej)GD 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�{XQapLL3s@DiJOewb9vk!Lb} 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

    kl z3iQ@lHpGytMUyZQ6=fS_4g3KR|dmQWL z6xv&5HAR@RCon7|V@?{=bh9R+sthxB+Ay$AdIzO(v>8AoNQ!rHA}v5DaPB%%Q~;|w zd>=@18*5Q9_D6_EAc25|3ASLkuK>%EiKu`%0$TDg#|SpyS&O2wvYt;eJFx=km{sEl zWDF}gCIoRY#F+;`Ip+h+ypvP3XTHhb-dM8Dtet|^)T9D&9@`k2&Ymlobu%NpDi)f^16eUKMd<|!&9or7)bQr2<}jJ$((M}I-7W^dFyR6w z=Olk48xp(L)@U?mxUBu;m;YP-ubNPTu5Cu*_;1m8>W{n>+%@uf09(Hg*s%i1o>lXC z{I_Hh+t%l=M&k2cgnEjggR)B)C{8;ZUx#i|Q&K>s)I3TzA)<+pnXWZp$SV_;EW38+ zKy+!cVDxfTQh$bLZcfD#?IYW4w=&jY(Zy9^zTdEh_~=h zUywYo_u2<;(hN?p0tOb><%X-P@_hBZfg-m__#OBNUX~fPAv-507#xbe2^h-}!A!jc zff7Zunaf$#SS}lQxl_UGFDNdCef#uf8QC~^kYL-VN@5bqgIQ*_m7xx5D8#TkJ%69= zq|@rYFt_vzjg8z$bONbxl9I}8z|-!bMJ+BWTowTOAz2IU@+GVsA^m=*^iv2|=9<6> zPC`}MY=|BA4#v7SdK|_1fU8}p@Y-fqZ}xsjai3bMBcjL|mC^GN?|2Zk3zuN#@u4jr zfn80s8}##iM)>%i5!cv{th`LA7h=-cmy_pOTRsATv8HqAB1{}^0Kh#dgBg-EY17h* z+(FVWTl*vT0wexCBoaSHxaI_mPc4cg>JXbt;aV$os5($7l`C-4EpNKrF=FV+{g4=` zVPa+gMmGfsz!Q=Fwom3xocNh|7}=L>B-Y#q0(Z-d1^0-6lEpRZN<`z!K|}b5L?ro^ z4ODfKE+J#gG$kQDqjbkN-d=aQ2mMNYhe^v5@5wU@7Y!w~<6v;JxlQ!Z-oX>k6;zDN zQ^U$w3r9JVD)rrzt{2+{`h|Eze|1uMzTEEhX{0*uWNYwHu^sY7>rw_?3Q`yr6KT{h z(Vs+ie4IutjIXQnP(EMa^Hd24D#dBenhnB_TD)&LlCGUl6586@5>iEb_VJdv_BZ9T z5z53diN6o4uf1%%f9uSO3UG>idT-5^k|Vb-{J*@DUv}R#M<5ES#;1X0aHz^@X(VBUvH4XuBFr`y z6{`iZPPwci8&DPfgM&%^FO!iMIKRNTmpo%KhQRrKsYPV7kV_f|>e^CCHI9ia1xgj8 zaZ0xRy)W~A&PmLdFbOsJx5BH&5%mKpi z5zb9een9r$?P$c7=hAkhN8rDlYZD-;BKAma>>bLytrF8 zO)d(B*rB16Yfy?X%_^VMxcc4X;#sx*@_?!7g>|nUs~k1OZWg$Ek)OXC*w!-Et$N?t z{7L`$qerI`gyrRwzQ-x{*&^{yTyhMI(gecj1&{H#Y+*kMZ{6hm{`ixXMP&KI<>pnP zfWU<+zr6!yVW522NFo%0>RDl|z;HRNfS8Mv*txedzn`kFXS+Ge1bElCgDYVd6Jwrh zr-PgGT;?WCRwFFKj>Gu60E4hh=b`kRVj5d}+hZus&r>WG4hlpQQPwk;R<%9{MSn82 zvLx^*8hW7jkio18=3g4CnLdZ%Zb6`%}1>{ zC0_LFc${w`%6)lBO4T?DMDH6bT4? z-^5RFCT7PH+P9$`BBfYi)OP}j=#SQY5G2)vl&m;a2FZ&5=8+q4z zQuyt zM}tQ=i|6>lu+CM=F*!HOT?WZ}tK@c5nms!k5K70bs9C-qGAk%3HzvY+&oA&44<7RM z^o(}9(}R;FD;gYTuRWeB#RM}QL^hkCMfU4}ujk0fIOy&~f^(Rih%y*CMqJ-J$7KqE z=$%zKca>(CtfNCzyJ2~@^qC@ zAzp;cxkzXxe`4*A5HqBr%HS`*HL7fIVwNc~D%+kwHpd`FwGPj-na(vgZhh}qX-Q2# z$1w|$l$?neO?MOG26RkqWvSHnjJy?aQ)cH}YNw4V=k zhpPff?P5$WPw0iTRR-3Zgk?=9&Ber6*&I>a80#ZX`}g+X|zsf%_80wrgQOWhBs5~|f2R1_+Me5VU>6E9fOCV&-e zCdFnKJvK~$L#{P)BVh<4(?gUkMlQtwD(x2pc282{26T=MK%}97hZXBqy{C**3}gTZ z&gVik_^*<#KZA^MD^fiRptSjj#ge_~6?m(eYXPhU+&a?^#KFgAP5reN9o&B^xiP#` z^Qu*<8JJ6um>(Y!lQDFRF`;bM*fUol|aJ92SKs!u77McW0XZTS~2zIe2@RI(LmFZvi zclkc5!~C}aT3B>mY1wrnwAn6P_I-<@Q!}k*qp!+RlTM=I5+pQx+O^Bf^hHY+T{GKE z`L>u@qYYzdb41Z!XUFD~EcI#oq9SsCATGT}bHIF)c^4bP3~-p)7WCaNOghb#MW$1s zAo6!I?jowX1bG0$Kk`@bXPlr?3KW=FvR^Mez|*clj`=2Tc*$}gu3APhv`teuLVHJq z2tphaNxyfQd#W2#;7=mof->AEk%M^|#Pl2R4t)ZO#=oNf{)CzBNhX>2Pr+k*Z_%&< zOy=0H^Ct>4ODPlkJxvntG?|4Tt19&`unPnV+AFB?BC^^f?Z?9=lYXiOxef`qZ>->2 zuzLQGHy95muw%{~pI_uXryF!GF2E$BN!DcLV^UmX2Ql{)pFAr`^6VXmOt3qJL3BVT zFtD(<)WAw$^va@9mmmYb>Wf`L+sqR{FatgS2E`Fd2RybHK(|QZoS#|p5boct2g8xD zL%qR7i-696nN*{}d?h+2@9%J|v8hQ?&wl28k&e%NM-nb%pDRnB_x~jv86526JW7q3>V%EI4_@Y6yxyh?#%ii>_N_@e zSr;-wWh|>TDRH)yt@IpROnFY7hg`d1a4_$(+a94gv$ikhqqxOY`RS^&zjJ~U9(6Ie zcS_+t?lkD|-r2>asZ4`0As~sS;iaL+Ab)o;R5TuP7`B4?9^c=fe%7xS@&>ON?}1`* zZPl2f7C7Y4^RP6sB9lX*_}W@4dEyy)YK56N=r|> zv$`>Me?3LLCmfG$;TSoGHrY|>m6EXZ4w-T+fmJycb;TK>o6}D4HT;E3$o?qDU5WTD z>ZFVlNaXJ1S$>We4#&| zGtbu|S|iX;OAt_55Y-U?l-l~67pGkz?k>WZ^9t*=?01+3$U`JT~PNVX|+bPS#L@j zo}#mP&6)FTABg4J%~{v4?~EZ%O3^D;)Ts*~F}+}1aX{L(u4((9o<0r=OJOq-b^F@- zJ%?**=z00>mL+4x_?KfpZ?NxOc&NX^M6nlDWz$8`^bqCb?*eo4gN&|T*%^}uF0OPm z)}8S}?*sXclQ++BTl6Z#rJTJIge`^#xl2|Kp&zz@g{dP&tNSiTat?iAbik`N&z&nK zc*j4qkoP&BcRiu#@^kP$Rm7{l3!LjiyhFH;i%=KFIO&y=SoY>oU~(fK{`7qJ5jxa= zK$t-(Y$`|GDo>@8QR>3!T&^g|M#cCy_>*ooR=As)=3KxPzlwll)w|QQ9u4N&i#w6d5 z+&*sZal;5s0Q9i5uQx!OT0l7FXdHCd)^ zx9Hp>i1Jvt?LPvR{N9${-Yl{c@nlf(6-3AZ&K-R(YA(irF>fsrzP|_^Y<#=IOoe%h zzlkW>Vs|`(Z{~JEe!hQRfpdSTH~H5(@bq3*QP8!hy2MK=6VG->byzvfuCfijk8wTK z#UEV#?5|sA}YLo}ARyTAL;f8fRbHpp_;Be=gU zCZ&$sI3b3LnkiW?7xDg`ZB!RDFpksDU$T7p`>r;nV|;a z#T^<5P7+PxUBEjJ$SR#}Hk8+TJg$RvcoFc{(;GWF#O{dZFiol&A_VR^j>07`RW{#SSRVmT$a*TE3E$bw}G1mKX6q-*WN>@Ew;ql4Gq-2K{%-H53f zuQzw(3!>%6Fuu2-nBreTp}bd?52Ht#0s+9##D~x&B(Z81S^l;I>;JLi%yyIeBgr%b z&ndoe)KasBUydTfYH%#O@NEB&$=CHFK|dVy$1cS4c^fc8H;^I!(cY~*yXPHec-fs1 zmttmrgc$bCP)fgUl-rEq@oDuNIsgBrZ56ne=^_7;!{N?%S3r#QbMB3Xvd7hMAScRl zYLi*ugbF!c^A7tpGIybM~lo=Nvbq`I)b9DPl-0O$2-NpuQ zs0=vCN=&@f7PGi!s>{?lvatlF3P(amc|Aj5p5#6KvO*8%%=kk3+ynCO*+W?GWZ6u? zK(E$XRU*vPb#*@2l~>Ttss4mY^dFs5U7diDFfJd6 zT?fXmy`4ol$1~m)=`3>0`X1kCN#$|DI~^w0zGhCQC3NO(PgN|CUDYFK^+_Nz-U zV`iaX0``GMxvI7v<;PF-Z2jn1=3%GLH~XSnz>f4Q#AFXjsnlbMvYhRU;dzdH!2Qq} z>K}+8BTwa+H6GNBR2lHQE<_$*Klbta?V0nPyib@?#2?mn`fsv|{9TaOLH>=&;wjD{ zsp=5`=^tEQm~#y3wCv=iH{Okq%fF+E&+2mM_n6oQ&?csG!2;Xr-)5m0;!{IH@U}go z>4!iH(4uIpakM8I>xQ9WBm|S9?M{w2Jc`Es1bwq8CaX}Npx$@#^ei1{6sR)Z4|G>w z#;_vHVneHbhy>YSLe)8!%GY;WQr+a4WbXZ$hBik|j^l1TmmgtVC|M%YWaatYh(m`% zDR~ZPBj-X3W);n`k6&Bv>CX)MPRP8+1^y?H3W6hsDmI#1se}jaoq~xwb8hl^XfRww zL!6V<^#dYr97^hXk*rXa0>eKEL`aX>5WYP+TWD$a`K%E2{TU6?ANb8Dz-90^f#STF z@y-W{Q5{xa@4;dC_bQu>gt@+P8ySaVE?jgm;@DXVGQs2qNTrY}BWQ$7q<}RCaepHz ztt6_dv$nQH?|5QC<>w+i8VBMx(jcXR=xev8DBGj?Ss&MZjf`*8Q23%a;HKck$;o21 z*Q)cJC&)dMaqMupgiN_P`Hnao2JM`J3I3NTym;+`(ySNA_h;L1-xI;J4fPx&6_k`u zE+}-9yu%l^oipQ0DoecfLT6vJPciendds9qiMC|2D3(wcz&p!RG<9z*5!?a;k&dl3 z1*I!evb@}>QIGtk(0=T;jt;?Vw`b$UXY=B5A0faPdDq1FZtV-*iR1~A;X(!2?ly5o z>=SanwO!#_#91p)5p_7KNVmmdiE{*crzA6*0JKj-(8#&c=jaYzjf%A_idJC<`0oMp zSw>T8GeCe($SLP)U5MO;?AiZ;%EJ+yvjI%QM1OoPpin6g-mH^Y?Q!bx{zGJGOwPBl zJMdz0b@p>XPG_u{~=cYjHS}chQIh`&7^D)Y!)f~Wl2IN%q$k{yAhCs3y z3fHfIiEA6Kw+<$BJP1R64w?6lppE#`WHRX^Mz8_*Epx`@AddetC9<)E&qul4E_liM zLWnzU?klvXsU^wFsZOc;DGD;a2ZO^w$E_=#R2y6l;RD?t*?H9ztVV!3fAmps5#G^1 z779=I1!ZlCi|3$^NA)HY{g?Hdx;}Yfm`8)$6Ttz~PVQkHtUa8eu5R+C_T4kBFuBtk z9VfEdcug?vt#yj+tC~CVKrDF$LPNLcG(Ew}Qj!AF1riNFIO83S=l*!&_20Ff;={nX z8&QS~8RT-{dEF?yc>#&PJSZwJ_vhN)pO0B^ug_OtWh|FLF!d@`(+fm}dSvas{_V4K z!+LgAl{Kod&tP7?23gYIg%iOQq>qn;Qe{Bd{0naLT8so7s!3otBNN3cly$!~&Ld(` z_1TaHzWds56slmXlZZ?De$t^ue1-2Ab6+IUC0O*ui$*XAo_T}Y zx`K1<7GWtuYe6YV|Meq;;YL26Gxz_pf8XtkU0?!D0TiekfI!-Np2tC+U3w!l0ukPr z&+F=;fW}Pb8G3<3;HDrUKLIP$7v~f>9?a}J^0Uz!&7pYhHUy`-7?g}LjBbwklYb|Y zOe6O@mp`8~<4YQaDHrd_rvsQQP2J4N$_AkR)#`b63)aJSoMFHd_5O`J?UtcW!6D65Ug(io)s>hkO@~K0n_A1~>rY z?HD_yu~c&PqO!bBGy^t|re8fWumZ){KjIkX4Zy96;;KHy%5wgYr0s>M<%gqe@}~0& z^8LgBkG-k8Lefps+#+ko3wWeeH1+~U`;!aDO+o>PEX!0EiJ7;ALgkXi&jVxB zB)t4bcI@5y<_|#o_-0p^ON&{@P^>z^Au#!H<`JS%Tg;NsbmathFKHv`uaVnhSkcrp zH**sTE54^{+7k$SY~}RCEr;vtebpB){x>ktU5)WJ2JzfqCOGQpSq?{pJr-iq*eA+vD1QX504+#3q<%dMX@^HpGFCVU>DiDyeUTc4PaQ z`<|=c&soBaG-#yZCBKsa4Ln$LN|V8^E+p9$Q&kV`Lh?7Vgv`%(?QrWMh!-+H!_2f0 zP^lRj#LNK_zf{OFFWu2YnbFazEQ{ zJ-|5vW}-g=muwv3g)i!~ynQ~@dM_yR97rbUr;*5c9~L`42$MAH@JY2B^`c;4^)!IYK}Chr@lhkG^rRJV#|h zG1d10z*sByz5IN`nTD6NkxXbuj_x|Vu{i5+vB+@;hN4%;QcMlvV`*vdABQ*WVOV~Z*`oi!t%zK z7dKt!%vV(|TgI@ux(QZw8(g`7n|8dt_UWfj3rDob;r%SFy{Q>muBr(SPh1#(;9wy3 zvw$8NVh82-S;_blxJ6#q`+MuBA>82_e_q9rKPu`k%;YV{XV8}j1#CD2bSsYO1jcQihvkR z%_}k&dB`DOhh70cco=>?1i9%xjNu7@SCY1+w|#&4U0i2$DCZbs07ZR;<&;O_aq7RB z0Q)LtS0AcX?>O6i1CAEbIAati&=;j5C^3SPcpS>Nne%=TpXb#0o~?m)w~b^Up`e;V zf`y+LIeOGQVm~-t%q<8@#IZ{HPdwQ+yozf;dG{hBR`VD-ko_n!>w_t2A9!3vXu#gQJJGBLNYh);yc1lkPV=6)Vu+>qg=#l8-U;23^c?N#7=J@v*D#HR@fXf z=3n0c&o|Tg=_Q30*$m#5e?u00BlrmlMGACu6rJPiieiq5%&e|C}s`_)E%jZv zgfqtx4~F~ndW}cnzHj6(+ z16~LU{Lc!Q5Jpc+ma$0Q*rslr+k+F&SSJ|@j8b(Ib~L{YBBYN{gF zItX?(KS{=siOELkHmHZzqKbM76r!#5^1J;sz?JbD45Mo4(=02 z{WgkaO5nwPV5qTwATGF9qnQ%Hi1*kfwl8I zwGMiU+dxe@CLyv#8&M&q%l&PNs_axXB?3?OJmSEJycls<=!<?&!ZKTssKnvSW60oc zeR|PdI|jlR4@KCnl%l?$P_-h=;H3ya6{|6(cB?nK3{^@0Drx#qSW?&Yfe)8JxcaxO z37Ab{2~KNKRc?nV*zR31?Db}vMvNV&GH)P($Y4<38tzLt2KChSJk9nI%rJ`E{}Y~~ z2gOg-;4In~?r%KJcxTQ-KD89Ua0;DYINzV=5DhC<3>U^j+8%@F2Utem0BFMoP0+Tw zqW*65#i_~G;{o~GnDd+@zP6_)GQ(!(Z!lSeT7Y3H?SjWL@){$4Hu{6oR>b7r$50rJ zCH1W_i#s14fj9%uWg~kR2-9DRgr8sM{8H&_c+|Uw-D=lJf%o)Ln$Sb z;ZOH}<{Cp9C-Iy%V50Por3kyfuI?m^F7x+q!soKu%J9gzEP?TD?~DHUz)B>Jb#=^g_PrSVt-q z2g+u1U$B>~q)zL%ud-icD&7_|j9j=MW0AbBEiAR73i{+0?2($FEZzv7-$`?BpE!C$ zPxvm*YWV|_kCU-tD!$;3`^RPW&3tyW_1aLPz7YA7`B7c?9um_FofKb?`TG~~dCtm< z(^PInMW`Ho^fT@3N=74g-ZToV_~Y=8y(Qr5BqhY%MjWi?+*LHs9f{KFE|3~<@OrNY zh2cCn%MM9OtSoU_3HggrNSuP|WI|>)&iOPY{Y!0AzCFr_@RBc94eoh;F!|0U6AMq1glVQ@mD2x)>PX9`Mi>f*EX!tSKRAWr;>8*q-4m?&5RmKnMAw6HuHiGCR)0(0%|j z_5m9oIjD)Q|A)HwfRCz7+lNn|nwd;`PauR|1*Ai4Ac}oqEo*n}y4GD?^|iOPq1emX z8`w|;r1u_5fRK=0Cz+P%GpB#oGcYkAzVG+j7ymDupE8q~Gv_>~-OpXF`?{({bWWQ% zx<7h@2Ot+Qnd9j*kc-QRN&4JStZVhC%uGu6?vE%~002M$NklKNd#x@f$y%9(Y^+A|}NnG6sPUusBMi~v2hKU|g9_WoK<4viK2#8Z0 z`0gj#%zd?H!@A0gPZ>^96UQ_jv1lv>+^(bLSh!|kp`*6^sEOsKeo|gx$utSyB#JEYAzuo1%#6X)0p?Vl`XyVTwyuP>V zvByqBj>d zBRrd8V1&EDK(=gZuJz!C7FTgpR= z-8NT@KO5gic!4dJWBL*l2%50{-xRQt<6LymKfRr*;(p;PMVH?Z-6UC=2Fw0#ADZOhAsu~Gs);XxP2qug8DB;NuS!#^Y6I)smMsjt=69nsB19%P+0ZH?=N*Uc%d)cJm+5X?m&VS&n|qvHWO*60^8$?AD*1EeDC=CBFlrpbrotAynY~A}tj|EyMzu zrCq*gc$r{j*LP^){ivR|6O;rDE9;lJ`$4dJI^;()+l6+0$tT(f;Y^jN-vVeR37rdLPJd-hrXx)C9LDk;+YR6zW0SzhKIh#R;q0uUS&4a1_BB-bUc zume7ZXMS$pxT@72@*adE*Dv5#0nz{CmjAW!CB{rkXRlIJg+mzmjn48)G4J^6G7#gy zd+XVL)5=91CZjc*Vw85C()NcU5n@<%zt?+EWt1mXKesK_oX2s%d$EFPJQt_`w$mTJ zh~P<&b@(KgeEEPpFQ8%)ay#p_&Oimr83%deu_9?$xvIyg%9tGb%uQ!*KNI;dSd)ZN z%9?oAR6wlmfpk6Ku=+fxOMH$zkK+GT;CqNj;TPa(E@l|Yini(TNbGM5 zFxKYb*;KT*aUv5B@@f}RoaHWqt{+6Gwu0i=zwkU)2VLCNXda1N^7!M?@7{W@g-jZ~ zk40(N6@t+)p~%IotSHR2^3n)#_Kh^HTL}7&rRy z$7zD5C^Z^Wz(`dHYW9#z)vNBFP;gA>8rdiD@D`)n$50($(<%1j6rOEG-@|?WxbjR| zN=kvFp+WmS?B@z_0sno$#79cGqXL+V;iu%AQVVS;W@bu}1g>OFB2m+lCX-$uDs($SmQyl-KRJI5Zx^Gc?yITsWb2m3t zukD|kn^d-@*0=dCu^WU};SoQC$?E}L7N(J0d-6I2IIk?nv_@2ZeFi&YPAESlzdVUw zLz1@K#PEZPY^GvS)xQ%AhU@J-^LLbIo12^4W6^X$^u@#ykZ;pzMOhGRJ#?_L&QCz+ z;W$7BC0*U%O3veGjEBpa(dW_?L zx@}Y6$p!-}41llnqtD+_9aW|2m~+!uufoH9>)9;n;u1ymK#Vc01}OfQl^+%*JS2i0 z9GB(TXDKw%l$rf6gsG2!==?g=uP>ry>M?7Ea7|^`uWT2jn1bew0`dTnlg}y9^>~Wy zZky$Hcys-sz8XTCs!BYnl7MI5H6>J!L8^m@D->+6LLMk9CdqRtqwt;-rk_W#f9~4a zN;1nOjKO1o8dQ|z9+W9%x1up{jHu6aUHVYm@^cqihZw%

    2-cn6gsGIE2zEilUdg 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

    Z5%j-C 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%0A=e`l9AQljxQ1J>rUJsDX<`+@ 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 literal 0 HcmV?d00001 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.css b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.css new file mode 100755 index 000000000..4d410b12e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.css @@ -0,0 +1,41 @@ +.com { + color: #93A1A1; +} +.lit { + color: #195F91; +} +.pun, .opn, .clo { + color: #93A1A1; +} +.fun { + color: #DC322F; +} +.str, .atv { + color: #DD1144; +} +.kwd, .linenums .tag { + color: #1E347B; +} +.typ, .atn, .dec, .var { + color: teal; +} +.pln { + color: #48484C; +} +.prettyprint { + background-color: #F7F7F9; + border: 1px solid #E1E1E8; + padding: 8px; +} +.prettyprint.linenums { + box-shadow: 40px 0 0 #FBFBFC inset, 41px 0 0 #ECECF0 inset; +} +ol.linenums { + margin: 0 0 0 33px; +} +ol.linenums li { + color: #BEBEC5; + line-height: 18px; + padding-left: 12px; + text-shadow: 0 1px 0 #FFFFFF; +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.js b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.js new file mode 100755 index 000000000..eef5ad7e6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_static/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + +

    +
    + guzzle +

    Guzzle

    +

    Guzzle is a PHP HTTP client
    & framework for building RESTful web service clients.

    +

    + View Guzzle on GitHub + Read the docs +

    +
    +
    + + + +
    + +

    Introducing Guzzle

    + +

    Guzzle takes the pain out of sending HTTP requests and the redundancy out of creating web service clients. It's + a framework that includes the tools needed to create a robust web service client, including: + Service descriptions for defining the inputs and outputs of an API, resource iterators for traversing + paginated resources, batching for sending a large number of requests as efficiently as possible.

    + +
      +
    • All the power of cURL with a simple interface.
    • +
    • Persistent connections and parallel requests.
    • +
    • Streams request and response bodies
    • +
    • Service descriptions for quickly building clients.
    • +
    • Powered by the Symfony2 EventDispatcher.
    • +
    • Use all of the code or only specific components.
    • +
    • Plugins for caching, logging, OAuth, mocks, and more
    • +
    • Includes a custom node.js webserver to test your clients.
    • +
    + +
    + Guzzle is now part of Drupal 8 core and powers the official AWS SDK for PHP +
    + +

    GitHub Example

    + +
    <?php
    +require_once 'vendor/autoload.php';
    +use Guzzle\Http\Client;
    +
    +// Create a client and provide a base URL
    +$client = new Client('https://api.github.com');
    +// Create a request with basic Auth
    +$request = $client->get('/user')->setAuth('user', 'pass');
    +// Send the request and get the response
    +$response = $request->send();
    +echo $response->getBody();
    +// >>> {"type":"User", ...
    +echo $response->getHeader('Content-Length');
    +// >>> 792
    +
    + +

    Twitter Example

    +
    <?php
    +// Create a client to work with the Twitter API
    +$client = new Client('https://api.twitter.com/{version}', array(
    +    'version' => '1.1'
    +));
    +
    +// Sign all requests with the OauthPlugin
    +$client->addSubscriber(new Guzzle\Plugin\Oauth\OauthPlugin(array(
    +    'consumer_key'  => '***',
    +    'consumer_secret' => '***',
    +    'token'       => '***',
    +    'token_secret'  => '***'
    +)));
    +
    +echo $client->get('statuses/user_timeline.json')->send()->getBody();
    +// >>> {"public_gists":6,"type":"User" ...
    +
    +// Create a tweet using POST
    +$request = $client->post('statuses/update.json', null, array(
    +    'status' => 'Tweeted with Guzzle, http://guzzlephp.org'
    +));
    +
    +// Send the request and parse the JSON response into an array
    +$data = $request->send()->json();
    +echo $data['text'];
    +// >>> Tweeted with Guzzle, http://t.co/kngJMfRk
    +
    +
    + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/leftbar.html b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/leftbar.html new file mode 100755 index 000000000..e69de29bb diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/nav_links.html b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/nav_links.html new file mode 100755 index 000000000..d4f2165bb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/_templates/nav_links.html @@ -0,0 +1,5 @@ +
  • Docs
  • +
  • API
  • +
  • GitHub
  • +
  • Forum
  • +
  • IRC
  • diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/batching/batching.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/batching/batching.rst new file mode 100755 index 000000000..57f04d80f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/batching/batching.rst @@ -0,0 +1,183 @@ +======== +Batching +======== + +Guzzle provides a fairly generic and very customizable batching framework that allows developers to efficiently +transfer requests in parallel. + +Sending requests and commands in parallel +----------------------------------------- + +You can send HTTP requests in parallel by passing an array of ``Guzzle\Http\Message\RequestInterface`` objects to +``Guzzle\Http\Client::send()``: + +.. code-block:: php + + $responses = $client->send(array( + $client->get('http://www.example.com/foo'), + $client->get('http://www.example.com/baz') + $client->get('http://www.example.com/bar') + )); + +You can send commands in parallel by passing an array of ``Guzzle\Service\Command\CommandInterface`` objects +``Guzzle\Service\Client::execute()``: + +.. code-block:: php + + $commands = $client->execute(array( + $client->getCommand('foo'), + $client->getCommand('baz'), + $client->getCommand('bar') + )); + +These approaches work well for most use-cases. When you need more control over the requests that are sent in +parallel or you need to send a large number of requests, you need to use the functionality provided in the +``Guzzle\Batch`` namespace. + +Batching overview +----------------- + +The batch object, ``Guzzle\Batch\Batch``, is a queue. You add requests to the queue until you are ready to transfer +all of the requests. In order to efficiently transfer the items in the queue, the batch object delegates the +responsibility of dividing the queue into manageable parts to a divisor (``Guzzle\Batch\BatchDivisorInterface``). +The batch object then iterates over each array of items created by the divisor and sends them to the batch object's +``Guzzle\Batch\BatchTransferInterface``. + +.. code-block:: php + + use Guzzle\Batch\Batch; + use Guzzle\Http\BatchRequestTransfer; + + // BatchRequestTransfer acts as both the divisor and transfer strategy + $transferStrategy = new BatchRequestTransfer(10); + $divisorStrategy = $transferStrategy; + + $batch = new Batch($transferStrategy, $divisorStrategy); + + // Add some requests to the batch queue + $batch->add($request1) + ->add($request2) + ->add($request3); + + // Flush the queue and retrieve the flushed items + $arrayOfTransferredRequests = $batch->flush(); + +.. note:: + + You might find that your transfer strategy will need to act as both the divisor and transfer strategy. + +Using the BatchBuilder +---------------------- + +The ``Guzzle\Batch\BatchBuilder`` makes it easier to create batch objects. The batch builder also provides an easier +way to add additional behaviors to your batch object. + +Transferring requests +~~~~~~~~~~~~~~~~~~~~~ + +The ``Guzzle\Http\BatchRequestTransfer`` class efficiently transfers HTTP requests in parallel by grouping batches of +requests by the curl_multi handle that is used to transfer the requests. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->build(); + +Transferring commands +~~~~~~~~~~~~~~~~~~~~~ + +The ``Guzzle\Service\Command\BatchCommandTransfer`` class efficiently transfers service commands by grouping commands +by the client that is used to transfer them. You can add commands to a batch object that are transferred by different +clients, and the batch will handle the rest. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferCommands(10) + ->build(); + + $batch->add($client->getCommand('foo')) + ->add($client->getCommand('baz')) + ->add($client->getCommand('bar')); + + $commands = $batch->flush(); + +Batch behaviors +--------------- + +You can add various behaviors to your batch that allow for more customizable transfers. + +Automatically flushing a queue +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\FlushingBatch`` decorator when you want to pump a large number of items into a batch queue and +have the queue automatically flush when the size of the queue reaches a certain threshold. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->autoFlushAt(10) + ->build(); + +Batch builder method: ``autoFlushAt($threshold)`` + +Notifying on flush +~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\NotifyingBatch`` decorator if you want a function to be notified each time the batch queue is +flushed. This is useful when paired with the flushing batch decorator. Pass a callable to the ``notify()`` method of +a batch builder to use this decorator with the builder. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->autoFlushAt(10) + ->notify(function (array $transferredItems) { + echo 'Transferred ' . count($transferredItems) . "items\n"; + }) + ->build(); + +Batch builder method:: ``notify(callable $callback)`` + +Keeping a history +~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\HistoryBatch`` decorator if you want to maintain a history of all the items transferred with +the batch queue. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->keepHistory() + ->build(); + +After transferring items, you can use the ``getHistory()`` of a batch to retrieve an array of transferred items. Be +sure to periodically clear the history using ``clearHistory()``. + +Batch builder method: ``keepHistory()`` + +Exception buffering +~~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\ExceptionBufferingBatch`` decorator to buffer exceptions during a transfer so that you can +transfer as many items as possible then deal with the errored batches after the transfer completes. After transfer, +use the ``getExceptions()`` method of a batch to retrieve an array of +``Guzzle\Batch\Exception\BatchTransferException`` objects. You can use these exceptions to attempt to retry the +failed batches. Be sure to clear the buffered exceptions when you are done with them by using the +``clearExceptions()`` method. + +Batch builder method: ``bufferExceptions()`` diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/conf.py b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/conf.py new file mode 100755 index 000000000..92bc46bb5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/conf.py @@ -0,0 +1,94 @@ +import sys, os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +# -- General configuration ----------------------------------------------------- + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' + +project = u'Guzzle' +copyright = u'2012, Michael Dowling' +version = '3.0.0' +release = '3.0.0' + +exclude_patterns = ['_build'] + +# -- Options for HTML output --------------------------------------------------- + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "Guzzle documentation" +html_short_title = "Guzzle" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': ['localtoc.html', 'leftbar.html', 'searchbox.html'] +} + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Guzzledoc' + +# -- Guzzle Sphinx theme setup ------------------------------------------------ + +sys.path.insert(0, '/Users/dowling/projects/guzzle_sphinx_theme') + +import guzzle_sphinx_theme +html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' +html_theme_path = guzzle_sphinx_theme.html_theme_path() +html_theme = 'guzzle_sphinx_theme' + +# Guzzle theme options (see theme.conf for more information) +html_theme_options = { + "index_template": "index.html", + "project_nav_name": "Guzzle", + "github_user": "guzzle", + "github_repo": "guzzle", + "disqus_comments_shortname": "guzzle", + "google_analytics_account": "UA-22752917-1" +} + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = {} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Guzzle.tex', u'Guzzle Documentation', + u'Michael Dowling', 'manual'), +] + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'guzzle', u'Guzzle Documentation', + [u'Michael Dowling'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Guzzle', u'Guzzle Documentation', + u'Michael Dowling', 'Guzzle', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/docs.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/docs.rst new file mode 100755 index 000000000..cf87908bd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/docs.rst @@ -0,0 +1,73 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services + +==================== +Guzzle Documentation +==================== + +Getting started +--------------- + +.. toctree:: + :maxdepth: 1 + + getting-started/overview + getting-started/installation + getting-started/faq + +The HTTP client +--------------- + +.. toctree:: + :maxdepth: 2 + + http-client/client + http-client/request + http-client/response + http-client/entity-bodies + http-client/http-redirects + http-client/uri-templates + +Plugins +------- + +.. toctree:: + :maxdepth: 1 + + plugins/plugins-overview + plugins/creating-plugins + plugins/async-plugin + plugins/backoff-plugin + plugins/cache-plugin + plugins/cookie-plugin + plugins/curl-auth-plugin + plugins/history-plugin + plugins/log-plugin + plugins/md5-validator-plugin + plugins/mock-plugin + plugins/oauth-plugin + +The web service client +---------------------- + +.. toctree:: + :maxdepth: 1 + + webservice-client/webservice-client + webservice-client/using-the-service-builder + webservice-client/guzzle-service-descriptions + batching/batching + iterators/resource-iterators + iterators/guzzle-iterators + +Testing +------- + +.. toctree:: + :maxdepth: 2 + + testing/unit-testing + +API Docs +-------- + +`Read the API docs `_ diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/faq.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/faq.rst new file mode 100755 index 000000000..a0a3fdbb6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/faq.rst @@ -0,0 +1,29 @@ +=== +FAQ +=== + +What should I do if I get this error: Fatal error: Maximum function nesting level of '100' reached, aborting! +------------------------------------------------------------------------------------------------------------- + +You could run into this error if you have the XDebug extension installed and you execute a lot of requests in +callbacks. This error message comes specifically from the XDebug extension. PHP itself does not have a function +nesting limit. Change this setting in your php.ini to increase the limit:: + + xdebug.max_nesting_level = 1000 + +[`source `_] + +How can I speed up my client? +----------------------------- + +There are several things you can do to speed up your client: + +1. Utilize a C based HTTP message parser (e.g. ``Guzzle\Parser\Message\PeclHttpMessageParser``) +2. Disable operation validation by setting the ``command.disable_validation`` option to true on a command + +Why am I getting a 417 error response? +-------------------------------------- + +This can occur for a number of reasons, but if you are sending PUT, POST, or PATCH requests with an +``Expect: 100-Continue`` header, a server that does not support this header will return a 417 response. You can work +around this by calling ``$request->removeHeader('Expect');`` after setting the entity body of a request. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/installation.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/installation.rst new file mode 100755 index 000000000..77d400131 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/installation.rst @@ -0,0 +1,154 @@ +============ +Installation +============ + +Requirements +------------ + +#. PHP 5.3.3+ compiled with the cURL extension +#. A recent version of cURL 7.16.2+ compiled with OpenSSL and zlib + +Installing Guzzle +----------------- + +Composer +~~~~~~~~ + +The recommended way to install Guzzle is with `Composer `_. Composer is a dependency +management tool for PHP that allows you to declare the dependencies your project needs and installs them into your +project. + +.. code-block:: bash + + # Install Composer + curl -sS https://getcomposer.org/installer | php + + # Add Guzzle as a dependency + php composer.phar require guzzle/guzzle:~3.9 + +After installing, you need to require Composer's autoloader: + +.. code-block:: php + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and other best-practices for defining +dependencies at `getcomposer.org `_. + +Using only specific parts of Guzzle +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While you can always just rely on ``guzzle/guzzle``, Guzzle provides several smaller parts of Guzzle as individual +packages available through Composer. + ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| Package name | Description | ++===============================================================================================+==========================================+ +| `guzzle/common `_ | Provides ``Guzzle\Common`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/http `_ | Provides ``Guzzle\Http`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/parser `_ | Provides ``Guzzle\Parser`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/batch `_ | Provides ``Guzzle\Batch`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/cache `_ | Provides ``Guzzle\Cache`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/inflection `_ | Provides ``Guzzle\Inflection`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/iterator `_ | Provides ``Guzzle\Iterator`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/log `_ | Provides ``Guzzle\Log`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin `_ | Provides ``Guzzle\Plugin`` (all plugins) | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-async `_ | Provides ``Guzzle\Plugin\Async`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-backoff `_ | Provides ``Guzzle\Plugin\BackoffPlugin`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-cache `_ | Provides ``Guzzle\Plugin\Cache`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-cookie `_ | Provides ``Guzzle\Plugin\Cookie`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-error-response `_ | Provides ``Guzzle\Plugin\ErrorResponse`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-history `_ | Provides ``Guzzle\Plugin\History`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-log `_ | Provides ``Guzzle\Plugin\Log`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-md5 `_ | Provides ``Guzzle\Plugin\Md5`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-mock `_ | Provides ``Guzzle\Plugin\Mock`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-oauth `_ | Provides ``Guzzle\Plugin\Oauth`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/service `_ | Provides ``Guzzle\Service`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/stream `_ | Provides ``Guzzle\Stream`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ + +Bleeding edge +^^^^^^^^^^^^^ + +During your development, you can keep up with the latest changes on the master branch by setting the version +requirement for Guzzle to ``dev-master``. + +.. code-block:: js + + { + "require": { + "guzzle/guzzle": "dev-master" + } + } + +PEAR +~~~~ + +Guzzle can be installed through PEAR: + +.. code-block:: bash + + pear channel-discover guzzlephp.org/pear + pear install guzzle/guzzle + +You can install a specific version of Guzzle by providing a version number suffix: + +.. code-block:: bash + + pear install guzzle/guzzle-3.9.0 + +Contributing to Guzzle +---------------------- + +In order to contribute, you'll need to checkout the source from GitHub and install Guzzle's dependencies using +Composer: + +.. code-block:: bash + + git clone https://github.com/guzzle/guzzle.git + cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev + +Guzzle is unit tested with PHPUnit. You will need to create your own phpunit.xml file in order to run the unit tests +(or just copy phpunit.xml.dist to phpunit.xml). Run the tests using the vendored PHPUnit binary: + +.. code-block:: bash + + vendor/bin/phpunit + +You'll need to install node.js v0.5.0 or newer in order to test the cURL implementation. + +Framework integrations +---------------------- + +Using Guzzle with Symfony +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Bundles are available on GitHub: + +- `DdeboerGuzzleBundle `_ for Guzzle 2 +- `MisdGuzzleBundle `_ for Guzzle 3 + +Using Guzzle with Silex +~~~~~~~~~~~~~~~~~~~~~~~ + +A `Guzzle Silex service provider `_ is available on GitHub. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/overview.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/overview.rst new file mode 100755 index 000000000..505b40978 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/getting-started/overview.rst @@ -0,0 +1,85 @@ +================= +Welcome to Guzzle +================= + +What is Guzzle? +~~~~~~~~~~~~~~~ + +Guzzle is a PHP HTTP client and framework for building web service clients. Guzzle takes the pain out of sending HTTP +requests and the redundancy out of creating web service clients. + +Features at a glance +-------------------- + +- All the power of cURL with a simple interface. +- Persistent connections and parallel requests. +- Streams request and response bodies +- Service descriptions for quickly building clients. +- Powered by the Symfony2 EventDispatcher. +- Use all of the code or only specific components. +- Plugins for caching, logging, OAuth, mocks, and more +- Includes a custom node.js webserver to test your clients. +- Service descriptions for defining the inputs and outputs of an API +- Resource iterators for traversing paginated resources +- Batching for sending a large number of requests as efficiently as possible + +.. code-block:: php + + // Really simple using a static facade + Guzzle\Http\StaticClient::mount(); + $response = Guzzle::get('http://guzzlephp.org'); + + // More control using a client class + $client = new \Guzzle\Http\Client('http://guzzlephp.org'); + $request = $client->get('/'); + $response = $request->send(); + +License +------- + +Licensed using the `MIT license `_. + + Copyright (c) 2013 Michael Dowling + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Contributing +------------ + +Guidelines +~~~~~~~~~~ + +This is still a work in progress, but there are only a few rules: + +1. Guzzle follows PSR-0, PSR-1, and PSR-2 +2. All pull requests must include unit tests to ensure the change works as expected and to prevent future regressions + +Reporting a security vulnerability +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We want to ensure that Guzzle is a secure HTTP client library for everyone. If you've discovered a security +vulnerability in Guzzle, we appreciate your help in disclosing it to us in a +`responsible manner `_. + +Publicly disclosing a vulnerability can put the entire community at risk. If you've discovered a security concern, +please email us at security@guzzlephp.org. We'll work with you to make sure that we understand the scope of the issue, +and that we fully address your concern. We consider correspondence sent to security@guzzlephp.org our highest priority, +and work to address any issues that arise as quickly as possible. + +After a security vulnerability has been corrected, a security hotfix release will be deployed as soon as possible. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/client.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/client.rst new file mode 100755 index 000000000..723d729db --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/client.rst @@ -0,0 +1,569 @@ +====================== +The Guzzle HTTP client +====================== + +Guzzle gives PHP developers complete control over HTTP requests while utilizing HTTP/1.1 best practices. Guzzle's HTTP +functionality is a robust framework built on top of the `PHP libcurl bindings `_. + +The three main parts of the Guzzle HTTP client are: + ++--------------+-------------------------------------------------------------------------------------------------------+ +| Clients | ``Guzzle\Http\Client`` (creates and sends requests, associates a response with a request) | ++--------------+-------------------------------------------------------------------------------------------------------+ +| Requests | ``Guzzle\Http\Message\Request`` (requests with no body), | +| | ``Guzzle\Http\Message\EntityEnclosingRequest`` (requests with a body) | ++--------------+-------------------------------------------------------------------------------------------------------+ +| Responses | ``Guzzle\Http\Message\Response`` | ++--------------+-------------------------------------------------------------------------------------------------------+ + +Creating a Client +----------------- + +Clients create requests, send requests, and set responses on a request object. When instantiating a client object, +you can pass an optional "base URL" and optional array of configuration options. A base URL is a +:doc:`URI template ` that contains the URL of a remote server. When creating requests with a relative +URL, the base URL of a client will be merged into the request's URL. + +.. code-block:: php + + use Guzzle\Http\Client; + + // Create a client and provide a base URL + $client = new Client('https://api.github.com'); + + $request = $client->get('/user'); + $request->setAuth('user', 'pass'); + echo $request->getUrl(); + // >>> https://api.github.com/user + + // You must send a request in order for the transfer to occur + $response = $request->send(); + + echo $response->getBody(); + // >>> {"type":"User", ... + + echo $response->getHeader('Content-Length'); + // >>> 792 + + $data = $response->json(); + echo $data['type']; + // >>> User + +Base URLs +~~~~~~~~~ + +Notice that the URL provided to the client's ``get()`` method is relative. Relative URLs will always merge into the +base URL of the client. There are a few rules that control how the URLs are merged. + +.. tip:: + + Guzzle follows `RFC 3986 `_ when merging base URLs and + relative URLs. + +In the above example, we passed ``/user`` to the ``get()`` method of the client. This is a relative URL, so it will +merge into the base URL of the client-- resulting in the derived URL of ``https://api.github.com/users``. + +``/user`` is a relative URL but uses an absolute path because it contains the leading slash. Absolute paths will +overwrite any existing path of the base URL. If an absolute path is provided (e.g. ``/path/to/something``), then the +path specified in the base URL of the client will be replaced with the absolute path, and the query string provided +by the relative URL will replace the query string of the base URL. + +Omitting the leading slash and using relative paths will add to the path of the base URL of the client. So using a +client base URL of ``https://api.twitter.com/v1.1`` and creating a GET request with ``statuses/user_timeline.json`` +will result in a URL of ``https://api.twitter.com/v1.1/statuses/user_timeline.json``. If a relative path and a query +string are provided, then the relative path will be appended to the base URL path, and the query string provided will +be merged into the query string of the base URL. + +If an absolute URL is provided (e.g. ``http://httpbin.org/ip``), then the request will completely use the absolute URL +as-is without merging in any of the URL parts specified in the base URL. + +Configuration options +~~~~~~~~~~~~~~~~~~~~~ + +The second argument of the client's constructor is an array of configuration data. This can include URI template data +or special options that alter the client's behavior: + ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``request.options`` | Associative array of :ref:`Request options ` to apply to every | +| | request created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``redirect.disable`` | Disable HTTP redirects for every request created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``curl.options`` | Associative array of cURL options to apply to every request created by the client. | +| | if either the key or value of an entry in the array is a string, Guzzle will | +| | attempt to find a matching defined cURL constant automatically (e.g. | +| | "CURLOPT_PROXY" will be converted to the constant ``CURLOPT_PROXY``). | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``ssl.certificate_authority`` | Set to true to use the Guzzle bundled SSL certificate bundle (this is used by | +| | default, 'system' to use the bundle on your system, a string pointing to a file to | +| | use a specific certificate file, a string pointing to a directory to use multiple | +| | certificates, or ``false`` to disable SSL validation (not recommended). | +| | | +| | When using Guzzle inside of a phar file, the bundled SSL certificate will be | +| | extracted to your system's temp folder, and each time a client is created an MD5 | +| | check will be performed to ensure the integrity of the certificate. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``command.params`` | When using a ``Guzzle\Service\Client`` object, this is an associative array of | +| | default options to set on each command created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ + +Here's an example showing how to set various configuration options, including default headers to send with each request, +default query string parameters to add to each request, a default auth scheme for each request, and a proxy to use for +each request. Values can be injected into the client's base URL using variables from the configuration array. + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client('https://api.twitter.com/{version}', array( + 'version' => 'v1.1', + 'request.options' => array( + 'headers' => array('Foo' => 'Bar'), + 'query' => array('testing' => '123'), + 'auth' => array('username', 'password', 'Basic|Digest|NTLM|Any'), + 'proxy' => 'tcp://localhost:80' + ) + )); + +Setting a custom User-Agent +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default Guzzle User-Agent header is ``Guzzle/ curl/ PHP/``. You can +customize the User-Agent header of a client by calling the ``setUserAgent()`` method of a Client object. + +.. code-block:: php + + // Completely override the default User-Agent + $client->setUserAgent('Test/123'); + + // Prepend a string to the default User-Agent + $client->setUserAgent('Test/123', true); + +Creating requests with a client +------------------------------- + +A Client object exposes several methods used to create Request objects: + +* Create a custom HTTP request: ``$client->createRequest($method, $uri, array $headers, $body, $options)`` +* Create a GET request: ``$client->get($uri, array $headers, $options)`` +* Create a HEAD request: ``$client->head($uri, array $headers, $options)`` +* Create a DELETE request: ``$client->delete($uri, array $headers, $body, $options)`` +* Create a POST request: ``$client->post($uri, array $headers, $postBody, $options)`` +* Create a PUT request: ``$client->put($uri, array $headers, $body, $options)`` +* Create a PATCH request: ``$client->patch($uri, array $headers, $body, $options)`` + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client('http://baseurl.com/api/v1'); + + // Create a GET request using Relative to base URL + // URL of the request: http://baseurl.com/api/v1/path?query=123&value=abc) + $request = $client->get('path?query=123&value=abc'); + $response = $request->send(); + + // Create HEAD request using a relative URL with an absolute path + // URL of the request: http://baseurl.com/path?query=123&value=abc + $request = $client->head('/path?query=123&value=abc'); + $response = $request->send(); + + // Create a DELETE request using an absolute URL + $request = $client->delete('http://www.example.com/path?query=123&value=abc'); + $response = $request->send(); + + // Create a PUT request using the contents of a PHP stream as the body + // Specify custom HTTP headers + $request = $client->put('http://www.example.com/upload', array( + 'X-Header' => 'My Header' + ), fopen('http://www.test.com/', 'r')); + $response = $request->send(); + + // Create a POST request and add the POST files manually + $request = $client->post('http://localhost:8983/solr/update') + ->addPostFiles(array('file' => '/path/to/documents.xml')); + $response = $request->send(); + + // Check if a resource supports the DELETE method + $supportsDelete = $client->options('/path')->send()->isMethodAllowed('DELETE'); + $response = $request->send(); + +Client objects create Request objects using a request factory (``Guzzle\Http\Message\RequestFactoryInterface``). +You can inject a custom request factory into the Client using ``$client->setRequestFactory()``, but you can typically +rely on a Client's default request factory. + +Static clients +-------------- + +You can use Guzzle's static client facade to more easily send simple HTTP requests. + +.. code-block:: php + + // Mount the client so that you can access it at \Guzzle + Guzzle\Http\StaticClient::mount(); + $response = Guzzle::get('http://guzzlephp.org'); + +Each request method of the static client (e.g. ``get()``, ``post()`, ``put()``, etc) accepts an associative array of request +options to apply to the request. + +.. code-block:: php + + $response = Guzzle::post('http://test.com', array( + 'headers' => array('X-Foo' => 'Bar'), + 'body' => array('Test' => '123'), + 'timeout' => 10 + )); + +.. _request-options: + +Request options +--------------- + +Request options can be specified when creating a request or in the ``request.options`` parameter of a client. These +options can control various aspects of a request including: headers to send, query string data, where the response +should be downloaded, proxies, auth, etc. + +headers +~~~~~~~ + +Associative array of headers to apply to the request. When specified in the ``$options`` argument of a client creational +method (e.g. ``get()``, ``post()``, etc), the headers in the ``$options`` array will overwrite headers specified in the +``$headers`` array. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'headers' => array('X-Foo' => 'Bar') + )); + +Headers can be specified on a client to add default headers to every request sent by a client. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + + // Set a single header using path syntax + $client->setDefaultOption('headers/X-Foo', 'Bar'); + + // Set all headers + $client->setDefaultOption('headers', array('X-Foo' => 'Bar')); + +.. note:: + + In addition to setting request options when creating requests or using the ``setDefaultOption()`` method, any + default client request option can be set using a client's config object: + + .. code-block:: php + + $client->getConfig()->setPath('request.options/headers/X-Foo', 'Bar'); + +query +~~~~~ + +Associative array of query string parameters to the request. When specified in the ``$options`` argument of a client +creational method, the query string parameters in the ``$options`` array will overwrite query string parameters +specified in the `$url`. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'query' => array('abc' => '123') + )); + +Query string parameters can be specified on a client to add default query string parameters to every request sent by a +client. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + + // Set a single query string parameter using path syntax + $client->setDefaultOption('query/abc', '123'); + + // Set an array of default query string parameters + $client->setDefaultOption('query', array('abc' => '123')); + +body +~~~~ + +Sets the body of a request. The value supplied to the body option can be a ``Guzzle\Http\EntityBodyInterface``, string, +fopen resource, or array when sending POST requests. When a ``body`` request option is supplied, the option value will +overwrite the ``$body`` argument of a client creational method. + +auth +~~~~ + +Specifies and array of HTTP authorization parameters parameters to use with the request. The array must contain the +username in index [0], the password in index [1], and can optionally contain the authentication type in index [2]. +The available authentication types are: "Basic" (default), "Digest", "NTLM", or "Any". + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'auth' => array('username', 'password', 'Digest') + )); + + // You can add auth headers to every request of a client + $client->setDefaultOption('auth', array('username', 'password', 'Digest')); + +cookies +~~~~~~~ + +Specifies an associative array of cookies to add to the request. + +allow_redirects +~~~~~~~~~~~~~~~ + +Specifies whether or not the request should follow redirects. Requests will follow redirects by default. Set +``allow_redirects`` to ``false`` to disable redirects. + +save_to +~~~~~~~ + +The ``save_to`` option specifies where the body of a response is downloaded. You can pass the path to a file, an fopen +resource, or a ``Guzzle\Http\EntityBodyInterface`` object. + +See :ref:`Changing where a response is downloaded ` for more information on setting the +`save_to` option. + +events +~~~~~~ + +The `events` option makes it easy to attach listeners to the various events emitted by a request object. The `events` +options must be an associative array mapping an event name to a Closure or array the contains a Closure and the +priority of the event. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'events' => array( + 'request.before_send' => function (\Guzzle\Common\Event $e) { + echo 'About to send ' . $e['request']; + } + ) + )); + + // Using the static client: + Guzzle::get($url, array( + 'events' => array( + 'request.before_send' => function (\Guzzle\Common\Event $e) { + echo 'About to send ' . $e['request']; + } + ) + )); + +plugins +~~~~~~~ + +The `plugins` options makes it easy to attach an array of plugins to a request. + +.. code-block:: php + + // Using the static client: + Guzzle::get($url, array( + 'plugins' => array( + new Guzzle\Plugin\Cache\CachePlugin(), + new Guzzle\Plugin\Cookie\CookiePlugin() + ) + )); + +exceptions +~~~~~~~~~~ + +The `exceptions` option can be used to disable throwing exceptions for unsuccessful HTTP response codes +(e.g. 404, 500, etc). Set `exceptions` to false to not throw exceptions. + +params +~~~~~~ + +The `params` options can be used to specify an associative array of data parameters to add to a request. Note that +these are not query string parameters. + +timeout / connect_timeout +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify the maximum number of seconds to allow for an entire transfer to take place before timing out using +the `timeout` request option. You can specify the maximum number of seconds to wait while trying to connect using the +`connect_timeout` request option. Set either of these options to 0 to wait indefinitely. + +.. code-block:: php + + $request = $client->get('http://www.example.com', array(), array( + 'timeout' => 20, + 'connect_timeout' => 1.5 + )); + +verify +~~~~~~ + +Set to true to enable SSL certificate validation (the default), false to disable SSL certificate validation, or supply +the path to a CA bundle to enable verification using a custom certificate. + +cert +~~~~ + +The `cert` option lets you specify a PEM formatted SSL client certificate to use with servers that require one. If the +certificate requires a password, provide an array with the password as the second item. + +This would typically be used in conjunction with the `ssl_key` option. + +.. code-block:: php + + $request = $client->get('https://www.example.com', array(), array( + 'cert' => '/etc/pki/client_certificate.pem' + ) + + $request = $client->get('https://www.example.com', array(), array( + 'cert' => array('/etc/pki/client_certificate.pem', 's3cr3tp455w0rd') + ) + +ssl_key +~~~~~~~ + +The `ssl_key` option lets you specify a file containing your PEM formatted private key, optionally protected by a password. +Note: your password is sensitive, keep the PHP script containing it safe. + +This would typically be used in conjunction with the `cert` option. + +.. code-block:: php + + $request = $client->get('https://www.example.com', array(), array( + 'ssl_key' => '/etc/pki/private_key.pem' + ) + + $request = $client->get('https://www.example.com', array(), array( + 'ssl_key' => array('/etc/pki/private_key.pem', 's3cr3tp455w0rd') + ) + +proxy +~~~~~ + +The `proxy` option is used to specify an HTTP proxy (e.g. `http://username:password@192.168.16.1:10`). + +debug +~~~~~ + +The `debug` option is used to show verbose cURL output for a transfer. + +stream +~~~~~~ + +When using a static client, you can set the `stream` option to true to return a `Guzzle\Stream\Stream` object that can +be used to pull data from a stream as needed (rather than have cURL download the entire contents of a response to a +stream all at once). + +.. code-block:: php + + $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); + while (!$stream->feof()) { + echo $stream->readLine(); + } + +Sending requests +---------------- + +Requests can be sent by calling the ``send()`` method of a Request object, but you can also send requests using the +``send()`` method of a Client. + +.. code-block:: php + + $request = $client->get('http://www.amazon.com'); + $response = $client->send($request); + +Sending requests in parallel +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Client's ``send()`` method accept a single ``Guzzle\Http\Message\RequestInterface`` object or an array of +RequestInterface objects. When an array is specified, the requests will be sent in parallel. + +Sending many HTTP requests serially (one at a time) can cause an unnecessary delay in a script's execution. Each +request must complete before a subsequent request can be sent. By sending requests in parallel, a pool of HTTP +requests can complete at the speed of the slowest request in the pool, significantly reducing the amount of time +needed to execute multiple HTTP requests. Guzzle provides a wrapper for the curl_multi functions in PHP. + +Here's an example of sending three requests in parallel using a client object: + +.. code-block:: php + + use Guzzle\Common\Exception\MultiTransferException; + + try { + $responses = $client->send(array( + $client->get('http://www.google.com/'), + $client->head('http://www.google.com/'), + $client->get('https://www.github.com/') + )); + } catch (MultiTransferException $e) { + + echo "The following exceptions were encountered:\n"; + foreach ($e as $exception) { + echo $exception->getMessage() . "\n"; + } + + echo "The following requests failed:\n"; + foreach ($e->getFailedRequests() as $request) { + echo $request . "\n\n"; + } + + echo "The following requests succeeded:\n"; + foreach ($e->getSuccessfulRequests() as $request) { + echo $request . "\n\n"; + } + } + +If the requests succeed, an array of ``Guzzle\Http\Message\Response`` objects are returned. A single request failure +will not cause the entire pool of requests to fail. Any exceptions thrown while transferring a pool of requests will +be aggregated into a ``Guzzle\Common\Exception\MultiTransferException`` exception. + +Plugins and events +------------------ + +Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications +powered by the +`Symfony2 Event Dispatcher component `_. Any +event listener or subscriber attached to a Client object will automatically be attached to each request created by the +client. + +Using the same cookie session for each request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Attach a ``Guzzle\Plugin\Cookie\CookiePlugin`` to a client which will in turn add support for cookies to every request +created by a client, and each request will use the same cookie session: + +.. code-block:: php + + use Guzzle\Plugin\Cookie\CookiePlugin; + use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; + + // Create a new cookie plugin + $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); + + // Add the cookie plugin to the client + $client->addSubscriber($cookiePlugin); + +.. _client-events: + +Events emitted from a client +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Guzzle\Http\Client`` object emits the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| client.create_request | Called when a client creates a request | * client: The client | +| | | * request: The created request | ++------------------------------+--------------------------------------------+------------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Http\Client; + + $client = new Client(); + + // Add a listener that will echo out requests as they are created + $client->getEventDispatcher()->addListener('client.create_request', function (Event $e) { + echo 'Client object: ' . spl_object_hash($e['client']) . "\n"; + echo "Request object: {$e['request']}\n"; + }); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst new file mode 100755 index 000000000..823b0c022 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst @@ -0,0 +1,151 @@ +=========================== +Request and response bodies +=========================== + +`Entity body `_ is the term used for the body of an HTTP +message. The entity body of requests and responses is inherently a +`PHP stream `_ in Guzzle. The body of the request can be either a string or +a PHP stream which are converted into a ``Guzzle\Http\EntityBody`` object using its factory method. When using a +string, the entity body is stored in a `temp PHP stream `_. The use of +temp PHP streams helps to protect your application from running out of memory when sending or receiving large entity +bodies in your messages. When more than 2MB of data is stored in a temp stream, it automatically stores the data on +disk rather than in memory. + +EntityBody objects provide a great deal of functionality: compression, decompression, calculate the Content-MD5, +calculate the Content-Length (when the resource is repeatable), guessing the Content-Type, and more. Guzzle doesn't +need to load an entire entity body into a string when sending or retrieving data; entity bodies are streamed when +being uploaded and downloaded. + +Here's an example of gzip compressing a text file then sending the file to a URL: + +.. code-block:: php + + use Guzzle\Http\EntityBody; + + $body = EntityBody::factory(fopen('/path/to/file.txt', 'r+')); + echo $body->read(1024); + $body->seek(0, SEEK_END); + $body->write('foo'); + echo $body->ftell(); + $body->rewind(); + + // Send a request using the body + $response = $client->put('http://localhost:8080/uploads', null, $body)->send(); + +The body of the request can be specified in the ``Client::put()`` or ``Client::post()`` method, or, you can specify +the body of the request by calling the ``setBody()`` method of any +``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object. + +Compression +----------- + +You can compress the contents of an EntityBody object using the ``compress()`` method. The compress method accepts a +filter that must match to one of the supported +`PHP stream filters `_ on your system (e.g. `zlib.deflate`, +``bzip2.compress``, etc). Compressing an entity body will stream the entire entity body through a stream compression +filter into a temporary PHP stream. You can uncompress an entity body using the ``uncompress()`` method and passing +the PHP stream filter to use when decompressing the stream (e.g. ``zlib.inflate``). + +.. code-block:: php + + use Guzzle\Http\EntityBody; + + $body = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + echo $body->getSize(); + // >>> 1048576 + + // Compress using the default zlib.deflate filter + $body->compress(); + echo $body->getSize(); + // >>> 314572 + + // Decompress the stream + $body->uncompress(); + echo $body->getSize(); + // >>> 1048576 + +Decorators +---------- + +Guzzle provides several EntityBody decorators that can be used to add functionality to an EntityBody at runtime. + +IoEmittingEntityBody +~~~~~~~~~~~~~~~~~~~~ + +This decorator will emit events when data is read from a stream or written to a stream. Add an event subscriber to the +entity body's ``body.read`` or ``body.write`` methods to receive notifications when data data is transferred. + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Http\EntityBody; + use Guzzle\Http\IoEmittingEntityBody; + + $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + $body = new IoEmittingEntityBody($original); + + // Listen for read events + $body->getEventDispatcher()->addListener('body.read', function (Event $e) { + // Grab data from the event + $entityBody = $e['body']; + // Amount of data retrieved from the body + $lengthOfData = $e['length']; + // The actual data that was read + $data = $e['read']; + }); + + // Listen for write events + $body->getEventDispatcher()->addListener('body.write', function (Event $e) { + // Grab data from the event + $entityBody = $e['body']; + // The data that was written + $data = $e['write']; + // The actual amount of data that was written + $data = $e['read']; + }); + +ReadLimitEntityBody +~~~~~~~~~~~~~~~~~~~ + +The ReadLimitEntityBody decorator can be used to transfer a subset or slice of an existing EntityBody object. This can +be useful for breaking a large file into smaller pieces to be sent in chunks (e.g. Amazon S3's multipart upload API). + +.. code-block:: php + + use Guzzle\Http\EntityBody; + use Guzzle\Http\ReadLimitEntityBody; + + $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + echo $original->getSize(); + // >>> 1048576 + + // Limit the size of the body to 1024 bytes and start reading from byte 2048 + $body = new ReadLimitEntityBody($original, 1024, 2048); + echo $body->getSize(); + // >>> 1024 + echo $body->ftell(); + // >>> 0 + +CachingEntityBody +~~~~~~~~~~~~~~~~~ + +The CachingEntityBody decorator is used to allow seeking over previously read bytes on non-seekable read streams. This +can be useful when transferring a non-seekable entity body fails due to needing to rewind the stream (for example, +resulting from a redirect). Data that is read from the remote stream will be buffered in a PHP temp stream so that +previously read bytes are cached first in memory, then on disk. + +.. code-block:: php + + use Guzzle\Http\EntityBody; + use Guzzle\Http\CachingEntityBody; + + $original = EntityBody::factory(fopen('http://www.google.com', 'r')); + $body = new CachingEntityBody($original); + + $body->read(1024); + echo $body->ftell(); + // >>> 1024 + + $body->seek(0); + echo $body->ftell(); + // >>> 0 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst new file mode 100755 index 000000000..32ba26891 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst @@ -0,0 +1,99 @@ +============== +HTTP redirects +============== + +By default, Guzzle will automatically follow redirects using the non-RFC compliant implementation used by most web +browsers. This means that redirects for POST requests are followed by a GET request. You can force RFC compliance by +enabling the strict mode on a request's parameter object: + +.. code-block:: php + + // Set per request + $request = $client->post(); + $request->getParams()->set('redirect.strict', true); + + // You can set globally on a client so all requests use strict redirects + $client->getConfig()->set('request.params', array( + 'redirect.strict' => true + )); + +By default, Guzzle will redirect up to 5 times before throwing a ``Guzzle\Http\Exception\TooManyRedirectsException``. +You can raise or lower this value using the ``redirect.max`` parameter of a request object: + +.. code-block:: php + + $request->getParams()->set('redirect.max', 2); + +Redirect history +---------------- + +You can get the number of redirects of a request using the resulting response object's ``getRedirectCount()`` method. +Similar to cURL's ``effective_url`` property, Guzzle provides the effective URL, or the last redirect URL that returned +the request, in a response's ``getEffectiveUrl()`` method. + +When testing or debugging, it is often useful to see a history of redirects for a particular request. This can be +achieved using the HistoryPlugin. + +.. code-block:: php + + $request = $client->get('/'); + $history = new Guzzle\Plugin\History\HistoryPlugin(); + $request->addSubscriber($history); + $response = $request->send(); + + // Get the last redirect URL or the URL of the request that received + // this response + echo $response->getEffectiveUrl(); + + // Get the number of redirects + echo $response->getRedirectCount(); + + // Iterate over each sent request and response + foreach ($history->getAll() as $transaction) { + // Request object + echo $transaction['request']->getUrl() . "\n"; + // Response object + echo $transaction['response']->getEffectiveUrl() . "\n"; + } + + // Or, simply cast the HistoryPlugin to a string to view each request and response + echo $history; + +Disabling redirects +------------------- + +You can disable redirects on a client by passing a configuration option in the client's constructor: + +.. code-block:: php + + $client = new Client(null, array('redirect.disable' => true)); + +You can also disable redirects per request: + +.. code-block:: php + + $request = $client->get($url, array(), array('allow_redirects' => false)); + +Redirects and non-repeatable streams +------------------------------------ + +If you are redirected when sending data from a non-repeatable stream and some of the data has been read off of the +stream, then you will get a ``Guzzle\Http\Exception\CouldNotRewindStreamException``. You can get around this error by +adding a custom rewind method to the entity body object being sent in the request. + +.. code-block:: php + + $request = $client->post( + 'http://httpbin.com/redirect/2', + null, + fopen('http://httpbin.com/get', 'r') + ); + + // Add a custom function that can be used to rewind the stream + // (reopen in this example) + $request->getBody()->setRewindFunction(function ($body) { + $body->setStream(fopen('http://httpbin.com/get', 'r')); + return true; + ); + + $response = $client->send(); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/request.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/request.rst new file mode 100755 index 000000000..a8387a915 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/request.rst @@ -0,0 +1,667 @@ +===================== +Using Request objects +===================== + +HTTP request messages +--------------------- + +Request objects are all about building an HTTP message. Each part of an HTTP request message can be set individually +using methods on the request object or set in bulk using the ``setUrl()`` method. Here's the format of an HTTP request +with each part of the request referencing the method used to change it:: + + PUT(a) /path(b)?query=123(c) HTTP/1.1(d) + X-Header(e): header + Content-Length(e): 4 + + data(f) + ++-------------------------+---------------------------------------------------------------------------------+ +| a. **Method** | The request method can only be set when instantiating a request | ++-------------------------+---------------------------------------------------------------------------------+ +| b. **Path** | ``$request->setPath('/path');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| c. **Query** | ``$request->getQuery()->set('query', '123');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| d. **Protocol version** | ``$request->setProtocolVersion('1.1');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| e. **Header** | ``$request->setHeader('X-Header', 'header');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| f. **Entity Body** | ``$request->setBody('data'); // Only available with PUT, POST, PATCH, DELETE`` | ++-------------------------+---------------------------------------------------------------------------------+ + +Creating requests with a client +------------------------------- + +Client objects are responsible for creating HTTP request objects. + +GET requests +~~~~~~~~~~~~ + +`GET requests `_ are the most common form of HTTP +requests. When you visit a website in your browser, the HTML of the website is downloaded using a GET request. GET +requests are idempotent requests that are typically used to download content (an entity) identified by a request URL. + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client(); + + // Create a request that has a query string and an X-Foo header + $request = $client->get('http://www.amazon.com?a=1', array('X-Foo' => 'Bar')); + + // Send the request and get the response + $response = $request->send(); + +You can change where the body of a response is downloaded on any request using the +``$request->setResponseBody(string|EntityBodyInterface|resource)`` method of a request. You can also set the ``save_to`` +option of a request: + +.. code-block:: php + + // Send the response body to a file + $request = $client->get('http://test.com', array(), array('save_to' => '/path/to/file')); + + // Send the response body to an fopen resource + $request = $client->get('http://test.com', array(), array('save_to' => fopen('/path/to/file', 'w'))); + +HEAD requests +~~~~~~~~~~~~~ + +`HEAD requests `_ work exactly like GET requests except +that they do not actually download the response body (entity) of the response message. HEAD requests are useful for +retrieving meta information about an entity identified by a Request-URI. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + $request = $client->head('http://www.amazon.com'); + $response = $request->send(); + echo $response->getContentLength(); + // >>> Will output the Content-Length header value + +DELETE requests +~~~~~~~~~~~~~~~ + +A `DELETE method `_ requests that the origin server +delete the resource identified by the Request-URI. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + $request = $client->delete('http://example.com'); + $response = $request->send(); + +POST requests +~~~~~~~~~~~~~ + +While `POST requests `_ can be used for a number of +reasons, POST requests are often used when submitting HTML form data to a website. POST requests can include an entity +body in the HTTP request. + +POST requests in Guzzle are sent with an ``application/x-www-form-urlencoded`` Content-Type header if POST fields are +present but no files are being sent in the POST. If files are specified in the POST request, then the Content-Type +header will become ``multipart/form-data``. + +The ``post()`` method of a client object accepts four arguments: the URL, optional headers, post fields, and an array of +request options. To send files in the POST request, prepend the ``@`` symbol to the array value (just like you would if +you were using the PHP ``curl_setopt`` function). + +Here's how to create a multipart/form-data POST request containing files and fields: + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post', array(), array( + 'custom_field' => 'my custom value', + 'file_field' => '@/path/to/file.xml' + )); + + $response = $request->send(); + +.. note:: + + Remember to **always** sanitize user input when sending POST requests: + + .. code-block:: php + + // Prevent users from accessing sensitive files by sanitizing input + $_POST = array('firstname' => '@/etc/passwd'); + $request = $client->post('http://www.example.com', array(), array ( + 'firstname' => str_replace('@', '', $_POST['firstname']) + )); + +You can alternatively build up the contents of a POST request. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post') + ->setPostField('custom_field', 'my custom value') + ->addPostFile('file', '/path/to/file.xml'); + + $response = $request->send(); + +Raw POST data +^^^^^^^^^^^^^ + +POST requests can also contain raw POST data that is not related to HTML forms. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post', array(), 'this is the body'); + $response = $request->send(); + +You can set the body of POST request using the ``setBody()`` method of the +``Guzzle\Http\Message\EntityEnclosingRequest`` object. This method accepts a string, a resource returned from +``fopen``, or a ``Guzzle\Http\EntityBodyInterface`` object. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post'); + // Set the body of the POST to stream the contents of /path/to/large_body.txt + $request->setBody(fopen('/path/to/large_body.txt', 'r')); + $response = $request->send(); + +PUT requests +~~~~~~~~~~~~ + +The `PUT method `_ requests that the enclosed entity be +stored under the supplied Request-URI. PUT requests are similar to POST requests in that they both can send an entity +body in the request message. + +The body of a PUT request (any any ``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object) is always stored as +a ``Guzzle\Http\Message\EntityBodyInterface`` object. This allows a great deal of flexibility when sending data to a +remote server. For example, you can stream the contents of a stream returned by fopen, stream the contents of a +callback function, or simply send a string of data. + +.. code-block:: php + + $request = $client->put('http://httpbin.org/put', array(), 'this is the body'); + $response = $request->send(); + +Just like with POST, PATH, and DELETE requests, you can set the body of a PUT request using the ``setBody()`` method. + +.. code-block:: php + + $request = $client->put('http://httpbin.org/put'); + $request->setBody(fopen('/path/to/large_body.txt', 'r')); + $response = $request->send(); + +PATCH requests +~~~~~~~~~~~~~~ + +`PATCH requests `_ are used to modify a resource. + +.. code-block:: php + + $request = $client->patch('http://httpbin.org', array(), 'this is the body'); + $response = $request->send(); + +OPTIONS requests +~~~~~~~~~~~~~~~~ + +The `OPTIONS method `_ represents a request for +information about the communication options available on the request/response chain identified by the Request-URI. + +.. code-block:: php + + $request = $client->options('http://httpbin.org'); + $response = $request->send(); + + // Check if the PUT method is supported by this resource + var_export($response->isMethodAllows('PUT')); + +Custom requests +~~~~~~~~~~~~~~~ + +You can create custom HTTP requests that use non-standard HTTP methods using the ``createRequest()`` method of a +client object. + +.. code-block:: php + + $request = $client->createRequest('COPY', 'http://example.com/foo', array( + 'Destination' => 'http://example.com/bar', + 'Overwrite' => 'T' + )); + $response = $request->send(); + +Query string parameters +----------------------- + +Query string parameters of a request are owned by a request's ``Guzzle\Http\Query`` object that is accessible by +calling ``$request->getQuery()``. The Query class extends from ``Guzzle\Common\Collection`` and allows you to set one +or more query string parameters as key value pairs. You can set a parameter on a Query object using the +``set($key, $value)`` method or access the query string object like an associative array. Any previously specified +value for a key will be overwritten when using ``set()``. Use ``add($key, $value)`` to add a value to query string +object, and in the event of a collision with an existing value at a specific key, the value will be converted to an +array that contains all of the previously set values. + +.. code-block:: php + + $request = new Guzzle\Http\Message\Request('GET', 'http://www.example.com?foo=bar&abc=123'); + + $query = $request->getQuery(); + echo "{$query}\n"; + // >>> foo=bar&abc=123 + + $query->remove('abc'); + echo "{$query}\n"; + // >>> foo=bar + + $query->set('foo', 'baz'); + echo "{$query}\n"; + // >>> foo=baz + + $query->add('foo', 'bar'); + echo "{$query}\n"; + // >>> foo%5B0%5D=baz&foo%5B1%5D=bar + +Whoah! What happened there? When ``foo=bar`` was added to the existing ``foo=baz`` query string parameter, the +aggregator associated with the Query object was used to help convert multi-value query string parameters into a string. +Let's disable URL-encoding to better see what's happening. + +.. code-block:: php + + $query->useUrlEncoding(false); + echo "{$query}\n"; + // >>> foo[0]=baz&foo[1]=bar + +.. note:: + + URL encoding can be disabled by passing false, enabled by passing true, set to use RFC 1738 by passing + ``Query::FORM_URLENCODED`` (internally uses PHP's ``urlencode`` function), or set to RFC 3986 by passing + ``Query::RFC_3986`` (this is the default and internally uses PHP's ``rawurlencode`` function). + +As you can see, the multiple values were converted into query string parameters following the default PHP convention of +adding numerically indexed square bracket suffixes to each key (``foo[0]=baz&foo[1]=bar``). The strategy used to convert +multi-value parameters into a string can be customized using the ``setAggregator()`` method of the Query class. Guzzle +ships with the following query string aggregators by default: + +1. ``Guzzle\Http\QueryAggregator\PhpAggregator``: Aggregates using PHP style brackets (e.g. ``foo[0]=baz&foo[1]=bar``) +2. ``Guzzle\Http\QueryAggregator\DuplicateAggregator``: Performs no aggregation and allows for key value pairs to be + repeated in a URL (e.g. ``foo=baz&foo=bar``) +3. ``Guzzle\Http\QueryAggregator\CommaAggregator``: Aggregates using commas (e.g. ``foo=baz,bar``) + +.. _http-message-headers: + +HTTP Message Headers +-------------------- + +HTTP message headers are case insensitive, multiple occurrences of any header can be present in an HTTP message +(whether it's valid or not), and some servers require specific casing of particular headers. Because of this, request +and response headers are stored in ``Guzzle\Http\Message\Header`` objects. The Header object can be cast as a string, +counted, or iterated to retrieve each value from the header. Casting a Header object to a string will return all of +the header values concatenated together using a glue string (typically ", "). + +A request (and response) object have several methods that allow you to retrieve and modify headers. + +* ``getHeaders()``: Get all of the headers of a message as a ``Guzzle\Http\Message\Header\HeaderCollection`` object. +* ``getHeader($header)``: Get a specific header from a message. If the header exists, you'll get a + ``Guzzle\Http\Message\Header`` object. If the header does not exist, this methods returns ``null``. +* ``hasHeader($header)``: Returns true or false based on if the message has a particular header. +* ``setHeader($header, $value)``: Set a header value and overwrite any previously set value for this header. +* ``addHeader($header, $value)``: Add a header with a particular name. If a previous value was already set by the same, + then the header will contain multiple values. +* ``removeHeader($header)``: Remove a header by name from the message. + +.. code-block:: php + + $request = new Request('GET', 'http://httpbin.com/cookies'); + // addHeader will set and append to any existing header values + $request->addHeader('Foo', 'bar'); + $request->addHeader('foo', 'baz'); + // setHeader overwrites any existing values + $request->setHeader('Test', '123'); + + // Request headers can be cast as a string + echo $request->getHeader('Foo'); + // >>> bar, baz + echo $request->getHeader('Test'); + // >>> 123 + + // You can count the number of headers of a particular case insensitive name + echo count($request->getHeader('foO')); + // >>> 2 + + // You can iterate over Header objects + foreach ($request->getHeader('foo') as $header) { + echo $header . "\n"; + } + + // You can get all of the request headers as a Guzzle\Http\Message\Header\HeaderCollection object + $headers = $request->getHeaders(); + + // Missing headers return NULL + var_export($request->getHeader('Missing')); + // >>> null + + // You can see all of the different variations of a header by calling raw() on the Header + var_export($request->getHeader('foo')->raw()); + +Setting the body of a request +----------------------------- + +Requests that can send a body (e.g. PUT, POST, DELETE, PATCH) are instances of +``Guzzle\Http\Message\EntityEnclosingRequestInterface``. Entity enclosing requests contain several methods that allow +you to specify the body to send with a request. + +Use the ``setBody()`` method of a request to set the body that will be sent with a request. This method accepts a +string, a resource returned by ``fopen()``, an array, or an instance of ``Guzzle\Http\EntityBodyInterface``. The body +will then be streamed from the underlying ``EntityBodyInterface`` object owned by the request. When setting the body +of the request, you can optionally specify a Content-Type header and whether or not to force the request to use +chunked Transfer-Encoding. + +.. code-block:: php + + $request = $client->put('/user.json'); + $request->setBody('{"foo":"baz"}', 'application/json'); + +Content-Type header +~~~~~~~~~~~~~~~~~~~ + +Guzzle will automatically add a Content-Type header to a request if the Content-Type can be guessed based on the file +extension of the payload being sent or the file extension present in the path of a request. + +.. code-block:: php + + $request = $client->put('/user.json', array(), '{"foo":"bar"}'); + // The Content-Type was guessed based on the path of the request + echo $request->getHeader('Content-Type'); + // >>> application/json + + $request = $client->put('/user.json'); + $request->setBody(fopen('/tmp/user_data.json', 'r')); + // The Content-Type was guessed based on the path of the entity body + echo $request->getHeader('Content-Type'); + // >>> application/json + +Transfer-Encoding: chunked header +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When sending HTTP requests that contain a payload, you must let the remote server know how to determine when the entire +message has been sent. This usually is done by supplying a ``Content-Length`` header that tells the origin server the +size of the body that is to be sent. In some cases, the size of the payload being sent in a request cannot be known +before initiating the transfer. In these cases (when using HTTP/1.1), you can use the ``Transfer-Encoding: chunked`` +header. + +If the Content-Length cannot be determined (i.e. using a PHP ``http://`` stream), then Guzzle will automatically add +the ``Transfer-Encoding: chunked`` header to the request. + +.. code-block:: php + + $request = $client->put('/user.json'); + $request->setBody(fopen('http://httpbin.org/get', 'r')); + + // The Content-Length could not be determined + echo $request->getHeader('Transfer-Encoding'); + // >>> chunked + +See :doc:`/http-client/entity-bodies` for more information on entity bodies. + +Expect: 100-Continue header +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Expect: 100-Continue`` header is used to help a client prevent sending a large payload to a server that will +reject the request. This allows clients to fail fast rather than waste bandwidth sending an erroneous payload. Guzzle +will automatically add the ``Expect: 100-Continue`` header to a request when the size of the payload exceeds 1MB or if +the body of the request is not seekable (this helps to prevent errors when a non-seekable body request is redirected). + +.. note:: + + If you find that your larger requests are taking too long to complete, you should first check if the + ``Expect: 100-Continue`` header is being sent with the request. Some servers do not respond well to this header, + which causes cURL to sleep for `1 second `_. + +POST fields and files +~~~~~~~~~~~~~~~~~~~~~ + +Any entity enclosing request can send POST style fields and files. This includes POST, PUT, PATCH, and DELETE requests. +Any request that has set POST fields or files will use cURL's POST message functionality. + +.. code-block:: php + + $request = $client->post('/post'); + // Set an overwrite any previously specified value + $request->setPostField('foo', 'bar'); + // Append a value to any existing values + $request->getPostFields()->add('foo', 'baz'); + // Remove a POST field by name + $request->removePostField('fizz'); + + // Add a file to upload (forces multipart/form-data) + $request->addPostFile('my_file', '/path/to/file', 'plain/text'); + // Remove a POST file by POST key name + $request->removePostFile('my_other_file'); + +.. tip:: + + Adding a large number of POST fields to a POST request is faster if you use the ``addPostFields()`` method so that + you can add and process multiple fields with a single call. Adding multiple POST files is also faster using + ``addPostFiles()``. + +Working with cookies +-------------------- + +Cookies can be modified and retrieved from a request using the following methods: + +.. code-block:: php + + $request->addCookie($name, $value); + $request->removeCookie($name); + $value = $request->getCookie($name); + $valueArray = $request->getCookies(); + +Use the :doc:`cookie plugin ` if you need to reuse cookies between requests. + +.. _request-set-response-body: + +Changing where a response is downloaded +---------------------------------------- + +When a request is sent, the body of the response will be stored in a PHP temp stream by default. You can change the +location in which the response will be downloaded using ``$request->setResponseBody($body)`` or the ``save_to`` request +option. This can be useful for downloading the contents of a URL to a specific file. + +Here's an example of using request options: + +.. code-block:: php + + $request = $this->client->get('http://example.com/large.mov', array(), array( + 'save_to' => '/tmp/large_file.mov' + )); + $request->send(); + var_export(file_exists('/tmp/large_file.mov')); + // >>> true + +Here's an example of using ``setResponseBody()``: + +.. code-block:: php + + $body = fopen('/tmp/large_file.mov', 'w'); + $request = $this->client->get('http://example.com/large.mov'); + $request->setResponseBody($body); + + // You can more easily specify the name of a file to save the contents + // of the response to by passing a string to ``setResponseBody()``. + + $request = $this->client->get('http://example.com/large.mov'); + $request->setResponseBody('/tmp/large_file.mov'); + +Custom cURL options +------------------- + +Most of the functionality implemented in the libcurl bindings has been simplified and abstracted by Guzzle. Developers +who need access to `cURL specific functionality `_ can still add cURL handle +specific behavior to Guzzle HTTP requests by modifying the cURL options collection of a request: + +.. code-block:: php + + $request->getCurlOptions()->set(CURLOPT_LOW_SPEED_LIMIT, 200); + +Other special options that can be set in the ``curl.options`` array include: + ++-------------------------+---------------------------------------------------------------------------------+ +| debug | Adds verbose cURL output to a temp stream owned by the cURL handle object | ++-------------------------+---------------------------------------------------------------------------------+ +| progress | Instructs cURL to emit events when IO events occur. This allows you to be | +| | notified when bytes are transferred over the wire by subscribing to a request's | +| | ``curl.callback.read``, ``curl.callback.write``, and ``curl.callback.progress`` | +| | events. | ++-------------------------+---------------------------------------------------------------------------------+ + +Request options +--------------- + +Requests options can be specified when creating a request or in the ``request.options`` parameter of a client. These +options can control various aspects of a request including: headers to send, query string data, where the response +should be downloaded, proxies, auth, etc. + +.. code-block:: php + + $request = $client->get($url, $headers, array('proxy' => 'http://proxy.com')); + +See :ref:`Request options ` for more information. + +Working with errors +------------------- + +HTTP errors +~~~~~~~~~~~ + +Requests that receive a 4xx or 5xx response will throw a ``Guzzle\Http\Exception\BadResponseException``. More +specifically, 4xx errors throw a ``Guzzle\Http\Exception\ClientErrorResponseException``, and 5xx errors throw a +``Guzzle\Http\Exception\ServerErrorResponseException``. You can catch the specific exceptions or just catch the +BadResponseException to deal with either type of error. Here's an example of catching a generic BadResponseException: + +.. code-block:: php + + try { + $response = $client->get('/not_found.xml')->send(); + } catch (Guzzle\Http\Exception\BadResponseException $e) { + echo 'Uh oh! ' . $e->getMessage(); + echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n"; + echo 'HTTP request: ' . $e->getRequest() . "\n"; + echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n"; + echo 'HTTP response: ' . $e->getResponse() . "\n"; + } + +Throwing an exception when a 4xx or 5xx response is encountered is the default behavior of Guzzle requests. This +behavior can be overridden by adding an event listener with a higher priority than -255 that stops event propagation. +You can subscribe to ``request.error`` to receive notifications any time an unsuccessful response is received. + +You can change the response that will be associated with the request by calling ``setResponse()`` on the +``$event['request']`` object passed into your listener, or by changing the ``$event['response']`` value of the +``Guzzle\Common\Event`` object that is passed to your listener. Transparently changing the response associated with a +request by modifying the event allows you to retry failed requests without complicating the code that uses the client. +This might be useful for sending requests to a web service that has expiring auth tokens. When a response shows that +your token has expired, you can get a new token, retry the request with the new token, and return the successful +response to the user. + +Here's an example of retrying a request using updated authorization credentials when a 401 response is received, +overriding the response of the original request with the new response, and still allowing the default exception +behavior to be called when other non-200 response status codes are encountered: + +.. code-block:: php + + // Add custom error handling to any request created by this client + $client->getEventDispatcher()->addListener('request.error', function(Event $event) { + + if ($event['response']->getStatusCode() == 401) { + + $newRequest = $event['request']->clone(); + $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken()); + $newResponse = $newRequest->send(); + + // Set the response object of the request without firing more events + $event['response'] = $newResponse; + + // You can also change the response and fire the normal chain of + // events by calling $event['request']->setResponse($newResponse); + + // Stop other events from firing when you override 401 responses + $event->stopPropagation(); + } + + }); + +cURL errors +~~~~~~~~~~~ + +Connection problems and cURL specific errors can also occur when transferring requests using Guzzle. When Guzzle +encounters cURL specific errors while transferring a single request, a ``Guzzle\Http\Exception\CurlException`` is +thrown with an informative error message and access to the cURL error message. + +A ``Guzzle\Http\Exception\MultiTransferException`` exception is thrown when a cURL specific error occurs while +transferring multiple requests in parallel. You can then iterate over all of the exceptions encountered during the +transfer. + +Plugins and events +------------------ + +Guzzle request objects expose various events that allow you to hook in custom logic. A request object owns a +``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling +``$request->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or +event subscribers (classes that listen to specific events of a dispatcher). You can add event subscribers to a request +directly by just calling ``$request->addSubscriber($mySubscriber);``. + +.. _request-events: + +Events emitted from a request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Guzzle\Http\Message\Request`` and ``Guzzle\Http\Message\EntityEnclosingRequest`` object emit the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| request.before_send | About to send request | * request: Request to be sent | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.sent | Sent the request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.complete | Completed a full HTTP transaction | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.success | Completed a successful request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.error | Completed an unsuccessful request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.exception | An unsuccessful response was | * request: Request | +| | received. | * response: Received response | +| | | * exception: BadResponseException | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.receive.status_line | Received the start of a response | * line: Full response start line | +| | | * status_code: Status code | +| | | * reason_phrase: Reason phrase | +| | | * previous_response: (e.g. redirect) | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.progress | cURL progress event (only dispatched when | * handle: CurlHandle | +| | ``emit_io`` is set on a request's curl | * download_size: Total download size | +| | options) | * downloaded: Bytes downloaded | +| | | * upload_size: Total upload bytes | +| | | * uploaded: Bytes uploaded | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.write | cURL event called when data is written to | * request: Request | +| | an outgoing stream | * write: Data being written | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.read | cURL event called when data is written to | * request: Request | +| | an incoming stream | * read: Data being read | ++------------------------------+--------------------------------------------+------------------------------------------+ + +Creating a request event listener +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here's an example that listens to the ``request.complete`` event of a request and prints the request and response. + +.. code-block:: php + + use Guzzle\Common\Event; + + $request = $client->get('http://www.google.com'); + + // Echo out the response that was received + $request->getEventDispatcher()->addListener('request.complete', function (Event $e) { + echo $e['request'] . "\n\n"; + echo $e['response']; + }); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/response.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/response.rst new file mode 100755 index 000000000..ba487316f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/response.rst @@ -0,0 +1,141 @@ +====================== +Using Response objects +====================== + +Sending a request will return a ``Guzzle\Http\Message\Response`` object. You can view the raw HTTP response message by +casting the Response object to a string. Casting the response to a string will return the entity body of the response +as a string too, so this might be an expensive operation if the entity body is stored in a file or network stream. If +you only want to see the response headers, you can call ``getRawHeaders()``. + +Response status line +-------------------- + +The different parts of a response's `status line `_ +(the first line of the response HTTP message) are easily retrievable. + +.. code-block:: php + + $response = $client->get('http://www.amazon.com')->send(); + + echo $response->getStatusCode(); // >>> 200 + echo $response->getReasonPhrase(); // >>> OK + echo $response->getProtocol(); // >>> HTTP + echo $response->getProtocolVersion(); // >>> 1.1 + +You can determine the type of the response using several helper methods: + +.. code-block:: php + + $response->isSuccessful(); // true + $response->isInformational(); + $response->isRedirect(); + $response->isClientError(); + $response->isServerError(); + +Response headers +---------------- + +The Response object contains helper methods for retrieving common response headers. These helper methods normalize the +variations of HTTP response headers. + +.. code-block:: php + + $response->getCacheControl(); + $response->getContentType(); + $response->getContentLength(); + $response->getContentEncoding(); + $response->getContentMd5(); + $response->getEtag(); + // etc... There are methods for every known response header + +You can interact with the Response headers using the same exact methods used to interact with Request headers. See +:ref:`http-message-headers` for more information. + +.. code-block:: php + + echo $response->getHeader('Content-Type'); + echo $response->getHeader('Content-Length'); + echo $response->getHeaders()['Content-Type']; // PHP 5.4 + +Response body +------------- + +The entity body object of a response can be retrieved by calling ``$response->getBody()``. The response EntityBody can +be cast to a string, or you can pass ``true`` to this method to retrieve the body as a string. + +.. code-block:: php + + $request = $client->get('http://www.amazon.com'); + $response = $request->send(); + echo $response->getBody(); + +See :doc:`/http-client/entity-bodies` for more information on entity bodies. + +JSON Responses +~~~~~~~~~~~~~~ + +You can easily parse and use a JSON response as an array using the ``json()`` method of a response. This method will +always return an array if the response is valid JSON or if the response body is empty. You will get an exception if you +call this method and the response is not valid JSON. + +.. code-block:: php + + $data = $response->json(); + echo gettype($data); + // >>> array + +XML Responses +~~~~~~~~~~~~~ + +You can easily parse and use a XML response as SimpleXMLElement object using the ``xml()`` method of a response. This +method will always return a SimpleXMLElement object if the response is valid XML or if the response body is empty. You +will get an exception if you call this method and the response is not valid XML. + +.. code-block:: php + + $xml = $response->xml(); + echo $xml->foo; + // >>> Bar! + +Streaming responses +------------------- + +Some web services provide streaming APIs that allow a client to keep a HTTP request open for an extended period of +time while polling and reading. Guzzle provides a simple way to convert HTTP request messages into +``Guzzle\Stream\Stream`` objects so that you can send the initial headers of a request, read the response headers, and +pull in the response body manually as needed. + +Here's an example using the Twitter Streaming API to track the keyword "bieber": + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Stream\PhpStreamRequestFactory; + + $client = new Client('https://stream.twitter.com/1'); + + $request = $client->post('statuses/filter.json', null, array( + 'track' => 'bieber' + )); + + $request->setAuth('myusername', 'mypassword'); + + $factory = new PhpStreamRequestFactory(); + $stream = $factory->fromRequest($request); + + // Read until the stream is closed + while (!$stream->feof()) { + // Read a line from the stream + $line = $stream->readLine(); + // JSON decode the line of data + $data = json_decode($line, true); + } + +You can use the ``stream`` request option when using a static client to more easily create a streaming response. + +.. code-block:: php + + $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); + while (!$stream->feof()) { + echo $stream->readLine(); + } diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst new file mode 100755 index 000000000..c18ac3e8d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst @@ -0,0 +1,52 @@ +============= +URI templates +============= + +The ``$uri`` passed to one of the client's request creational methods or the base URL of a client can utilize URI +templates. Guzzle supports the entire `URI templates RFC `_. URI templates add a +special syntax to URIs that replace template place holders with user defined variables. + +Every request created by a Guzzle HTTP client passes through a URI template so that URI template expressions are +automatically expanded: + +.. code-block:: php + + $client = new Guzzle\Http\Client('https://example.com/', array('a' => 'hi')); + $request = $client->get('/{a}'); + +Because of URI template expansion, the URL of the above request will become ``https://example.com/hi``. Notice that +the template was expanded using configuration variables of the client. You can pass in custom URI template variables +by passing the URI of your request as an array where the first index of the array is the URI template and the second +index of the array are template variables that are merged into the client's configuration variables. + +.. code-block:: php + + $request = $client->get(array('/test{?a,b}', array('b' => 'there'))); + +The URL for this request will become ``https://test.com?a=hi&b=there``. URI templates aren't limited to just simple +variable replacements; URI templates can provide an enormous amount of flexibility when creating request URIs. + +.. code-block:: php + + $request = $client->get(array('http://example.com{+path}{/segments*}{?query,data*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => 'value' + ) + ))); + +The resulting URL would become ``http://example.com/foo/bar/one/two?query=test&more=value``. + +By default, URI template expressions are enclosed in an opening and closing brace (e.g. ``{var}``). If you are working +with a web service that actually uses braces (e.g. Solr), then you can specify a custom regular expression to use to +match URI template expressions. + +.. code-block:: php + + $client->getUriTemplate()->setRegex('/\<\$(.+)\>/'); + $client->get('/<$a>'); + +You can learn about all of the different features of URI templates by reading the +`URI templates RFC `_. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/index.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/index.rst new file mode 100755 index 000000000..f76f3bbe6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/index.rst @@ -0,0 +1,5 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services +.. toctree:: + :hidden: + + docs.rst diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst new file mode 100755 index 000000000..a5c7fd33f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst @@ -0,0 +1,97 @@ +================ +Guzzle iterators +================ + +Guzzle provides several SPL iterators that can be used with other SPL iterators, including Guzzle resource iterators. +Guzzle's ``guzzle/iterator`` component can also be used independently of the rest of Guzzle through Packagist and +Composer: https://packagist.org/packages/guzzle/iterator + +ChunkedIterator +--------------- + +Pulls out multiple values from an inner iterator and yields and array of values for each outer iteration -- essentially +pulling out chunks of values from the inner iterator. + +.. code-block:: php + + use Guzzle\Iterator\ChunkedIterator; + + $inner = new ArrayIterator(range(0, 8)); + $chunkedIterator = new ChunkedIterator($inner, 2); + + foreach ($chunkedIterator as $chunk) { + echo implode(', ', $chunk) . "\n"; + } + + // >>> 0, 1 + // >>> 2, 3 + // >>> 4, 5 + // >>> 6, 7 + // >>> 8 + +FilterIterator +-------------- + +This iterator is used to filter values out of the inner iterator. This iterator can be used when PHP 5.4's +CallbackFilterIterator is not available. + +.. code-block:: php + + use Guzzle\Iterator\FilterIterator; + + $inner = new ArrayIterator(range(1, 10)); + $filterIterator = new FilterIterator($inner, function ($value) { + return $value % 2; + }); + + foreach ($filterIterator as $value) { + echo $value . "\n"; + } + + // >>> 2 + // >>> 4 + // >>> 6 + // >>> 8 + // >>> 10 + +MapIterator +----------- + +This iterator modifies the values of the inner iterator before yielding. + +.. code-block:: php + + use Guzzle\Iterator\MapIterator; + + $inner = new ArrayIterator(range(0, 3)); + + $mapIterator = new MapIterator($inner, function ($value) { + return $value * 10; + }); + + foreach ($mapIterator as $value) { + echo $value . "\n"; + } + + // >>> 0 + // >>> 10 + // >>> 20 + // >>> 30 + +MethodProxyIterator +------------------- + +This decorator is useful when you need to expose a specific method from an inner iterator that might be wrapper +by one or more iterator decorators. This decorator proxies missing method calls to each inner iterator until one +of the inner iterators can fulfill the call. + +.. code-block:: php + + use Guzzle\Iterator\MethodProxyIterator; + + $inner = new \ArrayIterator(); + $proxy = new MethodProxyIterator($inner); + + // Proxy method calls to the ArrayIterator + $proxy->append('a'); + $proxy->append('b'); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst new file mode 100755 index 000000000..ce0bee59f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst @@ -0,0 +1,149 @@ +================== +Resource iterators +================== + +Web services often implement pagination in their responses which requires the end-user to issue a series of consecutive +requests in order to fetch all of the data they asked for. Users of your web service client should not be responsible +for implementing the logic involved in iterating through pages of results. Guzzle provides a simple resource iterator +foundation to make it easier on web service client developers to offer a useful abstraction layer. + +Getting an iterator from a client +--------------------------------- + + ResourceIteratorInterface Guzzle\Service\Client::getIterator($command [, array $commandOptions, array $iteratorOptions ]) + +The ``getIterator`` method of a ``Guzzle\Service\ClientInterface`` object provides a convenient interface for +instantiating a resource iterator for a specific command. This method implicitly uses a +``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object to create resource iterators. Pass an +instantiated command object or the name of a command in the first argument. When passing the name of a command, the +command factory of the client will create the command by name using the ``$commandOptions`` array. The third argument +may be used to pass an array of options to the constructor of the instantiated ``ResourceIteratorInterface`` object. + +.. code-block:: php + + $iterator = $client->getIterator('get_users'); + + foreach ($iterator as $user) { + echo $user['name'] . ' age ' . $user['age'] . PHP_EOL; + } + +The above code sample might execute a single request or a thousand requests. As a consumer of a web service, I don't +care. I just want to iterate over all of the users. + +Iterator options +~~~~~~~~~~~~~~~~ + +The two universal options that iterators should support are ``limit`` and ``page_size``. Using the ``limit`` option +tells the resource iterator to attempt to limit the total number of iterated resources to a specific amount. Keep in +mind that this is not always possible due to limitations that may be inherent to a web service. The ``page_size`` +option is used to tell a resource iterator how many resources to request per page of results. Much like the ``limit`` +option, you can not rely on getting back exactly the number of resources your specify in the ``page_size`` option. + +.. note:: + + The ``limit`` and ``page_size`` options can also be specified on an iterator using the ``setLimit($limit)`` and + ``setPageSize($pageSize)`` methods. + +Resolving iterator class names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default resource iterator factory of a client object expects that your iterators are stored under the ``Model`` +folder of your client and that an iterator is names after the CamelCase name of a command followed by the word +"Iterator". For example, if you wanted to create an iterator for the ``get_users`` command, then your iterator class +would be ``Model\GetUsersIterator`` and would be stored in ``Model/GetUsersIterator.php``. + +Creating an iterator +-------------------- + +While not required, resource iterators in Guzzle typically iterate using a ``Guzzle\Service\Command\CommandInterface`` +object. ``Guzzle\Service\Resource\ResourceIterator``, the default iterator implementation that you should extend, +accepts a command object and array of iterator options in its constructor. The command object passed to the resource +iterator is expected to be ready to execute and not previously executed. The resource iterator keeps a reference of +this command and clones the original command each time a subsequent request needs to be made to fetch more data. + +Implement the sendRequest method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The most important thing (and usually the only thing) you need to do when creating a resource iterator is to implement +the ``sendRequest()`` method of the resource iterator. The ``sendRequest()`` method is called when you begin +iterating or if there are no resources left to iterate and it you expect to retrieve more resources by making a +subsequent request. The ``$this->command`` property of the resource iterator is updated with a cloned copy of the +original command object passed into the constructor of the iterator. Use this command object to issue your subsequent +requests. + +The ``sendRequest()`` method must return an array of the resources you retrieved from making the subsequent call. +Returning an empty array will stop the iteration. If you suspect that your web service client will occasionally return +an empty result set but still requires further iteration, then you must implement a sort of loop in your +``sendRequest()`` method that will continue to issue subsequent requests until your reach the end of the paginated +result set or until additional resources are retrieved from the web service. + +Update the nextToken property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Beyond fetching more results, the ``sendRequest()`` method is responsible for updating the ``$this->nextToken`` +property of the iterator. Setting this property to anything other than null tells the iterator that issuing a +subsequent request using the nextToken value will probably return more results. You must continually update this +value in your ``sendRequest()`` method as each response is received from the web service. + +Example iterator +---------------- + +Let's say you want to implement a resource iterator for the ``get_users`` command of your web service. The +``get_users`` command receives a response that contains a list of users, and if there are more pages of results to +retrieve, returns a value called ``next_user``. This return value is known as the **next token** and should be used to +issue subsequent requests. + +Assume the response to a ``get_users`` command returns JSON data that looks like this: + +.. code-block:: javascript + + { + "users": [ + { "name": "Craig Johnson", "age": 10 }, + { "name": "Tom Barker", "age": 20 }, + { "name": "Bob Mitchell", "age": 74 } + ], + "next_user": "Michael Dowling" + } + +Assume that because there is a ``next_user`` value, there will be more users if a subsequent request is issued. If the +``next_user`` value is missing or null, then we know there are no more results to fetch. Let's implement a resource +iterator for this command. + +.. code-block:: php + + namespace MyService\Model; + + use Guzzle\Service\Resource\ResourceIterator; + + /** + * Iterate over a get_users command + */ + class GetUsersIterator extends ResourceIterator + { + protected function sendRequest() + { + // If a next token is set, then add it to the command + if ($this->nextToken) { + $this->command->set('next_user', $this->nextToken); + } + + // Execute the command and parse the result + $result = $this->command->execute(); + + // Parse the next token + $this->nextToken = isset($result['next_user']) ? $result['next_user'] : false; + + return $result['users']; + } + } + +As you can see, it's pretty simple to implement an iterator. There are a few things that you should notice from this +example: + +1. You do not need to create a new command in the ``sendRequest()`` method. A new command object is cloned from the + original command passed into the constructor of the iterator before the ``sendRequest()`` method is called. + Remember that the resource iterator expects a command that has not been executed. +2. When the ``sendRequest()`` method is first called, you will not have a ``$this->nextToken`` value, so always check + before setting it on a command. Notice that the next token is being updated each time a request is sent. +3. After fetching more resources from the service, always return an array of resources. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst new file mode 100755 index 000000000..9bd8f4251 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst @@ -0,0 +1,18 @@ +============ +Async plugin +============ + +The AsyncPlugin allows you to send requests that do not wait on a response. This is handled through cURL by utilizing +the progress event. When a request has sent all of its data to the remote server, Guzzle adds a 1ms timeout on the +request and instructs cURL to not download the body of the response. The async plugin then catches the exception and +adds a mock response to the request, along with an X-Guzzle-Async header to let you know that the response was not +fully downloaded. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Async\AsyncPlugin; + + $client = new Client('http://www.example.com'); + $client->addSubscriber(new AsyncPlugin()); + $response = $client->get()->send(); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst new file mode 100755 index 000000000..5a7694141 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst @@ -0,0 +1,22 @@ +==================== +Backoff retry plugin +==================== + +The ``Guzzle\Plugin\Backoff\BackoffPlugin`` automatically retries failed HTTP requests using custom backoff strategies: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Backoff\BackoffPlugin; + + $client = new Client('http://www.test.com/'); + // Use a static factory method to get a backoff plugin using the exponential backoff strategy + $backoffPlugin = BackoffPlugin::getExponentialBackoff(); + + // Add the backoff plugin to the client object + $client->addSubscriber($backoffPlugin); + +The BackoffPlugin's constructor accepts a ``Guzzle\Plugin\Backoff\BackoffStrategyInterface`` object that is used to +determine when a retry should be issued and how long to delay between retries. The above code example shows how to +attach a BackoffPlugin to a client that is pre-configured to retry failed 500 and 503 responses using truncated +exponential backoff (emulating the behavior of Guzzle 2's ExponentialBackoffPlugin). diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst new file mode 100755 index 000000000..d2fd5df26 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst @@ -0,0 +1,169 @@ +================= +HTTP Cache plugin +================= + +Guzzle can leverage HTTP's caching specifications using the ``Guzzle\Plugin\Cache\CachePlugin``. The CachePlugin +provides a private transparent proxy cache that caches HTTP responses. The caching logic, based on +`RFC 2616 `_, uses HTTP headers to control caching behavior, +cache lifetime, and supports Vary, ETag, and Last-Modified based revalidation: + +.. code-block:: php + + use Guzzle\Http\Client; + use Doctrine\Common\Cache\FilesystemCache; + use Guzzle\Cache\DoctrineCacheAdapter; + use Guzzle\Plugin\Cache\CachePlugin; + use Guzzle\Plugin\Cache\DefaultCacheStorage; + + $client = new Client('http://www.test.com/'); + + $cachePlugin = new CachePlugin(array( + 'storage' => new DefaultCacheStorage( + new DoctrineCacheAdapter( + new FilesystemCache('/path/to/cache/files') + ) + ) + )); + + // Add the cache plugin to the client object + $client->addSubscriber($cachePlugin); + $client->get('http://www.wikipedia.org/')->send(); + + // The next request will revalidate against the origin server to see if it + // has been modified. If a 304 response is received the response will be + // served from cache + $client->get('http://www.wikipedia.org/')->send(); + +The cache plugin intercepts GET and HEAD requests before they are actually transferred to the origin server. The cache +plugin then generates a hash key based on the request method and URL, and checks to see if a response exists in the cache. If +a response exists in the cache, the cache adapter then checks to make sure that the caching rules associated with the response +satisfy the request, and ensures that response still fresh. If the response is acceptable for the request any required +revalidation, then the cached response is served instead of contacting the origin server. + +Vary +---- + +Cache keys are derived from a request method and a request URL. Multiple responses can map to the same cache key and +stored in Guzzle's underlying cache storage object. You should use the ``Vary`` HTTP header to tell the cache storage +object that the cache response must have been cached for a request that matches the headers specified in the Vary header +of the request. This allows you to have specific cache entries for the same request URL but variations in a request's +headers determine which cache entry is served. Please see the http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44 +for more information. + +Cache options +------------- + +There are several options you can add to requests or clients to modify the behavior of the cache plugin. + +Override cache TTL +~~~~~~~~~~~~~~~~~~ + +You can override the number of seconds a cacheable response is stored in the cache by setting the +``cache.override_ttl`` parameter on the params object of a request: + +.. code-block:: php + + // If the response to the request is cacheable, then the response will be cached for 100 seconds + $request->getParams()->set('cache.override_ttl', 100); + +If a response doesn't specify any freshness policy, it will be kept in cache for 3600 seconds by default. + +Custom caching decision +~~~~~~~~~~~~~~~~~~~~~~~ + +If the service you are interacting with does not return caching headers or returns responses that are normally +something that would not be cached, you can set a custom ``can_cache`` object on the constructor of the CachePlugin +and provide a ``Guzzle\Plugin\Cache\CanCacheInterface`` object. You can use the +``Guzzle\Plugin\Cache\CallbackCanCacheStrategy`` to easily make a caching decision based on an HTTP request and +response. + +Revalidation options +~~~~~~~~~~~~~~~~~~~~ + +You can change the revalidation behavior of a request using the ``cache.revalidate`` parameter. Setting this +parameter to ``never`` will ensure that a revalidation request is never sent, and the response is always served from +the origin server. Setting this parameter to ``skip`` will never revalidate and uses the response stored in the cache. + +Normalizing requests for caching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``cache.key_filter`` parameter if you wish to strip certain query string parameters from your +request before creating a unique hash for the request. This parameter can be useful if your requests have query +string values that cause each request URL to be unique (thus preventing a cache hit). The ``cache.key_filter`` +format is simply a comma separated list of query string values to remove from the URL when creating a cache key. +For example, here we are saying that the ``a`` and ``q`` query string variables should be ignored when generating a +cache key for the request: + +.. code-block:: php + + $request->getParams()->set('cache.key_filter', 'a, q'); + +Other options +~~~~~~~~~~~~~ + +There are many other options available to the CachePlugin that can meet almost any caching requirement, including +custom revalidation implementations, custom cache key generators, custom caching decision strategies, and custom +cache storage objects. Take a look the constructor of ``Guzzle\Plugin\Cache\CachePlugin`` for more information. + +Setting Client-wide cache settings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify cache settings for every request created by a client by adding cache settings to the configuration +options of a client. + +.. code-block:: php + + $client = new Guzzle\Http\Client('http://www.test.com', array( + 'request.params' => array( + 'cache.override_ttl' => 3600, + 'params.cache.revalidate' => 'never' + ) + )); + + echo $client->get('/')->getParams()->get('cache.override_ttl'); + // >>> 3600 + + echo $client->get('/')->getParams()->get('cache.revalidate'); + // >>> never + +Cache revalidation +------------------ + +If the cache plugin determines that a response to a GET request needs revalidation, a conditional GET is transferred +to the origin server. If the origin server returns a 304 response, then a response containing the merged headers of +the cached response with the new response and the entity body of the cached response is returned. Custom revalidation +strategies can be injected into a CachePlugin if needed. + +Cache adapters +-------------- + +Guzzle doesn't try to reinvent the wheel when it comes to caching or logging. Plenty of other frameworks have +excellent solutions in place that you are probably already using in your applications. Guzzle uses adapters for +caching and logging. The cache plugin requires a cache adapter so that is can store responses in a cache. Guzzle +currently supports cache adapters for `Doctrine 2.0 `_ and the +`Zend Framework `_. + +Doctrine cache adapter +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use Doctrine\Common\Cache\ArrayCache; + use Guzzle\Cache\DoctrineCacheAdapter; + use Guzzle\Plugin\Cache\CachePlugin; + + $backend = new ArrayCache(); + $adapter = new DoctrineCacheAdapter($backend); + $cache = new CachePlugin($adapter); + +Zend Framework cache adapter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use Guzzle\Cache\ZendCacheAdapter; + use Zend\Cache\Backend\TestBackend; + + $backend = new TestBackend(); + $adapter = new ZendCacheAdapter($backend); + $cache = new CachePlugin($adapter); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst new file mode 100755 index 000000000..a6cc7d924 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst @@ -0,0 +1,33 @@ +============= +Cookie plugin +============= + +Some web services require a Cookie in order to maintain a session. The ``Guzzle\Plugin\Cookie\CookiePlugin`` will add +cookies to requests and parse cookies from responses using a CookieJar object: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Cookie\CookiePlugin; + use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; + + $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); + + // Add the cookie plugin to a client + $client = new Client('http://www.test.com/'); + $client->addSubscriber($cookiePlugin); + + // Send the request with no cookies and parse the returned cookies + $client->get('http://www.yahoo.com/')->send(); + + // Send the request again, noticing that cookies are being sent + $request = $client->get('http://www.yahoo.com/'); + $request->send(); + + echo $request; + +You can disable cookies per-request by setting the ``cookies.disable`` value to true on a request's params object. + +.. code-block:: php + + $request->getParams()->set('cookies.disable', true); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst new file mode 100755 index 000000000..0870155b5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst @@ -0,0 +1,93 @@ +================ +Creating plugins +================ + +.. highlight:: php + +Guzzle is extremely extensible because of the behavioral modifications that can be added to requests, clients, and +commands using an event system. Before and after the majority of actions are taken in the library, an event is emitted +with the name of the event and context surrounding the event. Observers can subscribe to a subject and modify the +subject based on the events received. Guzzle's event system utilizes the Symfony2 EventDispatcher and is the backbone +of its plugin architecture. + +Overview +-------- + +Plugins must implement the ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` interface. The +``EventSubscriberInterface`` requires that your class implements a static method, ``getSubscribedEvents()``, that +returns an associative array mapping events to methods on the object. See the +`Symfony2 documentation `_ for more information. + +Plugins can be attached to any subject, or object in Guzzle that implements that +``Guzzle\Common\HasDispatcherInterface``. + +Subscribing to a subject +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can subscribe an instantiated observer to an event by calling ``addSubscriber`` on a subject. + +.. code-block:: php + + $testPlugin = new TestPlugin(); + $client->addSubscriber($testPlugin); + +You can also subscribe to only specific events using a closure:: + + $client->getEventDispatcher()->addListener('request.create', function(Event $event) { + echo $event->getName(); + echo $event['request']; + }); + +``Guzzle\Common\Event`` objects are passed to notified functions. The Event object has a ``getName()`` method which +return the name of the emitted event and may contain contextual information that can be accessed like an array. + +Knowing what events to listen to +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Any class that implements the ``Guzzle\Common\HasDispatcherInterface`` must implement a static method, +``getAllEvents()``, that returns an array of the events that are emitted from the object. You can browse the source +to see each event, or you can call the static method directly in your code to get a list of available events. + +Event hooks +----------- + +* :ref:`client-events` +* :ref:`service-client-events` +* :ref:`request-events` +* ``Guzzle\Http\Curl\CurlMulti``: +* :ref:`service-builder-events` + +Examples of the event system +---------------------------- + +Simple Echo plugin +~~~~~~~~~~~~~~~~~~ + +This simple plugin prints a string containing the request that is about to be sent by listening to the +``request.before_send`` event:: + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + class EchoPlugin implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + return array('request.before_send' => 'onBeforeSend'); + } + + public function onBeforeSend(Guzzle\Common\Event $event) + { + echo 'About to send a request: ' . $event['request'] . "\n"; + } + } + + $client = new Guzzle\Service\Client('http://www.test.com/'); + + // Create the plugin and add it as an event subscriber + $plugin = new EchoPlugin(); + $client->addSubscriber($plugin); + + // Send a request and notice that the request is printed to the screen + $client->get('/')->send(); + +Running the above code will print a string containing the HTTP request that is about to be sent. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst new file mode 100755 index 000000000..66d4a01e3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst @@ -0,0 +1,32 @@ +========================== +cURL authentication plugin +========================== + +.. warning:: + + The CurlAuthPlugin is deprecated. You should use the `auth` parameter of a client to add authorization headers to + every request created by a client. + + .. code-block:: php + + $client->setDefaultOption('auth', array('username', 'password', 'Basic|Digest|NTLM|Any')); + +If your web service client requires basic authorization, then you can use the CurlAuthPlugin to easily add an +Authorization header to each request sent by the client. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\CurlAuth\CurlAuthPlugin; + + $client = new Client('http://www.test.com/'); + + // Add the auth plugin to the client object + $authPlugin = new CurlAuthPlugin('username', 'password'); + $client->addSubscriber($authPlugin); + + $response = $client->get('projects/1/people')->send(); + $xml = new SimpleXMLElement($response->getBody(true)); + foreach ($xml->person as $person) { + echo $person->email . "\n"; + } diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst new file mode 100755 index 000000000..b96befe79 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst @@ -0,0 +1,24 @@ +============== +History plugin +============== + +The history plugin tracks all of the requests and responses sent through a request or client. This plugin can be +useful for crawling or unit testing. By default, the history plugin stores up to 10 requests and responses. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\History\HistoryPlugin; + + $client = new Client('http://www.test.com/'); + + // Add the history plugin to the client object + $history = new HistoryPlugin(); + $history->setLimit(5); + $client->addSubscriber($history); + + $client->get('http://www.yahoo.com/')->send(); + + echo $history->getLastRequest(); + echo $history->getLastResponse(); + echo count($history); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst new file mode 100755 index 000000000..3e2b22944 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst @@ -0,0 +1,69 @@ +========== +Log plugin +========== + +Use the ``Guzzle\Plugin\Log\LogPlugin`` to view all data sent over the wire, including entity bodies and redirects. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Log\Zf1LogAdapter; + use Guzzle\Plugin\Log\LogPlugin; + use Guzzle\Log\MessageFormatter; + + $client = new Client('http://www.test.com/'); + + $adapter = new Zf1LogAdapter( + new \Zend_Log(new \Zend_Log_Writer_Stream('php://output')) + ); + $logPlugin = new LogPlugin($adapter, MessageFormatter::DEBUG_FORMAT); + + // Attach the plugin to the client, which will in turn be attached to all + // requests generated by the client + $client->addSubscriber($logPlugin); + + $response = $client->get('http://google.com')->send(); + +The code sample above wraps a ``Zend_Log`` object using a ``Guzzle\Log\Zf1LogAdapter``. After attaching the plugin to +the client, all data sent over the wire will be logged to stdout. + +The first argument of the LogPlugin's constructor accepts a ``Guzzle\Log\LogAdapterInterface`` object. This object is +an adapter that allows you to use the logging capabilities of your favorite log implementation. The second argument of +the constructor accepts a ``Guzzle\Log\MessageFormatter`` or a log messaged format string. The format string uses +variable substitution and allows you to define the log data that is important to your application. The different +variables that can be injected are as follows: + +================== ==================================================================================== +Variable Substitution +================== ==================================================================================== +{request} Full HTTP request message +{response} Full HTTP response message +{ts} Timestamp +{host} Host of the request +{method} Method of the request +{url} URL of the request +{host} Host of the request +{protocol} Request protocol +{version} Protocol version +{resource} Resource of the request (path + query + fragment) +{port} Port of the request +{hostname} Hostname of the machine that sent the request +{code} Status code of the response (if available) +{phrase} Reason phrase of the response (if available) +{curl_error} Curl error message (if available) +{curl_code} Curl error code (if available) +{curl_stderr} Curl standard error (if available) +{connect_time} Time in seconds it took to establish the connection (if available) +{total_time} Total transaction time in seconds for last transfer (if available) +{req_header_*} Replace `*` with the lowercased name of a request header to add to the message +{res_header_*} Replace `*` with the lowercased name of a response header to add to the message +{req_body} Request body +{res_body} Response body +================== ==================================================================================== + +The LogPlugin has a helper method that can be used when debugging that will output the full HTTP request and +response of a transaction: + +.. code-block:: php + + $client->addSubscriber(LogPlugin::getDebugPlugin()); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst new file mode 100755 index 000000000..1b1cfa8a8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst @@ -0,0 +1,29 @@ +==================== +MD5 validator plugin +==================== + +Entity bodies can sometimes be modified over the wire due to a faulty TCP transport or misbehaving proxy. If an HTTP +response contains a Content-MD5 header, then a MD5 hash of the entity body of a response can be compared against the +Content-MD5 header of the response to determine if the response was delivered intact. The +``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` will throw an ``UnexpectedValueException`` if the calculated MD5 hash does +not match the Content-MD5 header value: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Md5\Md5ValidatorPlugin; + + $client = new Client('http://www.test.com/'); + + $md5Plugin = new Md5ValidatorPlugin(); + + // Add the md5 plugin to the client object + $client->addSubscriber($md5Plugin); + + $request = $client->get('http://www.yahoo.com/'); + $request->send(); + +Calculating the MD5 hash of a large entity body or an entity body that was transferred using a Content-Encoding is an +expensive operation. When working in high performance applications, you might consider skipping the MD5 hash +validation for entity bodies bigger than a certain size or Content-Encoded entity bodies +(see ``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` for more information). diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst new file mode 100755 index 000000000..4900cb565 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst @@ -0,0 +1,27 @@ +=========== +Mock plugin +=========== + +The mock plugin is useful for testing Guzzle clients. The mock plugin allows you to queue an array of responses that +will satisfy requests sent from a client by consuming the request queue in FIFO order. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Mock\MockPlugin; + use Guzzle\Http\Message\Response; + + $client = new Client('http://www.test.com/'); + + $mock = new MockPlugin(); + $mock->addResponse(new Response(200)) + ->addResponse(new Response(404)); + + // Add the mock plugin to the client object + $client->addSubscriber($mock); + + // The following request will receive a 200 response from the plugin + $client->get('http://www.example.com/')->send(); + + // The following request will receive a 404 response from the plugin + $client->get('http://www.test.com/')->send(); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst new file mode 100755 index 000000000..e67eabaa1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst @@ -0,0 +1,30 @@ +============ +OAuth plugin +============ + +Guzzle ships with an OAuth 1.0 plugin that can sign requests using a consumer key, consumer secret, OAuth token, +and OAuth secret. Here's an example showing how to send an authenticated request to the Twitter REST API: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Oauth\OauthPlugin; + + $client = new Client('http://api.twitter.com/1'); + $oauth = new OauthPlugin(array( + 'consumer_key' => 'my_key', + 'consumer_secret' => 'my_secret', + 'token' => 'my_token', + 'token_secret' => 'my_token_secret' + )); + $client->addSubscriber($oauth); + + $response = $client->get('statuses/public_timeline.json')->send(); + +If you need to use a custom signing method, you can pass a ``signature_method`` configuration option in the +constructor of the OAuth plugin. The ``signature_method`` option must be a callable variable that accepts a string to +sign and signing key and returns a signed string. + +.. note:: + + You can omit the ``token`` and ``token_secret`` options to use two-legged OAuth. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc new file mode 100755 index 000000000..8d6d09b46 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc @@ -0,0 +1,9 @@ +* :doc:`/plugins/async-plugin` +* :doc:`/plugins/backoff-plugin` +* :doc:`/plugins/cache-plugin` +* :doc:`/plugins/cookie-plugin` +* :doc:`/plugins/history-plugin` +* :doc:`/plugins/log-plugin` +* :doc:`/plugins/md5-validator-plugin` +* :doc:`/plugins/mock-plugin` +* :doc:`/plugins/oauth-plugin` diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst new file mode 100755 index 000000000..19ae57ece --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst @@ -0,0 +1,59 @@ +====================== +Plugin system overview +====================== + +The workflow of sending a request and parsing a response is driven by Guzzle's event system, which is powered by the +`Symfony2 Event Dispatcher component `_. + +Any object in Guzzle that emits events will implement the ``Guzzle\Common\HasEventDispatcher`` interface. You can add +event subscribers directly to these objects using the ``addSubscriber()`` method, or you can grab the +``Symfony\Component\EventDispatcher\EventDispatcher`` object owned by the object using ``getEventDispatcher()`` and +add a listener or event subscriber. + +Adding event subscribers to clients +----------------------------------- + +Any event subscriber or event listener attached to the EventDispatcher of a ``Guzzle\Http\Client`` or +``Guzzle\Service\Client`` object will automatically be attached to all request objects created by the client. This +allows you to attach, for example, a HistoryPlugin to a client object, and from that point on, every request sent +through that client will utilize the HistoryPlugin. + +.. code-block:: php + + use Guzzle\Plugin\History\HistoryPlugin; + use Guzzle\Service\Client; + + $client = new Client(); + + // Create a history plugin and attach it to the client + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + // Create and send a request. This request will also utilize the HistoryPlugin + $client->get('http://httpbin.org')->send(); + + // Echo out the last sent request by the client + echo $history->getLastRequest(); + +.. tip:: + + :doc:`Create event subscribers `, or *plugins*, to implement reusable logic that can be + shared across clients. Event subscribers are also easier to test than anonymous functions. + +Pre-Built plugins +----------------- + +Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications +powered by the Symfony2 Event Dispatcher component. + +* :doc:`async-plugin` +* :doc:`backoff-plugin` +* :doc:`cache-plugin` +* :doc:`cookie-plugin` +* :doc:`curl-auth-plugin` +* :doc:`history-plugin` +* :doc:`log-plugin` +* :doc:`md5-validator-plugin` +* :doc:`mock-plugin` +* :doc:`oauth-plugin` + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/requirements.txt b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/requirements.txt new file mode 100755 index 000000000..f62e31837 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/requirements.txt @@ -0,0 +1,2 @@ +Sphinx>=1.2b1 +guzzle_sphinx_theme>=0.5.0 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/testing/unit-testing.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/testing/unit-testing.rst new file mode 100755 index 000000000..f4297af30 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/testing/unit-testing.rst @@ -0,0 +1,201 @@ +=========================== +Unit Testing Guzzle clients +=========================== + +Guzzle provides several tools that will enable you to easily unit test your web service clients. + +* PHPUnit integration +* Mock responses +* node.js web server for integration testing + +PHPUnit integration +------------------- + +Guzzle is unit tested using `PHPUnit `_. Your web service client's unit tests should extend +``Guzzle\Tests\GuzzleTestCase`` so that you can take advantage of some of the built in helpers. + +In order to unit test your client, a developer would need to copy phpunit.xml.dist to phpunit.xml and make any needed +modifications. As a best practice and security measure for you and your contributors, it is recommended to add an +ignore statement to your SCM so that phpunit.xml is ignored. + +Bootstrapping +~~~~~~~~~~~~~ + +Your web service client should have a tests/ folder that contains a bootstrap.php file. The bootstrap.php file +responsible for autoloading and configuring a ``Guzzle\Service\Builder\ServiceBuilder`` that is used throughout your +unit tests for loading a configured client. You can add custom parameters to your phpunit.xml file that expects users +to provide the path to their configuration data. + +.. code-block:: php + + Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Aws\Common\Aws::factory($_SERVER['CONFIG'])); + + Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Guzzle\Service\Builder\ServiceBuilder::factory(array( + 'test.unfuddle' => array( + 'class' => 'Guzzle.Unfuddle.UnfuddleClient', + 'params' => array( + 'username' => 'test_user', + 'password' => '****', + 'subdomain' => 'test' + ) + ) + ))); + +The above code registers a service builder that can be used throughout your unit tests. You would then be able to +retrieve an instantiated and configured Unfuddle client by calling ``$this->getServiceBuilder()->get('test.unfuddle)``. +The above code assumes that ``$_SERVER['CONFIG']`` contains the path to a file that stores service description +configuration. + +Unit testing remote APIs +------------------------ + +Mock responses +~~~~~~~~~~~~~~ + +One of the benefits of unit testing is the ability to quickly determine if there are errors in your code. If your +unit tests run slowly, then they become tedious and will likely be run less frequently. Guzzle's philosophy on unit +testing web service clients is that no network access should be required to run the unit tests. This means that +responses are served from mock responses or local servers. By adhering to this principle, tests will run much faster +and will not require an external resource to be available. The problem with this approach is that your mock responses +must first be gathered and then subsequently updated each time the remote API changes. + +Integration testing over the internet +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can perform integration testing with a web service over the internet by making calls directly to the service. If +the web service you are requesting uses a complex signing algorithm or some other specific implementation, then you +may want to include at least one actual network test that can be run specifically through the command line using +`PHPUnit group annotations `_. + +@group internet annotation +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When creating tests that require an internet connection, it is recommended that you add ``@group internet`` annotations +to your unit tests to specify which tests require network connectivity. + +You can then `run PHPUnit tests `_ that exclude the @internet +group by running ``phpunit --exclude-group internet``. + +API credentials +^^^^^^^^^^^^^^^ + +If API credentials are required to run your integration tests, you must add ```` parameters to your +phpunit.xml.dist file and extract these parameters in your bootstrap.php file. + +.. code-block:: xml + + + + + + + + + + + + + ./Tests + + + + +You can then extract the ``server`` variables in your bootstrap.php file by grabbing them from the ``$_SERVER`` +superglobal: ``$apiUser = $_SERVER['API_USER'];`` + +Further reading +^^^^^^^^^^^^^^^ + +A good discussion on the topic of testing remote APIs can be found in Sebastian Bergmann's +`Real-World Solutions for Developing High-Quality PHP Frameworks and Applications `_. + +Queueing Mock responses +----------------------- + +Mock responses can be used to test if requests are being generated correctly and responses and handled correctly by +your client. Mock responses can be queued up for a client using the ``$this->setMockResponse($client, $path)`` method +of your test class. Pass the client you are adding mock responses to and a single path or array of paths to mock +response files relative to the ``/tests/mock/ folder``. This will queue one or more mock responses for your client by +creating a simple observer on the client. Mock response files must contain a full HTTP response message: + +.. code-block:: none + + HTTP/1.1 200 OK + Date: Wed, 25 Nov 2009 12:00:00 GMT + Connection: close + Server: AmazonS3 + Content-Type: application/xml + + + EU + +After queuing mock responses for a client, you can get an array of the requests that were sent by the client that +were issued a mock response by calling ``$this->getMockedRequests()``. + +You can also use the ``Guzzle\Plugin\Mock\MockPlugin`` object directly with your clients. + +.. code-block:: php + + $plugin = new Guzzle\Plugin\Mock\MockPlugin(); + $plugin->addResponse(new Guzzle\Http\Message\Response(200)); + $client = new Guzzle\Http\Client(); + $client->addSubscriber($plugin); + + // The following request will get the mock response from the plugin in FIFO order + $request = $client->get('http://www.test.com/'); + $request->send(); + + // The MockPlugin maintains a list of requests that were mocked + $this->assertContainsOnly($request, $plugin->getReceivedRequests()); + +node.js web server for integration testing +------------------------------------------ + +Using mock responses is usually enough when testing a web service client. If your client needs to add custom cURL +options to requests, then you should use the node.js test web server to ensure that your HTTP request message is +being created correctly. + +Guzzle is based around PHP's libcurl bindings. cURL sometimes modifies an HTTP request message based on +``CURLOPT_*`` options. Headers that are added to your request by cURL will not be accounted for if you inject mock +responses into your tests. Additionally, some request entity bodies cannot be loaded by the client before transmitting +it to the sever (for example, when using a client as a sort of proxy and streaming content from a remote server). You +might also need to inspect the entity body of a ``multipart/form-data`` POST request. + +.. note:: + + You can skip all of the tests that require the node.js test web server by excluding the ``server`` group: + ``phpunit --exclude-group server`` + +Using the test server +~~~~~~~~~~~~~~~~~~~~~ + +The node.js test server receives requests and returns queued responses. The test server exposes a simple API that is +used to enqueue responses and inspect the requests that it has received. + +Retrieve the server object by calling ``$this->getServer()``. If the node.js server is not running, it will be +started as a forked process and an object that interfaces with the server will be returned. (note: stopping the +server is handled internally by Guzzle.) + +You can queue an HTTP response or an array of responses by calling ``$this->getServer()->enqueue()``: + +.. code-block:: php + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + +The above code queues a single 200 response with an empty body. Responses are queued using a FIFO order; this +response will be returned by the server when it receives the first request and then removed from the queue. If a +request is received by a server with no queued responses, an exception will be thrown in your unit test. + +You can inspect the requests that the server has retrieved by calling ``$this->getServer()->getReceivedRequests()``. +This method accepts an optional ``$hydrate`` parameter that specifies if you are retrieving an array of string HTTP +requests or an array of ``Guzzle\Http\RequestInterface`` subclassed objects. "Hydrating" the requests will allow +greater flexibility in your unit tests so that you can easily assert the state of the various parts of a request. + +You will need to modify the base_url of your web service client in order to use it against the test server. + +.. code-block:: php + + $client = $this->getServiceBuilder()->get('my_client'); + $client->setBaseUrl($this->getServer()->getUrl()); + +After running the above code, all calls made from the ``$client`` object will be sent to the test web server. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst new file mode 100755 index 000000000..ad6070b27 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst @@ -0,0 +1,619 @@ +=========================== +Guzzle service descriptions +=========================== + +Guzzle allows you to serialize HTTP requests and parse HTTP responses using a DSL called a service descriptions. +Service descriptions define web service APIs by documenting each operation, the operation's parameters, validation +options for each parameter, an operation's response, how the response is parsed, and any errors that can be raised for +an operation. Writing a service description for a web service allows you to more quickly consume a web service than +writing concrete commands for each web service operation. + +Guzzle service descriptions can be representing using a PHP array or JSON document. Guzzle's service descriptions are +heavily inspired by `Swagger `_. + +Service description schema +========================== + +A Guzzle Service description must match the following JSON schema document. This document can also serve as a guide when +implementing a Guzzle service description. + +Download the schema here: :download:`Guzzle JSON schema document ` + +.. class:: overflow-height-500px + + .. literalinclude:: ../_downloads/guzzle-schema-1.0.json + :language: json + +Top-level attributes +-------------------- + +Service descriptions are comprised of the following top-level attributes: + +.. code-block:: json + + { + "name": "string", + "apiVersion": "string|number", + "baseUrl": "string", + "description": "string", + "operations": {}, + "models": {}, + "includes": ["string.php", "string.json"] + } + ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| Property Name | Value | Description | ++=========================================+=========================+=======================================================================================================================+ +| name | string | Name of the web service | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| apiVersion | string|number | Version identifier that the service description is compatible with | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| baseUrl or basePath | string | Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the | +| | | process defined in RFC 2396. Some clients require custom logic to determine the baseUrl. In those cases, it is best | +| | | to not include a baseUrl in the service description, but rather allow the factory method of the client to configure | +| | | the client’s baseUrl. | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| description | string | Short summary of the web service | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| operations | object containing | Operations of the service. The key is the name of the operation and value is the attributes of the operation. | +| | :ref:`operation-schema` | | +| | | | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| models | object containing | Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP | +| | :ref:`model-schema` | response is parsed into a ``Guzzle\Service\Resource\Model`` object when an operation uses a ``model`` ``responseType``| ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| includes | array of .js, | Service description files to include and extend from (can be a .json, .js, or .php file) | +| | .json, or .php | | +| | files. | | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| (any additional properties) | mixed | Any additional properties specified as top-level attributes are allowed and will be treated as arbitrary data | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ + +.. _operation-schema: + +Operations +---------- + +Operations are the actions that can be taken on a service. Each operation is given a unique name and has a distinct +endpoint and HTTP method. If an API has a ``DELETE /users/:id`` operation, a satisfactory operation name might be +``DeleteUser`` with a parameter of ``id`` that is inserted into the URI. + +.. class:: overflow-height-250px + + .. code-block:: json + + { + "operations": { + "operationName": { + "extends": "string", + "httpMethod": "GET|POST|PUT|DELETE|PATCH|string", + "uri": "string", + "summary": "string", + "class": "string", + "responseClass": "string", + "responseNotes": "string", + "type": "string", + "description": "string", + "responseType": "primitive|class|(model by name)|documentation|(string)", + "deprecated": false, + "errorResponses": [ + { + "code": 500, + "reason": "Unexpected Error", + "class": "string" + } + ], + "data": { + "foo": "bar", + "baz": "bam" + }, + "parameters": {} + } + } + } + +.. csv-table:: + :header: "Property Name", "Value", "Description" + :widths: 20, 15, 65 + + "extends", "string", "Extend from another operation by name. The parent operation must be defined before the child." + "httpMethod", "string", "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" + "uri", "string", "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" + "summary", "string", "Short summary of what the operation does" + "class", "string", "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand. Using this attribute allows you to define an operation using a service description, but allows more customized logic to be implemented in user-land code." + "responseClass", "string", "Defined what is returned from the method. Can be a primitive, class name, or model name. You can specify the name of a class to return a more customized result from the operation (for example, a domain model object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``." + "responseNotes", "string", "A description of the response returned by the operation" + "responseType", "string", "The type of response that the operation creates: one of primitive, class, model, or documentation. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default." + "deprecated", "boolean", "Whether or not the operation is deprecated" + "errorResponses", "array", "Errors that could occur while executing the operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), 'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this error is encountered)" + "data", "object", "Any arbitrary data to associate with the operation" + "parameters", "object containing :ref:`parameter-schema` objects", "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." + "additionalParameters", "A single :ref:`parameter-schema` object", "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." + +additionalParameters +~~~~~~~~~~~~~~~~~~~~ + +When a webservice offers a large number of parameters that all are set in the same location (for example the query +string or a JSON document), defining each parameter individually can require a lot of time and repetition. Furthermore, +some web services allow for completely arbitrary parameters to be supplied for an operation. The +``additionalParameters`` attribute can be used to solve both of these issues. + +As an example, we can define a Twitter API operation quite easily using ``additionalParameters``. The +GetMentions operation accepts a large number of query string parameters. Defining each of these parameters +is ideal because it provide much more introspection for the client and opens the possibility to use the description with +other tools (e.g. a documentation generator). However, you can very quickly provide a "catch-all" serialization rule +that will place any custom parameters supplied to an operation the generated request's query string parameters. + +.. class:: overflow-height-250px + + .. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "responseClass": "GetMentionsOutput", + "additionalParameters": { + "location": "query" + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +responseClass +~~~~~~~~~~~~~ + +The ``responseClass`` attribute is used to define the return value of an operation (what is returned by calling the +``getResult()`` method of a command object). The value set in the responseClass attribute can be one of "primitive" +(meaning the result with be primitive type like a string), a class name meaning the result will be an instance of a +specific user-land class, or a model name meaning the result will be a ``Guzzle\Service\Resource\Model`` object that +uses a :ref:`model schema ` to define how the HTTP response is parsed. + +.. note:: + + Using a class name with a ``responseClass`` will only work if it is supported by the ``class`` that is instantiated + for the operation. Keep this in mind when specifying a custom ``class`` attribute that points to a custom + ``Guzzle\Service\Command\CommandInterface`` class. The default ``class``, + ``Guzzle\Service\Command\OperationCommand``, does support setting custom ``class`` attributes. + +You can specify the name of a class to return a more customized result from the operation (for example, a domain model +object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``. +Here's a very simple example of implementing a custom responseClass object. + +.. code-block:: json + + { + "operations": { + "test": { + "responseClass": "MyApplication\\User" + } + } + } + +.. code-block:: php + + namespace MyApplication; + + use Guzzle\Service\Command\ResponseClassInterface; + use Guzzle\Service\Command\OperationCommand; + + class User implements ResponseClassInterface + { + protected $name; + + public static function fromCommand(OperationCommand $command) + { + $response = $command->getResponse(); + $xml = $response->xml(); + + return new self((string) $xml->name); + } + + public function __construct($name) + { + $this->name = $name; + } + } + +errorResponses +~~~~~~~~~~~~~~ + +``errorResponses`` is an array containing objects that define the errors that could occur while executing the +operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), +'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this +error is encountered). + +ErrorResponsePlugin +^^^^^^^^^^^^^^^^^^^ + +Error responses are by default only used for documentation. If you don't need very complex exception logic for your web +service errors, then you can use the ``Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin`` to automatically throw defined +exceptions when one of the ``errorResponse`` rules are matched. The error response plugin will listen for the +``request.complete`` event of a request created by a command object. Every response (including a successful response) is +checked against the list of error responses for an exact match using the following order of checks: + +1. Does the errorResponse have a defined ``class``? +2. Is the errorResponse ``code`` equal to the status code of the response? +3. Is the errorResponse ``reason`` equal to the reason phrase of the response? +4. Throw the exception stored in the ``class`` attribute of the errorResponse. + +The ``class`` attribute must point to a class that implements +``Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface``. This interface requires that an error response class +implements ``public static function fromCommand(CommandInterface $command, Response $response)``. This method must +return an object that extends from ``\Exception``. After an exception is returned, it is thrown by the plugin. + +.. _parameter-schema: + +Parameter schema +---------------- + +Parameters in both operations and models are represented using the +`JSON schema `_ syntax. + +.. csv-table:: + :header: "Property Name", "Value", "Description" + :widths: 20, 15, 65 + + "name", "string", "Unique name of the parameter" + "type", "string|array", "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." + "instanceOf", "string", "When the type is an object, you can specify the class that the object must implement" + "required", "boolean", "Whether or not the parameter is required" + "default", "mixed", "Default value to use if no value is supplied" + "static", "boolean", "Set to true to specify that the parameter value cannot be changed from the default setting" + "description", "string", "Documentation of the parameter" + "location", "string", "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" + "sentAs", "string", "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." + "filters", "array", "Array of functions to to run a parameter value through." + +filters +~~~~~~~ + +Each value in the array must be a string containing the full class path to a static method or an array of complex +filter information. You can specify static methods of classes using the full namespace class name followed by +"::" (e.g. ``FooBar::baz()``). Some filters require arguments in order to properly filter a value. For complex filters, +use an object containing a ``method`` attribute pointing to a function, and an ``args`` attribute containing an +array of positional arguments to pass to the function. Arguments can contain keywords that are replaced when filtering +a value: ``@value`` is replaced with the value being filtered, and ``@api`` is replaced with the actual Parameter +object. + +.. code-block:: json + + { + "filters": [ + "strtolower", + { + "method": "MyClass::convertString", + "args": [ "test", "@value", "@api" ] + } + ] + } + +The above example will filter a parameter using ``strtolower``. It will then call the ``convertString`` static method +of ``MyClass``, passing in "test", the actual value of the parameter, and a ``Guzzle\Service\Description\Parameter`` +object. + +Operation parameter location attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The location field of top-level parameters control how a parameter is serialized when generating a request. + +uri location +^^^^^^^^^^^^ + +Parameters are injected into the ``uri`` attribute of the operation using +`URI-template expansion `_. + +.. code-block:: json + + { + "operations": { + "uriTest": { + "uri": "/test/{testValue}", + "parameters": { + "testValue": { + "location": "uri" + } + } + } + } + } + +query location +^^^^^^^^^^^^^^ + +Parameters are injected into the query string of a request. Query values can be nested, which would result in a PHP +style nested query string. The name of a parameter is the default name of the query string parameter added to the +request. You can override this behavior by specifying the ``sentAs`` attribute on the parameter. + +.. code-block:: json + + { + "operations": { + "queryTest": { + "parameters": { + "testValue": { + "location": "query", + "sentAs": "test_value" + } + } + } + } + } + +header location +^^^^^^^^^^^^^^^ + +Parameters are injected as headers on an HTTP request. The name of the parameter is used as the name of the header by +default. You can change the name of the header created by the parameter using the ``sentAs`` attribute. + +Headers that are of type ``object`` will be added as multiple headers to a request using the key of the input array as +the header key. Setting a ``sentAs`` attribute along with a type ``object`` will use the value of ``sentAs`` as a +prefix for each header key. + +body location +^^^^^^^^^^^^^ + +Parameters are injected as the body of a request. The input of these parameters may be anything that can be cast to a +string or a ``Guzzle\Http\EntityBodyInterface`` object. + +postField location +^^^^^^^^^^^^^^^^^^ + +Parameters are inserted as POST fields in a request. Nested values may be supplied and will be represented using +PHP style nested query strings. The POST field name is the same as the parameter name by default. You can use the +``sentAs`` parameter to override the POST field name. + +postFile location +^^^^^^^^^^^^^^^^^ + +Parameters are added as POST files. A postFile value may be a string pointing to a local filename or a +``Guzzle\Http\Message\PostFileInterface`` object. The name of the POST file will be the name of the parameter by +default. You can use a custom POST file name by using the ``sentAs`` attribute. + +Supports "string" and "array" types. + +json location +^^^^^^^^^^^^^ + +Parameters are added to the body of a request as top level keys of a JSON document. Nested values may be specified, +with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When JSON parameters are specified, the +``Content-Type`` of the request will change to ``application/json`` if a ``Content-Type`` has not already been specified +on the request. + +xml location +^^^^^^^^^^^^ + +Parameters are added to the body of a request as top level nodes of an XML document. Nested values may be specified, +with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When XML parameters are specified, the +``Content-Type`` of the request will change to ``application/xml`` if a ``Content-Type`` has not already been specified +on the request. + +responseBody location +^^^^^^^^^^^^^^^^^^^^^ + +Specifies the EntityBody of a response. This can be used to download the response body to a file or a custom Guzzle +EntityBody object. + +No location +^^^^^^^^^^^ + +If a parameter has no location attribute, then the parameter is simply used as a data value. + +Other locations +^^^^^^^^^^^^^^^ + +Custom locations can be registered as new locations or override default locations if needed. + +.. _model-schema: + +Model Schema +------------ + +Models are used in service descriptions to provide generic JSON schema definitions that can be extended from or used in +``$ref`` attributes. Models can also be referenced in a ``responseClass`` attribute to provide valuable output to an +operation. Models are JSON schema documents and use the exact syntax and attributes used in parameters. + +Response Models +~~~~~~~~~~~~~~~ + +Response models describe how a response is parsed into a ``Guzzle\Service\Resource\Model`` object. Response models are +always modeled as JSON schema objects. When an HTTP response is parsed using a response model, the rules specified on +each property of a response model will translate 1:1 as keys in a PHP associative array. When a ``sentAs`` attribute is +found in response model parameters, the value retrieved from the HTTP response is retrieved using the ``sentAs`` +parameter but stored in the response model using the name of the parameter. + +The location field of top-level parameters in a response model tell response parsers how data is retrieved from a +response. + +statusCode location +^^^^^^^^^^^^^^^^^^^ + +Retrieves the status code of the response. + +reasonPhrase location +^^^^^^^^^^^^^^^^^^^^^ + +Retrieves the reason phrase of the response. + +header location +^^^^^^^^^^^^^^^ + +Retrieves a header from the HTTP response. + +body location +^^^^^^^^^^^^^ + +Retrieves the body of an HTTP response. + +json location +^^^^^^^^^^^^^ + +Retrieves a top-level parameter from a JSON document contained in an HTTP response. + +You can use ``additionalProperties`` if the JSON document is wrapped in an outer array. This allows you to parse the +contents of each item in the array using the parsing rules defined in the ``additionalProperties`` schema. + +xml location +^^^^^^^^^^^^ + +Retrieves a top-level node value from an XML document contained in an HTTP response. + +Other locations +^^^^^^^^^^^^^^^ + +Custom locations can be registered as new locations or override default locations if needed. + +Example service description +--------------------------- + +Let's say you're interacting with a web service called 'Foo' that allows for the following routes and methods:: + + GET/POST /users + GET/DELETE /users/:id + +The following JSON service description implements this simple web service: + +.. class:: overflow-height-500px + + .. code-block:: json + + { + "name": "Foo", + "apiVersion": "2012-10-14", + "baseUrl": "http://api.foo.com", + "description": "Foo is an API that allows you to Baz Bar", + "operations": { + "GetUsers": { + "httpMethod": "GET", + "uri": "/users", + "summary": "Gets a list of users", + "responseClass": "GetUsersOutput" + }, + "CreateUser": { + "httpMethod": "POST", + "uri": "/users", + "summary": "Creates a new user", + "responseClass": "CreateUserOutput", + "parameters": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + }, + "GetUser": { + "httpMethod": "GET", + "uri": "/users/{id}", + "summary": "Retrieves a single user", + "responseClass": "GetUserOutput", + "parameters": { + "id": { + "location": "uri", + "description": "User to retrieve by ID", + "required": true + } + } + }, + "DeleteUser": { + "httpMethod": "DELETE", + "uri": "/users/{id}", + "summary": "Deletes a user", + "responseClass": "DeleteUserOutput", + "parameters": { + "id": { + "location": "uri", + "description": "User to delete by ID", + "required": true + } + } + } + }, + "models": { + "GetUsersOutput": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + } + }, + "CreateUserOutput": { + "type": "object", + "properties": { + "id": { + "location": "json", + "type": "string" + }, + "location": { + "location": "header", + "sentAs": "Location", + "type": "string" + } + } + }, + "GetUserOutput": { + "type": "object", + "properties": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + }, + "DeleteUserOutput": { + "type": "object", + "properties": { + "status": { + "location": "statusCode", + "type": "integer" + } + } + } + } + } + +If you attach this service description to a client, you would completely configure the client to interact with the +Foo web service and provide valuable response models for each operation. + +.. code-block:: php + + use Guzzle\Service\Description\ServiceDescription; + + $description = ServiceDescription::factory('/path/to/client.json'); + $client->setDescription($description); + + $command = $client->getCommand('DeleteUser', array('id' => 123)); + $responseModel = $client->execute($command); + echo $responseModel['status']; + +.. note:: + + You can add the service description to your client's factory method or constructor. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst new file mode 100755 index 000000000..b7113d68b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst @@ -0,0 +1,316 @@ +======================= +Using a service builder +======================= + +The best way to instantiate Guzzle web service clients is to let Guzzle handle building the clients for you using a +ServiceBuilder. A ServiceBuilder is responsible for creating concrete client objects based on configuration settings +and helps to manage credentials for different environments. + +You don't have to use a service builder, but they help to decouple your application from concrete classes and help to +share configuration data across multiple clients. Consider the following example. Here we are creating two clients that +require the same API public key and secret key. The clients are created using their ``factory()`` methods. + +.. code-block:: php + + use MyService\FooClient; + use MyService\BarClient; + + $foo = FooClient::factory(array( + 'key' => 'abc', + 'secret' => '123', + 'custom' => 'and above all' + )); + + $bar = BarClient::factory(array( + 'key' => 'abc', + 'secret' => '123', + 'custom' => 'listen to me' + )); + +The redundant specification of the API keys can be removed using a service builder. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + $builder = ServiceBuilder::factory(array( + 'services' => array( + 'abstract_client' => array( + 'params' => array( + 'key' => 'abc', + 'secret' => '123' + ) + ), + 'foo' => array( + 'extends' => 'abstract_client', + 'class' => 'MyService\FooClient', + 'params' => array( + 'custom' => 'and above all' + ) + ), + 'bar' => array( + 'extends' => 'abstract_client', + 'class' => 'MyService\FooClient', + 'params' => array( + 'custom' => 'listen to me' + ) + ) + ) + )); + + $foo = $builder->get('foo'); + $bar = $builder->get('bar'); + +You can make managing your API keys even easier by saving the service builder configuration in a JSON format in a +.json file. + +Creating a service builder +-------------------------- + +A ServiceBuilder can source information from an array, an PHP include file that returns an array, or a JSON file. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + // Source service definitions from a JSON file + $builder = ServiceBuilder::factory('services.json'); + +Sourcing data from an array +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Data can be source from a PHP array. The array must contain an associative ``services`` array that maps the name of a +client to the configuration information used by the service builder to create the client. Clients are given names +which are used to identify how a client is retrieved from a service builder. This can be useful for using multiple +accounts for the same service or creating development clients vs. production clients. + +.. code-block:: php + + $services = array( + 'includes' => array( + '/path/to/other/services.json', + '/path/to/other/php_services.php' + ), + 'services' => array( + 'abstract.foo' => array( + 'params' => array( + 'username' => 'foo', + 'password' => 'bar' + ) + ), + 'bar' => array( + 'extends' => 'abstract.foo', + 'class' => 'MyClientClass', + 'params' => array( + 'other' => 'abc' + ) + ) + ) + ); + +A service builder configuration array contains two top-level array keys: + ++------------+---------------------------------------------------------------------------------------------------------+ +| Key | Description | ++============+=========================================================================================================+ +| includes | Array of paths to JSON or PHP include files to include in the configuration. | ++------------+---------------------------------------------------------------------------------------------------------+ +| services | Associative array of defined services that can be created by the service builder. Each service can | +| | contain the following keys: | +| | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | Key | Description | | +| | +============+========================================================================================+ | +| | | class | The concrete class to instantiate that implements the | | +| | | | ``Guzzle\Common\FromConfigInterface``. | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | extends | The name of a previously defined service to extend from | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | params | Associative array of parameters to pass to the factory method of the service it is | | +| | | | instantiated | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | alias | An alias that can be used in addition to the array key for retrieving a client from | | +| | | | the service builder. | | +| | +------------+----------------------------------------------------------------------------------------+ | ++------------+---------------------------------------------------------------------------------------------------------+ + +The first client defined, ``abstract.foo``, is used as a placeholder of shared configuration values. Any service +extending abstract.foo will inherit its params. As an example, this can be useful when clients share the same username +and password. + +The next client, ``bar``, extends from ``abstract.foo`` using the ``extends`` attribute referencing the client from +which to extend. Additional parameters can be merged into the original service definition when extending a parent +service. + +.. important:: + + Each client that you intend to instantiate must specify a ``class`` attribute that references the full class name + of the client being created. The class referenced in the ``class`` parameter must implement a static ``factory()`` + method that accepts an array or ``Guzzle\Common\Collection`` object and returns an instantiated object. + +Sourcing from a PHP include +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create service builder configurations using a PHP include file. This can be useful if you wish to take +advantage of an opcode cache like APC to speed up the process of loading and processing the configuration. The PHP +include file is the same format as an array, but you simply create a PHP script that returns an array and save the +file with the .php file extension. + +.. code-block:: php + + '...'); + // Saved as config.php + +This configuration file can then be used with a service builder. + +.. code-block:: php + + $builder = ServiceBuilder::factory('/path/to/config.php'); + +Sourcing from a JSON document +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use JSON documents to serialize your service descriptions. The JSON format uses the exact same structure as +the PHP array syntax, but it's just serialized using JSON. + +.. code-block:: javascript + + { + "includes": ["/path/to/other/services.json", "/path/to/other/php_services.php"], + "services": { + "abstract.foo": { + "params": { + "username": "foo", + "password": "bar" + } + }, + "bar": { + "extends": "abstract.foo", + "class": "MyClientClass", + "params": { + "other": "abc" + } + } + } + } + +Referencing other clients in parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If one of your clients depends on another client as one of its parameters, you can reference that client by name by +enclosing the client's reference key in ``{}``. + +.. code-block:: javascript + + { + "services": { + "token": { + "class": "My\Token\TokenFactory", + "params": { + "access_key": "xyz" + } + }, + "client": { + "class": "My\Client", + "params": { + "token_client": "{token}", + "version": "1.0" + } + } + } + } + +When ``client`` is constructed by the service builder, the service builder will first create the ``token`` service +and then inject the token service into ``client``'s factory method in the ``token_client`` parameter. + +Retrieving clients from a service builder +----------------------------------------- + +Clients are referenced using a customizable name you provide in your service definition. The ServiceBuilder is a sort +of multiton object-- it will only instantiate a client once and return that client for subsequent retrievals. Clients +are retrieved by name (the array key used in the configuration) or by the ``alias`` setting of a service. + +Here's an example of retrieving a client from your ServiceBuilder: + +.. code-block:: php + + $client = $builder->get('foo'); + + // You can also use the ServiceBuilder object as an array + $client = $builder['foo']; + +Creating throwaway clients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get a "throwaway" client (a client that is not persisted by the ServiceBuilder) by passing ``true`` in the +second argument of ``ServiceBuilder::get()``. This allows you to create a client that will not be returned by other +parts of your code that use the service builder. Instead of passing ``true``, you can pass an array of configuration +settings that will override the configuration settings specified in the service builder. + +.. code-block:: php + + // Get a throwaway client and overwrite the "custom" setting of the client + $foo = $builder->get('foo', array( + 'custom' => 'in this world there are rules' + )); + +Getting raw configuration settings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get the raw configuration settings provided to the service builder for a specific service using the +``getData($name)`` method of a service builder. This method will null if the service was not found in the service +builder or an array of configuration settings if the service was found. + +.. code-block:: php + + $data = $builder->getData('foo'); + echo $data['key'] . "\n"; + echo $data['secret'] . "\n"; + echo $data['custom'] . "\n"; + +Adding a plugin to all clients +------------------------------ + +You can add a plugin to all clients created by a service builder using the ``addGlobalPlugin($plugin)`` method of a +service builder and passing a ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` object. The service builder +will then attach each global plugin to every client as it is created. This allows you to, for example, add a LogPlugin +to every request created by a service builder for easy debugging. + +.. code-block:: php + + use Guzzle\Plugin\Log\LogPlugin; + + // Add a debug log plugin to every client as it is created + $builder->addGlobalPlugin(LogPlugin::getDebugPlugin()); + + $foo = $builder->get('foo'); + $foo->get('/')->send(); + // Should output all of the data sent over the wire + +.. _service-builder-events: + +Events emitted from a service builder +------------------------------------- + +A ``Guzzle\Service\Builder\ServiceBuilder`` object emits the following events: + ++-------------------------------+--------------------------------------------+-----------------------------------------+ +| Event name | Description | Event data | ++===============================+============================================+=========================================+ +| service_builder.create_client | Called when a client is created | * client: The created client object | ++-------------------------------+--------------------------------------------+-----------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Service\Builder\ServiceBuilder; + + $builder = ServiceBuilder::factory('/path/to/config.json'); + + // Add an event listener to print out each client client as it is created + $builder->getEventDispatcher()->addListener('service_builder.create_client', function (Event $e) { + echo 'Client created: ' . get_class($e['client']) . "\n"; + }); + + $foo = $builder->get('foo'); + // Should output the class used for the "foo" client diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst new file mode 100755 index 000000000..7ec771e1c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst @@ -0,0 +1,659 @@ +====================== +The web service client +====================== + +The ``Guzzle\Service`` namespace contains various abstractions that help to make it easier to interact with a web +service API, including commands, service descriptions, and resource iterators. + +In this chapter, we'll build a simple `Twitter API client `_. + +Creating a client +================= + +A class that extends from ``Guzzle\Service\Client`` or implements ``Guzzle\Service\ClientInterface`` must implement a +``factory()`` method in order to be used with a :doc:`service builder `. + +Factory method +-------------- + +You can use the ``factory()`` method of a client directly if you do not need a service builder. + +.. code-block:: php + + use mtdowling\TwitterClient; + + // Create a client and pass an array of configuration data + $twitter = TwitterClient::factory(array( + 'consumer_key' => '****', + 'consumer_secret' => '****', + 'token' => '****', + 'token_secret' => '****' + )); + +.. note:: + + If you'd like to follow along, here's how to get your Twitter API credentials: + + 1. Visit https://dev.twitter.com/apps + 2. Click on an application that you've created + 3. Click on the "OAuth tool" tab + 4. Copy all of the settings under "OAuth Settings" + +Implementing a factory method +----------------------------- + +Creating a client and its factory method is pretty simple. You just need to implement ``Guzzle\Service\ClientInterface`` +or extend from ``Guzzle\Service\Client``. + +.. code-block:: php + + namespace mtdowling; + + use Guzzle\Common\Collection; + use Guzzle\Plugin\Oauth\OauthPlugin; + use Guzzle\Service\Client; + use Guzzle\Service\Description\ServiceDescription; + + /** + * A simple Twitter API client + */ + class TwitterClient extends Client + { + public static function factory($config = array()) + { + // Provide a hash of default client configuration options + $default = array('base_url' => 'https://api.twitter.com/1.1'); + + // The following values are required when creating the client + $required = array( + 'base_url', + 'consumer_key', + 'consumer_secret', + 'token', + 'token_secret' + ); + + // Merge in default settings and validate the config + $config = Collection::fromConfig($config, $default, $required); + + // Create a new Twitter client + $client = new self($config->get('base_url'), $config); + + // Ensure that the OauthPlugin is attached to the client + $client->addSubscriber(new OauthPlugin($config->toArray())); + + return $client; + } + } + +Service Builder +--------------- + +A service builder is used to easily create web service clients, provides a simple configuration driven approach to +creating clients, and allows you to share configuration settings across multiple clients. You can find out more about +Guzzle's service builder in :doc:`using-the-service-builder`. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + // Create a service builder and provide client configuration data + $builder = ServiceBuilder::factory('/path/to/client_config.json'); + + // Get the client from the service builder by name + $twitter = $builder->get('twitter'); + +The above example assumes you have JSON data similar to the following stored in "/path/to/client_config.json": + +.. code-block:: json + + { + "services": { + "twitter": { + "class": "mtdowling\\TwitterClient", + "params": { + "consumer_key": "****", + "consumer_secret": "****", + "token": "****", + "token_secret": "****" + } + } + } + } + +.. note:: + + A service builder becomes much more valuable when using multiple web service clients in a single application or + if you need to utilize the same client with varying configuration settings (e.g. multiple accounts). + +Commands +======== + +Commands are a concept in Guzzle that helps to hide the underlying implementation of an API by providing an easy to use +parameter driven object for each action of an API. A command is responsible for accepting an array of configuration +parameters, serializing an HTTP request, and parsing an HTTP response. Following the +`command pattern `_, commands in Guzzle offer a greater level of +flexibility when implementing and utilizing a web service client. + +Executing commands +------------------ + +You must explicitly execute a command after creating a command using the ``getCommand()`` method. A command has an +``execute()`` method that may be called, or you can use the ``execute()`` method of a client object and pass in the +command object. Calling either of these execute methods will return the result value of the command. The result value is +the result of parsing the HTTP response with the ``process()`` method. + +.. code-block:: php + + // Get a command from the client and pass an array of parameters + $command = $twitter->getCommand('getMentions', array( + 'count' => 5 + )); + + // Other parameters can be set on the command after it is created + $command['trim_user'] = false; + + // Execute the command using the command object. + // The result value contains an array of JSON data from the response + $result = $command->execute(); + + // You can retrieve the result of the command later too + $result = $command->getResult(). + +Command object also contains methods that allow you to inspect the HTTP request and response that was utilized with +the command. + +.. code-block:: php + + $request = $command->getRequest(); + $response = $command->getResponse(); + +.. note:: + + The format and notation used to retrieve commands from a client can be customized by injecting a custom command + factory, ``Guzzle\Service\Command\Factory\FactoryInterface``, on the client using ``$client->setCommandFactory()``. + +Executing with magic methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When using method missing magic methods with a command, the command will be executed right away and the result of the +command is returned. + +.. code-block:: php + + $jsonData = $twitter->getMentions(array( + 'count' => 5, + 'trim_user' => true + )); + +Creating commands +----------------- + +Commands are created using either the ``getCommand()`` method of a client or a magic missing method of a client. Using +the ``getCommand()`` method allows you to create a command without executing it, allowing for customization of the +command or the request serialized by the command. + +When a client attempts to create a command, it uses the client's ``Guzzle\Service\Command\Factory\FactoryInterface``. +By default, Guzzle will utilize a command factory that first looks for a concrete class for a particular command +(concrete commands) followed by a command defined by a service description (operation commands). We'll learn more about +concrete commands and operation commands later in this chapter. + +.. code-block:: php + + // Get a command from the twitter client. + $command = $twitter->getCommand('getMentions'); + $result = $command->execute(); + +Unless you've skipped ahead, running the above code will throw an exception. + + PHP Fatal error: Uncaught exception 'Guzzle\Common\Exception\InvalidArgumentException' with message + 'Command was not found matching getMentions' + +This exception was thrown because the "getMentions" command has not yet been implemented. Let's implement one now. + +Concrete commands +~~~~~~~~~~~~~~~~~ + +Commands can be created in one of two ways: create a concrete command class that extends +``Guzzle\Service\Command\AbstractCommand`` or +:doc:`create an OperationCommand based on a service description `. The recommended +approach is to use a service description to define your web service, but you can use concrete commands when custom +logic must be implemented for marshaling or unmarshaling a HTTP message. + +Commands are the method in which you abstract away the underlying format of the requests that need to be sent to take +action on a web service. Commands in Guzzle are meant to be built by executing a series of setter methods on a command +object. Commands are only validated right before they are executed. A ``Guzzle\Service\Client`` object is responsible +for executing commands. Commands created for your web service must implement +``Guzzle\Service\Command\CommandInterface``, but it's easier to extend the ``Guzzle\Service\Command\AbstractCommand`` +class, implement the ``build()`` method, and optionally implement the ``process()`` method. + +Serializing requests +^^^^^^^^^^^^^^^^^^^^ + +The ``build()`` method of a command is responsible for using the arguments of the command to build and serialize a +HTTP request and set the request on the ``$request`` property of the command object. This step is usually taken care of +for you when using a service description driven command that uses the default +``Guzzle\Service\Command\OperationCommand``. You may wish to implement the process method yourself when you aren't +using a service description or need to implement more complex request serialization. + +.. important:::: + + When implementing a custom ``build()`` method, be sure to set the class property of ``$this->request`` to an + instantiated and ready to send request. + +The following example shows how to implement the ``getMentions`` +`Twitter API `_ method using a concrete command. + +.. code-block:: php + + namespace mtdowling\Twitter\Command; + + use Guzzle\Service\Command\AbstractCommand; + + class GetMentions extends AbstractCommand + { + protected function build() + { + // Create the request property of the command + $this->request = $this->client->get('statuses/mentions_timeline.json'); + + // Grab the query object of the request because we will use it for + // serializing command parameters on the request + $query = $this->request->getQuery(); + + if ($this['count']) { + $query->set('count', $this['count']); + } + + if ($this['since_id']) { + $query->set('since_id', $this['since_id']); + } + + if ($this['max_id']) { + $query->set('max_id', $this['max_id']); + } + + if ($this['trim_user'] !== null) { + $query->set('trim_user', $this['trim_user'] ? 'true' : 'false'); + } + + if ($this['contributor_details'] !== null) { + $query->set('contributor_details', $this['contributor_details'] ? 'true' : 'false'); + } + + if ($this['include_entities'] !== null) { + $query->set('include_entities', $this['include_entities'] ? 'true' : 'false'); + } + } + } + +By default, a client will attempt to find concrete command classes under the ``Command`` namespace of a client. First +the client will attempt to find an exact match for the name of the command to the name of the command class. If an +exact match is not found, the client will calculate a class name using inflection. This is calculated based on the +folder hierarchy of a command and converting the CamelCased named commands into snake_case. Here are some examples on +how the command names are calculated: + +#. ``Foo\Command\JarJar`` **->** jar_jar +#. ``Foo\Command\Test`` **->** test +#. ``Foo\Command\People\GetCurrentPerson`` **->** people.get_current_person + +Notice how any sub-namespace beneath ``Command`` is converted from ``\`` to ``.`` (a period). CamelCasing is converted +to lowercased snake_casing (e.g. JarJar == jar_jar). + +Parsing responses +^^^^^^^^^^^^^^^^^ + +The ``process()`` method of a command is responsible for converting an HTTP response into something more useful. For +example, a service description operation that has specified a model object in the ``responseClass`` attribute of the +operation will set a ``Guzzle\Service\Resource\Model`` object as the result of the command. This behavior can be +completely modified as needed-- even if you are using operations and responseClass models. Simply implement a custom +``process()`` method that sets the ``$this->result`` class property to whatever you choose. You can reuse parts of the +default Guzzle response parsing functionality or get inspiration from existing code by using +``Guzzle\Service\Command\OperationResponseParser`` and ``Guzzle\Service\Command\DefaultResponseParser`` classes. + +If you do not implement a custom ``process()`` method and are not using a service description, then Guzzle will attempt +to guess how a response should be processed based on the Content-Type header of the response. Because the Twitter API +sets a ``Content-Type: application/json`` header on this response, we do not need to implement any custom response +parsing. + +Operation commands +~~~~~~~~~~~~~~~~~~ + +Operation commands are commands in which the serialization of an HTTP request and the parsing of an HTTP response are +driven by a Guzzle service description. Because request serialization, validation, and response parsing are +described using a DSL, creating operation commands is a much faster process than writing concrete commands. + +Creating operation commands for our Twitter client can remove a great deal of redundancy from the previous concrete +command, and allows for a deeper runtime introspection of the API. Here's an example service description we can use to +create the Twitter API client: + +.. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "description": "Twitter REST API client", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "summary": "Returns the 20 most recent mentions for the authenticating user.", + "responseClass": "GetMentionsOutput", + "parameters": { + "count": { + "description": "Specifies the number of tweets to try and retrieve", + "type": "integer", + "location": "query" + }, + "since_id": { + "description": "Returns results with an ID greater than the specified ID", + "type": "integer", + "location": "query" + }, + "max_id": { + "description": "Returns results with an ID less than or equal to the specified ID.", + "type": "integer", + "location": "query" + }, + "trim_user": { + "description": "Limits the amount of data returned for each user", + "type": "boolean", + "location": "query" + }, + "contributor_details": { + "description": "Adds more data to contributor elements", + "type": "boolean", + "location": "query" + }, + "include_entities": { + "description": "The entities node will be disincluded when set to false.", + "type": "boolean", + "location": "query" + } + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +If you're lazy, you can define the API in a less descriptive manner using ``additionalParameters``. +``additionalParameters`` define the serialization and validation rules of parameters that are not explicitly defined +in a service description. + +.. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "description": "Twitter REST API client", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "summary": "Returns the 20 most recent mentions for the authenticating user.", + "responseClass": "GetMentionsOutput", + "additionalParameters": { + "location": "query" + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +You should attach the service description to the client at the end of the client's factory method: + +.. code-block:: php + + // ... + class TwitterClient extends Client + { + public static function factory($config = array()) + { + // ... same code as before ... + + // Set the service description + $client->setDescription(ServiceDescription::factory('path/to/twitter.json')); + + return $client; + } + } + +The client can now use operations defined in the service description instead of requiring you to create concrete +command classes. Feel free to delete the concrete command class we created earlier. + +.. code-block:: php + + $jsonData = $twitter->getMentions(array( + 'count' => 5, + 'trim_user' => true + )); + +Executing commands in parallel +------------------------------ + +Much like HTTP requests, Guzzle allows you to send multiple commands in parallel. You can send commands in parallel by +passing an array of command objects to a client's ``execute()`` method. The client will serialize each request and +send them all in parallel. If an error is encountered during the transfer, then a +``Guzzle\Service\Exception\CommandTransferException`` is thrown, which allows you to retrieve a list of commands that +succeeded and a list of commands that failed. + +.. code-block:: php + + use Guzzle\Service\Exception\CommandTransferException; + + $commands = array(); + $commands[] = $twitter->getCommand('getMentions'); + $commands[] = $twitter->getCommand('otherCommandName'); + // etc... + + try { + $result = $client->execute($commands); + foreach ($result as $command) { + echo $command->getName() . ': ' . $command->getResponse()->getStatusCode() . "\n"; + } + } catch (CommandTransferException $e) { + // Get an array of the commands that succeeded + foreach ($e->getSuccessfulCommands() as $command) { + echo $command->getName() . " succeeded\n"; + } + // Get an array of the commands that failed + foreach ($e->getFailedCommands() as $command) { + echo $command->getName() . " failed\n"; + } + } + +.. note:: + + All commands executed from a client using an array must originate from the same client. + +Special command options +----------------------- + +Guzzle exposes several options that help to control how commands are validated, serialized, and parsed. +Command options can be specified when creating a command or in the ``command.params`` parameter in the +``Guzzle\Service\Client``. + +=========================== ============================================================================================ +command.request_options Option used to add :ref:`Request options ` to the request created by a + command +command.hidden_params An array of the names of parameters ignored by the ``additionalParameters`` parameter schema +command.disable_validation Set to true to disable JSON schema validation of the command's input parameters +command.response_processing Determines how the default response parser will parse the command. One of "raw" no parsing, + "model" (the default method used to parse commands using response models defined in service + descriptions) +command.headers (deprecated) Option used to specify custom headers. Use ``command.request_options`` instead +command.on_complete (deprecated) Option used to add an onComplete method to a command. Use + ``command.after_send`` event instead +command.response_body (deprecated) Option used to change the entity body used to store a response. + Use ``command.request_options`` instead +=========================== ============================================================================================ + +Advanced client configuration +============================= + +Default command parameters +-------------------------- + +When creating a client object, you can specify default command parameters to pass into all commands. Any key value pair +present in the ``command.params`` settings of a client will be added as default parameters to any command created +by the client. + +.. code-block:: php + + $client = new Guzzle\Service\Client(array( + 'command.params' => array( + 'default_1' => 'foo', + 'another' => 'bar' + ) + )); + +Magic methods +------------- + +Client objects will, by default, attempt to create and execute commands when a missing method is invoked on a client. +This powerful concept applies to both concrete commands and operation commands powered by a service description. This +makes it appear to the end user that you have defined actual methods on a client object, when in fact, the methods are +invoked using PHP's magic ``__call`` method. + +The ``__call`` method uses the ``getCommand()`` method of a client, which uses the client's internal +``Guzzle\Service\Command\Factory\FactoryInterface`` object. The default command factory allows you to instantiate +operations defined in a client's service description. The method in which a client determines which command to +execute is defined as follows: + +1. The client will first try to find a literal match for an operation in the service description. +2. If the literal match is not found, the client will try to uppercase the first character of the operation and find + the match again. +3. If a match is still not found, the command factory will inflect the method name from CamelCase to snake_case and + attempt to find a matching command. +4. If a command still does not match, an exception is thrown. + +.. code-block:: php + + // Use the magic method + $result = $twitter->getMentions(); + + // This is exactly the same as: + $result = $twitter->getCommand('getMentions')->execute(); + +You can disable magic methods on a client by passing ``false`` to the ``enableMagicMethod()`` method. + +Custom command factory +---------------------- + +A client by default uses the ``Guzzle\Service\Command\Factory\CompositeFactory`` which allows multiple command +factories to attempt to create a command by a certain name. The default CompositeFactory uses a ``ConcreteClassFactory`` +and a ``ServiceDescriptionFactory`` if a service description is specified on a client. You can specify a custom +command factory if your client requires custom command creation logic using the ``setCommandFactory()`` method of +a client. + +Custom resource Iterator factory +-------------------------------- + +Resource iterators can be retrieved from a client using the ``getIterator($name)`` method of a client. This method uses +a client's internal ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object. A client by default uses a +``Guzzle\Service\Resource\ResourceIteratorClassFactory`` to attempt to find concrete classes that implement resource +iterators. The default factory will first look for matching iterators in the ``Iterator`` subdirectory of the client +followed by the ``Model`` subdirectory of a client. Use the ``setResourceIteratorFactory()`` method of a client to +specify a custom resource iterator factory. + +Plugins and events +================== + +``Guzzle\Service\Client`` exposes various events that allow you to hook in custom logic. A client object owns a +``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling +``$client->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or +event subscribers (classes that listen to specific events of a dispatcher). + +.. _service-client-events: + +Events emitted from a Service Client +------------------------------------ + +A ``Guzzle\Service\Client`` object emits the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| client.command.create | The client created a command object | * client: Client object | +| | | * command: Command object | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.before_prepare | Before a command is validated and built. | * command: Command being prepared | +| | This is also before a request is created. | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.after_prepare | After a command instantiates and | * command: Command that was prepared | +| | configures its request object. | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.before_send | The client is about to execute a prepared | * command: Command to execute | +| | command | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.after_send | The client successfully completed | * command: The command that was executed | +| | executing a command | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.parse_response | Called when ``responseType`` is ``class`` | * command: The command with a response | +| | and the response is about to be parsed. | about to be parsed. | ++------------------------------+--------------------------------------------+------------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Service\Client; + + $client = new Client(); + + // create an event listener that operates on request objects + $client->getEventDispatcher()->addListener('command.after_prepare', function (Event $event) { + $command = $event['command']; + $request = $command->getRequest(); + + // do something with request + }); + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Common\Client; + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + class EventSubscriber implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + return array( + 'client.command.create' => 'onCommandCreate', + 'command.parse_response' => 'onParseResponse' + ); + } + + public function onCommandCreate(Event $event) + { + $client = $event['client']; + $command = $event['command']; + // operate on client and command + } + + public function onParseResponse(Event $event) + { + $command = $event['command']; + // operate on the command + } + } + + $client = new Client(); + + $client->addSubscriber(new EventSubscriber()); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phar-stub.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phar-stub.php new file mode 100755 index 000000000..cc2b53f4f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phar-stub.php @@ -0,0 +1,16 @@ +registerNamespaces(array( + 'Guzzle' => 'phar://guzzle.phar/src', + 'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher', + 'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib', + 'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src' +)); +$classLoader->register(); + +__HALT_COMPILER(); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/build.properties.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/build.properties.dist new file mode 100755 index 000000000..c60d3d9cf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/build.properties.dist @@ -0,0 +1,16 @@ +# you may need to update this if you're working on a fork. +guzzle.remote=git@github.com:guzzle/guzzle.git + +# github credentials -- only used by GitHub API calls to create subtree repos +github.basicauth=username:password +# for the subtree split and testing +github.org=guzzle + +# your git path +cmd.git=git + +# your composer command +cmd.composer=composer + +# test server start +cmd.testserver=node diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/dependencies.xml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/dependencies.xml new file mode 100755 index 000000000..e40e037c2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/dependencies.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + using git at ${cmd.git} + + + + found git at ${cmd.git} + + + + + + + + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/deploy.xml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/deploy.xml new file mode 100755 index 000000000..109e5ec4f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/imports/deploy.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + On branch ${head} + + + + + + + + + + working directory clean + + + ${git.status} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChangeLog Match: ${version.changelog} + Guzzle\Common\Version Match: ${version.version} + + + + releasing: phing -Dnew.version=3.0.x -Dhead=master release + -- + + + + + + + + + + + + + + + BEGINNING RELEASE FOR ${new.version} + + + + + + + + + + + + + + + + + + + + + + + + Tip: to create a new release, do: phing -Dnew.version=[TAG] -Dhead=[BRANCH] release + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php new file mode 100755 index 000000000..3b7040982 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php @@ -0,0 +1,152 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; + +class ComposerLintTask extends Task +{ + protected $dir = null; + protected $file = null; + protected $passthru = false; + protected $composer = null; + + /** + * The setter for the dir + * + * @param string $str Directory to crawl recursively for composer files + */ + public function setDir($str) + { + $this->dir = $str; + } + + /** + * The setter for the file + * + * @param string $str Individual file to validate + */ + public function setFile($str) + { + $this->file = $str; + } + + /** + * Whether to use PHP's passthru() function instead of exec() + * + * @param boolean $passthru If passthru shall be used + */ + public function setPassthru($passthru) + { + $this->passthru = (bool) $passthru; + } + + /** + * Composer to execute. If unset, will attempt composer.phar in project + * basedir, and if that fails, will attempt global composer + * installation. + * + * @param string $str Individual file to validate + */ + public function setComposer($str) + { + $this->file = $str; + } + + /** + * The init method: do init steps + */ + public function init() + { + // nothing needed here + } + + /** + * The main entry point + */ + public function main() + { + if ($this->composer === null) { + $this->findComposer(); + } + + $files = array(); + if (!empty($this->file) && file_exists($this->file)) { + $files[] = $this->file; + } + + if (!empty($this->dir)) { + $found = $this->findFiles(); + foreach ($found as $file) { + $files[] = $this->dir . DIRECTORY_SEPARATOR . $file; + } + } + + foreach ($files as $file) { + + $cmd = $this->composer . ' validate ' . $file; + $cmd = escapeshellcmd($cmd); + + if ($this->passthru) { + $retval = null; + passthru($cmd, $retval); + if ($retval == 1) { + throw new BuildException('invalid composer.json'); + } + } else { + $out = array(); + $retval = null; + exec($cmd, $out, $retval); + if ($retval == 1) { + $err = join("\n", $out); + throw new BuildException($err); + } else { + $this->log($out[0]); + } + } + + } + + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findFiles() + { + $ds = new DirectoryScanner(); + $ds->setBasedir($this->dir); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + return $ds->getIncludedFiles(); + } + + /** + * Find composer installation + * + */ + protected function findComposer() + { + $basedir = $this->project->getBasedir(); + $php = $this->project->getProperty('php.interpreter'); + + if (file_exists($basedir . '/composer.phar')) { + $this->composer = "$php $basedir/composer.phar"; + } else { + $out = array(); + exec('which composer', $out); + if (empty($out)) { + throw new BuildException( + 'Could not determine composer location.' + ); + } + $this->composer = $out[0]; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php new file mode 100755 index 000000000..f72a6b5d0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php @@ -0,0 +1,338 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; +require_once 'PEAR/PackageFileManager2.php'; +require_once 'PEAR/PackageFileManager/File.php'; +require_once 'PEAR/Packager.php'; + +class GuzzlePearPharPackageTask extends Task +{ + private $version; + private $deploy = true; + private $makephar = true; + + private $subpackages = array(); + + public function setVersion($str) + { + $this->version = $str; + } + + public function getVersion() + { + return $this->version; + } + + public function setDeploy($deploy) + { + $this->deploy = (bool) $deploy; + } + + public function getDeploy() + { + return $this->deploy; + } + + public function setMakephar($makephar) + { + $this->makephar = (bool) $makephar; + } + + public function getMakephar() + { + return $this->makephar; + } + + private $basedir; + private $guzzleinfo; + private $changelog_release_date; + private $changelog_notes = '-'; + + public function main() + { + $this->basedir = $this->getProject()->getBasedir(); + + if (!is_dir((string) $this->basedir.'/.subsplit')) { + throw new BuildException('PEAR packaging requires .subsplit directory'); + } + + // main composer file + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/composer.json'); + $this->guzzleinfo = json_decode($composer_file, true); + + // make sure we have a target + $pearwork = (string) $this->basedir . '/build/pearwork'; + if (!is_dir($pearwork)) { + mkdir($pearwork, 0777, true); + } + $pearlogs = (string) $this->basedir . '/build/artifacts/logs'; + if (!is_dir($pearlogs)) { + mkdir($pearlogs, 0777, true); + } + + $version = $this->getVersion(); + $this->grabChangelog(); + if ($version[0] == '2') { + $this->log('building single PEAR package'); + $this->buildSinglePackage(); + } else { + // $this->log("building PEAR subpackages"); + // $this->createSubPackages(); + // $this->log("building PEAR bundle package"); + $this->buildSinglePackage(); + } + + if ($this->getMakephar()) { + $this->log("building PHAR"); + $this->getProject()->executeTarget('package-phar'); + } + + if ($this->getDeploy()) { + $this->doDeployment(); + } + } + + public function doDeployment() + { + $basedir = (string) $this->basedir; + $this->log('beginning PEAR/PHAR deployment'); + + chdir($basedir . '/build/pearwork'); + if (!is_dir('./channel')) { + mkdir('./channel'); + } + + // Pull the PEAR channel down locally + passthru('aws s3 sync s3://pear.guzzlephp.org ./channel'); + + // add PEAR packages + foreach (scandir('./') as $file) { + if (substr($file, -4) == '.tgz') { + passthru('pirum add ./channel ' . $file); + } + } + + // if we have a new phar, add it + if ($this->getMakephar() && file_exists($basedir . '/build/artifacts/guzzle.phar')) { + rename($basedir . '/build/artifacts/guzzle.phar', './channel/guzzle.phar'); + } + + // Sync up with the S3 bucket + chdir($basedir . '/build/pearwork/channel'); + passthru('aws s3 sync . s3://pear.guzzlephp.org'); + } + + public function buildSinglePackage() + { + $v = $this->getVersion(); + $apiversion = $v[0] . '.0.0'; + + $opts = array( + 'packagedirectory' => (string) $this->basedir . '/.subsplit/src/', + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json'), + 'baseinstalldir' => '/', + 'packagefile' => 'package.xml' + //'outputdirectory' => (string) $this->basedir . '/build/pearwork/' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->addRole('md', 'doc'); + $pfm->addRole('pem', 'php'); + $pfm->setPackage('Guzzle'); + $pfm->setSummary("Object-oriented PHP HTTP Client for PHP 5.3+"); + $pfm->setDescription($this->guzzleinfo['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion($apiversion); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->addExtensionDep('required', 'curl'); + $pfm->setPearinstallerDep('1.4.6'); + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + if (!empty($this->subpackages)) { + foreach ($this->subpackages as $package) { + $pkg = dirname($package); + $pkg = str_replace('/', '_', $pkg); + $pfm->addConflictingPackageDepWithChannel($pkg, 'guzzlephp.org/pear', false, $apiversion); + } + } + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package.log', $log); + chdir($startdir); + } + + public function createSubPackages() + { + $this->findComponents(); + + foreach ($this->subpackages as $package) { + $baseinstalldir = dirname($package); + $dir = (string) $this->basedir.'/.subsplit/src/' . $baseinstalldir; + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/src/'. $package); + $package_info = json_decode($composer_file, true); + $this->log('building ' . $package_info['target-dir'] . ' subpackage'); + $this->buildSubPackage($dir, $baseinstalldir, $package_info); + } + } + + public function buildSubPackage($dir, $baseinstalldir, $info) + { + $package = str_replace('/', '_', $baseinstalldir); + $opts = array( + 'packagedirectory' => $dir, + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json', '*package.xml'), + 'baseinstalldir' => '/' . $info['target-dir'], + 'packagefile' => 'package.xml' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->setPackage($package); + $pfm->setSummary($info['description']); + $pfm->setDescription($info['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion('3.0.0'); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->setPearinstallerDep('1.4.6'); + + foreach ($info['require'] as $type => $version) { + if ($type == 'php') { + continue; + } + if ($type == 'symfony/event-dispatcher') { + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + } + if ($type == 'ext-curl') { + $pfm->addExtensionDep('required', 'curl'); + } + if (substr($type, 0, 6) == 'guzzle') { + $gdep = str_replace('/', ' ', $type); + $gdep = ucwords($gdep); + $gdep = str_replace(' ', '_', $gdep); + $pfm->addPackageDepWithChannel('required', $gdep, 'guzzlephp.org/pear', $this->getVersion()); + } + } + + // can't have main Guzzle package AND sub-packages + $pfm->addConflictingPackageDepWithChannel('Guzzle', 'guzzlephp.org/pear', false, $apiversion); + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'/package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'/package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package_'.$package.'.log', $log); + chdir($startdir); + } + + public function findComponents() + { + $ds = new DirectoryScanner(); + $ds->setBasedir((string) $this->basedir.'/.subsplit/src'); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + $files = $ds->getIncludedFiles(); + $this->subpackages = $files; + } + + public function grabChangelog() + { + $cl = file((string) $this->basedir.'/.subsplit/CHANGELOG.md'); + $notes = ''; + $in_version = false; + $release_date = null; + + foreach ($cl as $line) { + $line = trim($line); + if (preg_match('/^\* '.$this->getVersion().' \(([0-9\-]+)\)$/', $line, $matches)) { + $release_date = $matches[1]; + $in_version = true; + continue; + } + if ($in_version && empty($line) && empty($notes)) { + continue; + } + if ($in_version && ! empty($line)) { + $notes .= $line."\n"; + } + if ($in_version && empty($line) && !empty($notes)) { + $in_version = false; + } + } + $this->changelog_release_date = $release_date; + + if (! empty($notes)) { + $this->changelog_notes = $notes; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php new file mode 100755 index 000000000..5d56a5bd5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php @@ -0,0 +1,385 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +// base - base of tree to split out +// subIndicatorFile - composer.json, package.xml? +class GuzzleSubSplitTask extends GitBaseTask +{ + /** + * What git repository to pull from and publish to + */ + protected $remote = null; + + /** + * Publish for comma-separated heads instead of all heads + */ + protected $heads = null; + + /** + * Publish for comma-separated tags instead of all tags + */ + protected $tags = null; + + /** + * Base of the tree RELATIVE TO .subsplit working dir + */ + protected $base = null; + + /** + * The presence of this file will indicate that the directory it resides + * in is at the top level of a split. + */ + protected $subIndicatorFile = 'composer.json'; + + /** + * Do everything except actually send the update. + */ + protected $dryRun = null; + + /** + * Do not sync any heads. + */ + protected $noHeads = false; + + /** + * Do not sync any tags. + */ + protected $noTags = false; + + /** + * The splits we found in the heads + */ + protected $splits; + + public function setRemote($str) + { + $this->remote = $str; + } + + public function getRemote() + { + return $this->remote; + } + + public function setHeads($str) + { + $this->heads = explode(',', $str); + } + + public function getHeads() + { + return $this->heads; + } + + public function setTags($str) + { + $this->tags = explode(',', $str); + } + + public function getTags() + { + return $this->tags; + } + + public function setBase($str) + { + $this->base = $str; + } + + public function getBase() + { + return $this->base; + } + + public function setSubIndicatorFile($str) + { + $this->subIndicatorFile = $str; + } + + public function getSubIndicatorFile() + { + return $this->subIndicatorFile; + } + + public function setDryRun($bool) + { + $this->dryRun = (bool) $bool; + } + + public function getDryRun() + { + return $this->dryRun; + } + + public function setNoHeads($bool) + { + $this->noHeads = (bool) $bool; + } + + public function getNoHeads() + { + return $this->noHeads; + } + + public function setNoTags($bool) + { + $this->noTags = (bool) $bool; + } + + public function getNoTags() + { + return $this->noTags; + } + + /** + * GitClient from VersionControl_Git + */ + protected $client = null; + + /** + * The main entry point + */ + public function main() + { + $repo = $this->getRepository(); + if (empty($repo)) { + throw new BuildException('"repository" is a required parameter'); + } + + $remote = $this->getRemote(); + if (empty($remote)) { + throw new BuildException('"remote" is a required parameter'); + } + + chdir($repo); + $this->client = $this->getGitClient(false, $repo); + + // initalized yet? + if (!is_dir('.subsplit')) { + $this->subsplitInit(); + } else { + // update + $this->subsplitUpdate(); + } + + // find all splits based on heads requested + $this->findSplits(); + + // check that GitHub has the repos + $this->verifyRepos(); + + // execute the subsplits + $this->publish(); + } + + public function publish() + { + $this->log('DRY RUN ONLY FOR NOW'); + $base = $this->getBase(); + $base = rtrim($base, '/') . '/'; + $org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + + $splits = array(); + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + $splits[] = $base . $component . ':git@github.com:'. $org.'/'.$meta['repo']; + } + + $cmd = 'git subsplit publish '; + $cmd .= escapeshellarg(implode(' ', $splits)); + + if ($this->getNoHeads()) { + $cmd .= ' --no-heads'; + } else { + $cmd .= ' --heads='.$head; + } + + if ($this->getNoTags()) { + $cmd .= ' --no-tags'; + } else { + if ($this->getTags()) { + $cmd .= ' --tags=' . escapeshellarg(implode(' ', $this->getTags())); + } + } + + passthru($cmd); + } + } + + /** + * Runs `git subsplit update` + */ + public function subsplitUpdate() + { + $repo = $this->getRepository(); + $this->log('git-subsplit update...'); + $cmd = $this->client->getCommand('subsplit'); + $cmd->addArgument('update'); + try { + $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit update failed'. $e); + } + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar update --dev'); + chdir($repo); + } + + /** + * Runs `git subsplit init` based on the remote repository. + */ + public function subsplitInit() + { + $remote = $this->getRemote(); + $cmd = $this->client->getCommand('subsplit'); + $this->log('running git-subsplit init ' . $remote); + + $cmd->setArguments(array( + 'init', + $remote + )); + + try { + $output = $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit init failed'. $e); + } + $this->log(trim($output), Project::MSG_INFO); + $repo = $this->getRepository(); + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar install --dev'); + chdir($repo); + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findSplits() + { + $this->log("checking heads for subsplits"); + $repo = $this->getRepository(); + $base = $this->getBase(); + + $splits = array(); + $heads = $this->getHeads(); + + if (!empty($base)) { + $base = '/' . ltrim($base, '/'); + } else { + $base = '/'; + } + + chdir($repo . '/.subsplit'); + foreach ($heads as $head) { + $splits[$head] = array(); + + // check each head requested *BEFORE* the actual subtree split command gets it + passthru("git checkout '$head'"); + $ds = new DirectoryScanner(); + $ds->setBasedir($repo . '/.subsplit' . $base); + $ds->setIncludes(array('**/'.$this->subIndicatorFile)); + $ds->scan(); + $files = $ds->getIncludedFiles(); + + // Process the files we found + foreach ($files as $file) { + $pkg = file_get_contents($repo . '/.subsplit' . $base .'/'. $file); + $pkg_json = json_decode($pkg, true); + $name = $pkg_json['name']; + $component = str_replace('/composer.json', '', $file); + // keep this for split cmd + $tmpreponame = explode('/', $name); + $reponame = $tmpreponame[1]; + $splits[$head][$component]['repo'] = $reponame; + $nscomponent = str_replace('/', '\\', $component); + $splits[$head][$component]['desc'] = "[READ ONLY] Subtree split of $nscomponent: " . $pkg_json['description']; + } + } + + // go back to how we found it + passthru("git checkout master"); + chdir($repo); + $this->splits = $splits; + } + + /** + * Based on list of repositories we determined we *should* have, talk + * to GitHub and make sure they're all there. + * + */ + protected function verifyRepos() + { + $this->log('verifying GitHub target repos'); + $github_org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + $github_creds = $this->getOwningTarget()->getProject()->getProperty('github.basicauth'); + + if ($github_creds == 'username:password') { + $this->log('Skipping GitHub repo checks. Update github.basicauth in build.properties to verify repos.', 1); + return; + } + + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos?type=all'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + curl_close($ch); + $repos = json_decode($result, true); + $existing_repos = array(); + + // parse out the repos we found on GitHub + foreach ($repos as $repo) { + $tmpreponame = explode('/', $repo['full_name']); + $reponame = $tmpreponame[1]; + $existing_repos[$reponame] = $repo['description']; + } + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + + $reponame = $meta['repo']; + + if (!isset($existing_repos[$reponame])) { + $this->log("Creating missing repo $reponame"); + $payload = array( + 'name' => $reponame, + 'description' => $meta['desc'], + 'homepage' => 'http://www.guzzlephp.org/', + 'private' => true, + 'has_issues' => false, + 'has_wiki' => false, + 'has_downloads' => true, + 'auto_init' => false + ); + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + echo "Response code: ".curl_getinfo($ch, CURLINFO_HTTP_CODE)."\n"; + curl_close($ch); + } else { + $this->log("Repo $reponame exists", 2); + } + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phpunit.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phpunit.xml.dist new file mode 100755 index 000000000..208fdc08e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/phpunit.xml.dist @@ -0,0 +1,48 @@ + + + + + + ./tests/Guzzle/Tests + + + + + + + + + + ./src/Guzzle + + ./src/Guzzle + ./src/Guzzle/Common/Exception/GuzzleException.php + ./src/Guzzle/Http/Exception/HttpException.php + ./src/Guzzle/Http/Exception/ServerErrorResponseException.php + ./src/Guzzle/Http/Exception/ClientErrorResponseException.php + ./src/Guzzle/Http/Exception/TooManyRedirectsException.php + ./src/Guzzle/Http/Exception/CouldNotRewindStreamException.php + ./src/Guzzle/Common/Exception/BadMethodCallException.php + ./src/Guzzle/Common/Exception/InvalidArgumentException.php + ./src/Guzzle/Common/Exception/RuntimeException.php + ./src/Guzzle/Common/Exception/UnexpectedValueException.php + ./src/Guzzle/Service/Exception/ClientNotFoundException.php + ./src/Guzzle/Service/Exception/CommandException.php + ./src/Guzzle/Service/Exception/DescriptionBuilderException.php + ./src/Guzzle/Service/Exception/ServiceBuilderException.php + ./src/Guzzle/Service/Exception/ServiceNotFoundException.php + ./src/Guzzle/Service/Exception/ValidationException.php + ./src/Guzzle/Service/Exception/JsonException.php + + + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php new file mode 100755 index 000000000..0625d71c3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php @@ -0,0 +1,66 @@ +decoratedBatch = $decoratedBatch; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + * @codeCoverageIgnore + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->decoratedBatch, $method), $args); + } + + public function add($item) + { + $this->decoratedBatch->add($item); + + return $this; + } + + public function flush() + { + return $this->decoratedBatch->flush(); + } + + public function isEmpty() + { + return $this->decoratedBatch->isEmpty(); + } + + /** + * Trace the decorators associated with the batch + * + * @return array + */ + public function getDecorators() + { + $found = array($this); + if (method_exists($this->decoratedBatch, 'getDecorators')) { + $found = array_merge($found, $this->decoratedBatch->getDecorators()); + } + + return $found; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php new file mode 100755 index 000000000..4d41c54f8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php @@ -0,0 +1,92 @@ +transferStrategy = $transferStrategy; + $this->divisionStrategy = $divisionStrategy; + $this->queue = new \SplQueue(); + $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE); + $this->dividedBatches = array(); + } + + public function add($item) + { + $this->queue->enqueue($item); + + return $this; + } + + public function flush() + { + $this->createBatches(); + + $items = array(); + foreach ($this->dividedBatches as $batchIndex => $dividedBatch) { + while ($dividedBatch->valid()) { + $batch = $dividedBatch->current(); + $dividedBatch->next(); + try { + $this->transferStrategy->transfer($batch); + $items = array_merge($items, $batch); + } catch (\Exception $e) { + throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy); + } + } + // Keep the divided batch down to a minimum in case of a later exception + unset($this->dividedBatches[$batchIndex]); + } + + return $items; + } + + public function isEmpty() + { + return count($this->queue) == 0 && count($this->dividedBatches) == 0; + } + + /** + * Create batches for any queued items + */ + protected function createBatches() + { + if (count($this->queue)) { + if ($batches = $this->divisionStrategy->createBatches($this->queue)) { + // Convert arrays into iterators + if (is_array($batches)) { + $batches = new \ArrayIterator($batches); + } + $this->dividedBatches[] = $batches; + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php new file mode 100755 index 000000000..ea99b4dd0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php @@ -0,0 +1,199 @@ + 'Guzzle\Batch\BatchRequestTransfer', + 'command' => 'Guzzle\Batch\BatchCommandTransfer' + ); + + /** + * Create a new instance of the BatchBuilder + * + * @return BatchBuilder + */ + public static function factory() + { + return new self(); + } + + /** + * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}. + * + * @param $threshold Number of items to allow in the queue before a flush + * + * @return BatchBuilder + */ + public function autoFlushAt($threshold) + { + $this->autoFlush = $threshold; + + return $this; + } + + /** + * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}. + * + * @return BatchBuilder + */ + public function keepHistory() + { + $this->history = true; + + return $this; + } + + /** + * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer + * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator. + * + * @return BatchBuilder + */ + public function bufferExceptions() + { + $this->exceptionBuffering = true; + + return $this; + } + + /** + * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator. + * + * @param mixed $callable Callable function to notify + * + * @return BatchBuilder + * @throws InvalidArgumentException if the argument is not callable + */ + public function notify($callable) + { + $this->afterFlush = $callable; + + return $this; + } + + /** + * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer} + * object as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of requests + * + * @return BatchBuilder + */ + public function transferRequests($batchSize = 50) + { + $className = self::$mapping['request']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Configures the batch to transfer batches commands. Associates as + * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of commands + * + * @return BatchBuilder + */ + public function transferCommands($batchSize = 50) + { + $className = self::$mapping['command']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Specify the strategy used to divide the queue into an array of batches + * + * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches + * + * @return BatchBuilder + */ + public function createBatchesWith(BatchDivisorInterface $divisorStrategy) + { + $this->divisorStrategy = $divisorStrategy; + + return $this; + } + + /** + * Specify the strategy used to transport the items when flush is called + * + * @param BatchTransferInterface $transferStrategy How items are transferred + * + * @return BatchBuilder + */ + public function transferWith(BatchTransferInterface $transferStrategy) + { + $this->transferStrategy = $transferStrategy; + + return $this; + } + + /** + * Create and return the instantiated batch + * + * @return BatchInterface + * @throws RuntimeException if no transfer strategy has been specified + */ + public function build() + { + if (!$this->transferStrategy) { + throw new RuntimeException('No transfer strategy has been specified'); + } + + if (!$this->divisorStrategy) { + throw new RuntimeException('No divisor strategy has been specified'); + } + + $batch = new Batch($this->transferStrategy, $this->divisorStrategy); + + if ($this->exceptionBuffering) { + $batch = new ExceptionBufferingBatch($batch); + } + + if ($this->afterFlush) { + $batch = new NotifyingBatch($batch, $this->afterFlush); + } + + if ($this->autoFlush) { + $batch = new FlushingBatch($batch, $this->autoFlush); + } + + if ($this->history) { + $batch = new HistoryBatch($batch); + } + + return $batch; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php new file mode 100755 index 000000000..e0a2d9568 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php @@ -0,0 +1,39 @@ +callable = $callable; + $this->context = $context; + } + + public function createBatches(\SplQueue $queue) + { + return call_user_func($this->callable, $queue, $this->context); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php new file mode 100755 index 000000000..9cbf1aba4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php @@ -0,0 +1,40 @@ +callable = $callable; + $this->context = $context; + } + + public function transfer(array $batch) + { + return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php new file mode 100755 index 000000000..d55ac7d1f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php @@ -0,0 +1,75 @@ +batchSize = $batchSize; + } + + /** + * Creates batches by grouping commands by their associated client + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof CommandInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, new \ArrayObject(array($item))); + } else { + $groups[$client]->append($item); + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if (empty($batch)) { + return; + } + + // Get the client of the first found command + $client = reset($batch)->getClient(); + + // Keep a list of all commands with invalid clients + $invalid = array_filter($batch, function ($command) use ($client) { + return $command->getClient() !== $client; + }); + + if (!empty($invalid)) { + throw new InconsistentClientTransferException($invalid); + } + + $client->execute($batch); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php new file mode 100755 index 000000000..0214f05f4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php @@ -0,0 +1,18 @@ +batchSize = $batchSize; + } + + /** + * Creates batches of requests by grouping requests by their associated curl multi object. + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + // Create batches by client objects + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof RequestInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, array($item)); + } else { + $current = $groups[$client]; + $current[] = $item; + $groups[$client] = $current; + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if ($batch) { + reset($batch)->getClient()->send($batch); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php new file mode 100755 index 000000000..67f90a581 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php @@ -0,0 +1,47 @@ +size = $size; + } + + /** + * Set the size of each batch + * + * @param int $size Size of each batch + * + * @return BatchSizeDivisor + */ + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + /** + * Get the size of each batch + * + * @return int + */ + public function getSize() + { + return $this->size; + } + + public function createBatches(\SplQueue $queue) + { + return array_chunk(iterator_to_array($queue, false), $this->size); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php new file mode 100755 index 000000000..2e0b60dad --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php @@ -0,0 +1,16 @@ +batch = $batch; + $this->transferredItems = $transferredItems; + $this->transferStrategy = $transferStrategy; + $this->divisorStrategy = $divisorStrategy; + parent::__construct( + 'Exception encountered while transferring batch: ' . $exception->getMessage(), + $exception->getCode(), + $exception + ); + } + + /** + * Get the batch that we being sent when the exception occurred + * + * @return array + */ + public function getBatch() + { + return $this->batch; + } + + /** + * Get the items transferred at the point in which the exception was encountered + * + * @return array + */ + public function getTransferredItems() + { + return $this->transferredItems; + } + + /** + * Get the transfer strategy + * + * @return TransferStrategy + */ + public function getTransferStrategy() + { + return $this->transferStrategy; + } + + /** + * Get the divisor strategy + * + * @return DivisorStrategy + */ + public function getDivisorStrategy() + { + return $this->divisorStrategy; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php new file mode 100755 index 000000000..d7a892885 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php @@ -0,0 +1,50 @@ +decoratedBatch->isEmpty()) { + try { + $transferredItems = $this->decoratedBatch->flush(); + } catch (BatchTransferException $e) { + $this->exceptions[] = $e; + $transferredItems = $e->getTransferredItems(); + } + $items = array_merge($items, $transferredItems); + } + + return $items; + } + + /** + * Get the buffered exceptions + * + * @return array Array of BatchTransferException objects + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Clear the buffered exceptions + */ + public function clearExceptions() + { + $this->exceptions = array(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php new file mode 100755 index 000000000..367b68427 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php @@ -0,0 +1,60 @@ +threshold = $threshold; + parent::__construct($decoratedBatch); + } + + /** + * Set the auto-flush threshold + * + * @param int $threshold The auto-flush threshold + * + * @return FlushingBatch + */ + public function setThreshold($threshold) + { + $this->threshold = $threshold; + + return $this; + } + + /** + * Get the auto-flush threshold + * + * @return int + */ + public function getThreshold() + { + return $this->threshold; + } + + public function add($item) + { + $this->decoratedBatch->add($item); + if (++$this->currentTotal >= $this->threshold) { + $this->currentTotal = 0; + $this->decoratedBatch->flush(); + } + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php new file mode 100755 index 000000000..e345fdc34 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php @@ -0,0 +1,39 @@ +history[] = $item; + $this->decoratedBatch->add($item); + + return $this; + } + + /** + * Get the batch history + * + * @return array + */ + public function getHistory() + { + return $this->history; + } + + /** + * Clear the batch history + */ + public function clearHistory() + { + $this->history = array(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php new file mode 100755 index 000000000..96d04daa8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php @@ -0,0 +1,38 @@ +callable = $callable; + parent::__construct($decoratedBatch); + } + + public function flush() + { + $items = $this->decoratedBatch->flush(); + call_user_func($this->callable, $items); + + return $items; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json new file mode 100755 index 000000000..12404d381 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json @@ -0,0 +1,31 @@ +{ + "name": "guzzle/batch", + "description": "Guzzle batch component for batching requests, commands, or custom transfers", + "homepage": "http://guzzlephp.org/", + "keywords": ["batch", "HTTP", "REST", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Batch": "" } + }, + "suggest": { + "guzzle/http": "self.version", + "guzzle/service": "self.version" + }, + "target-dir": "Guzzle/Batch", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php new file mode 100755 index 000000000..a5c527167 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php @@ -0,0 +1,21 @@ +cache; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php new file mode 100755 index 000000000..94e623463 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php @@ -0,0 +1,117 @@ +newInstanceArgs($args); + } + } catch (\Exception $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php new file mode 100755 index 000000000..970c9e228 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php @@ -0,0 +1,55 @@ +callables = $callables; + } + + public function contains($id, array $options = null) + { + return call_user_func($this->callables['contains'], $id, $options); + } + + public function delete($id, array $options = null) + { + return call_user_func($this->callables['delete'], $id, $options); + } + + public function fetch($id, array $options = null) + { + return call_user_func($this->callables['fetch'], $id, $options); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php new file mode 100755 index 000000000..e1aaf9f81 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->contains($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->delete($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->fetch($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($id, $data, $lifeTime !== false ? $lifeTime : 0); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php new file mode 100755 index 000000000..68bd4af97 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php @@ -0,0 +1,31 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->test($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->remove($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->load($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($data, $id, array(), $lifeTime); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php new file mode 100755 index 000000000..1fc18a555 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->hasItem($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->removeItem($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->getItem($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->setItem($id, $data); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json new file mode 100755 index 000000000..a5d999bd6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/cache", + "description": "Guzzle cache adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Cache": "" } + }, + "target-dir": "Guzzle/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php new file mode 100755 index 000000000..d1e842b1c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php @@ -0,0 +1,49 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php new file mode 100755 index 000000000..5cb1535d0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php @@ -0,0 +1,403 @@ +data = $data; + } + + /** + * Create a new collection from an array, validate the keys, and add default values where missing + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return self + * @throws InvalidArgumentException if a parameter is missing + */ + public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array()) + { + $data = $config + $defaults; + + if ($missing = array_diff($required, array_keys($data))) { + throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing)); + } + + return new self($data); + } + + public function count() + { + return count($this->data); + } + + public function getIterator() + { + return new \ArrayIterator($this->data); + } + + public function toArray() + { + return $this->data; + } + + /** + * Removes all key value pairs + * + * @return Collection + */ + public function clear() + { + $this->data = array(); + + return $this; + } + + /** + * Get all or a subset of matching key value pairs + * + * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs + * + * @return array Returns an array of all matching key value pairs + */ + public function getAll(array $keys = null) + { + return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data; + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * + * @return mixed|null Value of the key or NULL + */ + public function get($key) + { + return isset($this->data[$key]) ? $this->data[$key] : null; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + * + * @return Collection Returns a reference to the object + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Add a value to a key. If a key of the same name has already been added, the key value will be converted into an + * array and the new value will be pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + * + * @return Collection Returns a reference to the object. + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } elseif (is_array($this->data[$key])) { + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + + return $this; + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + * + * @return Collection + */ + public function remove($key) + { + unset($this->data[$key]); + + return $this; + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Case insensitive search the keys in the collection + * + * @param string $key Key to search for + * + * @return bool|string Returns false if not found, otherwise returns the key + */ + public function keySearch($key) + { + foreach (array_keys($this->data) as $k) { + if (!strcasecmp($k, $key)) { + return $k; + } + } + + return false; + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data); + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + * + * @return Collection Returns a reference to the object + */ + public function replace(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * @param Collection|array $data Associative array of key value pair data + * + * @return Collection Returns a reference to the object. + */ + public function merge($data) + { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + + return $this; + } + + /** + * Over write key value pairs in this collection with all of the data from an array or collection. + * + * @param array|\Traversable $data Values to override over this config + * + * @return self + */ + public function overwriteWith($data) + { + if (is_array($data)) { + $this->data = $data + $this->data; + } elseif ($data instanceof Collection) { + $this->data = $data->toArray() + $this->data; + } else { + foreach ($data as $key => $value) { + $this->data[$key] = $value; + } + } + + return $this; + } + + /** + * Returns a Collection containing all the elements of the collection after applying the callback function to each + * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a + * modified value + * + * @param \Closure $closure Closure to apply + * @param array $context Context to pass to the closure + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function map(\Closure $closure, array $context = array(), $static = true) + { + $collection = $static ? new static() : new self(); + foreach ($this as $key => $value) { + $collection->add($key, $closure($key, $value, $context)); + } + + return $collection; + } + + /** + * Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns + * true, the current value from input is returned into the result Collection. The Closure must accept three + * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value. + * + * @param \Closure $closure Closure evaluation function + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function filter(\Closure $closure, $static = true) + { + $collection = ($static) ? new static() : new self(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection->add($key, $value); + } + } + + return $collection; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + /** + * Set a value into a nested array key. Keys will be created as needed to set the value. + * + * @param string $path Path to set + * @param mixed $value Value to set at the key + * + * @return self + * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value + */ + public function setPath($path, $value) + { + $current =& $this->data; + $queue = explode('/', $path); + while (null !== ($key = array_shift($queue))) { + if (!is_array($current)) { + throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array"); + } elseif (!$queue) { + $current[$key] = $value; + } elseif (isset($current[$key])) { + $current =& $current[$key]; + } else { + $current[$key] = array(); + $current =& $current[$key]; + } + } + + return $this; + } + + /** + * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays) + * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This + * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path. + * + * @param string $path Path to traverse and retrieve a value from + * @param string $separator Character used to add depth to the search + * @param mixed $data Optional data to descend into (used when wildcards are encountered) + * + * @return mixed|null + */ + public function getPath($path, $separator = '/', $data = null) + { + if ($data === null) { + $data =& $this->data; + } + + $path = is_array($path) ? $path : explode($separator, $path); + while (null !== ($part = array_shift($path))) { + if (!is_array($data)) { + return null; + } elseif (isset($data[$part])) { + $data =& $data[$part]; + } elseif ($part != '*') { + return null; + } else { + // Perform a wildcard search by diverging and merging paths + $result = array(); + foreach ($data as $value) { + if (!$path) { + $result = array_merge_recursive($result, (array) $value); + } elseif (null !== ($test = $this->getPath($path, $separator, $value))) { + $result = array_merge_recursive($result, (array) $test); + } + } + return $result; + } + } + + return $data; + } + + /** + * Inject configuration settings into an input string + * + * @param string $input Input to inject + * + * @return string + * @deprecated + */ + public function inject($input) + { + Version::warn(__METHOD__ . ' is deprecated'); + $replace = array(); + foreach ($this->data as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + return strtr($input, $replace); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php new file mode 100755 index 000000000..fad76a9b8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php @@ -0,0 +1,52 @@ +context = $context; + } + + public function getIterator() + { + return new \ArrayIterator($this->context); + } + + public function offsetGet($offset) + { + return isset($this->context[$offset]) ? $this->context[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->context[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->context[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->context[$offset]); + } + + public function toArray() + { + return $this->context; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php new file mode 100755 index 000000000..08d1c7256 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php @@ -0,0 +1,5 @@ +shortMessage = $message; + } + + /** + * Set all of the exceptions + * + * @param array $exceptions Array of exceptions + * + * @return self + */ + public function setExceptions(array $exceptions) + { + $this->exceptions = array(); + foreach ($exceptions as $exception) { + $this->add($exception); + } + + return $this; + } + + /** + * Add exceptions to the collection + * + * @param ExceptionCollection|\Exception $e Exception to add + * + * @return ExceptionCollection; + */ + public function add($e) + { + $this->exceptions[] = $e; + if ($this->message) { + $this->message .= "\n"; + } + + $this->message .= $this->getExceptionMessage($e, 0); + + return $this; + } + + /** + * Get the total number of request exceptions + * + * @return int + */ + public function count() + { + return count($this->exceptions); + } + + /** + * Allows array-like iteration over the request exceptions + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->exceptions); + } + + /** + * Get the first exception in the collection + * + * @return \Exception + */ + public function getFirst() + { + return $this->exceptions ? $this->exceptions[0] : null; + } + + private function getExceptionMessage(\Exception $e, $depth = 0) + { + static $sp = ' '; + $prefix = $depth ? str_repeat($sp, $depth) : ''; + $message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n"; + + if ($e instanceof self) { + if ($e->shortMessage) { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n"; + } + foreach ($e as $ee) { + $message .= "\n" . $this->getExceptionMessage($ee, $depth + 1); + } + } else { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n"; + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n"; + } + + return str_replace(getcwd(), '.', $message); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php new file mode 100755 index 000000000..458e6f2ea --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php @@ -0,0 +1,8 @@ +=5.3.2", + "symfony/event-dispatcher": ">=2.1" + }, + "autoload": { + "psr-0": { "Guzzle\\Common": "" } + }, + "target-dir": "Guzzle/Common", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php new file mode 100755 index 000000000..5005a887c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php @@ -0,0 +1,221 @@ +body = $body; + } + + public function __toString() + { + return (string) $this->body; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->body, $method), $args); + } + + public function close() + { + return $this->body->close(); + } + + public function setRewindFunction($callable) + { + $this->body->setRewindFunction($callable); + + return $this; + } + + public function rewind() + { + return $this->body->rewind(); + } + + public function compress($filter = 'zlib.deflate') + { + return $this->body->compress($filter); + } + + public function uncompress($filter = 'zlib.inflate') + { + return $this->body->uncompress($filter); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->body->getContentType(); + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + $hash = Stream::getHash($this, 'md5', $rawOutput); + + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } + + public function getContentEncoding() + { + return $this->body->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->body->getMetaData($key); + } + + public function getStream() + { + return $this->body->getStream(); + } + + public function setStream($stream, $size = 0) + { + $this->body->setStream($stream, $size); + + return $this; + } + + public function detachStream() + { + $this->body->detachStream(); + + return $this; + } + + public function getWrapper() + { + return $this->body->getWrapper(); + } + + public function getWrapperData() + { + return $this->body->getWrapperData(); + } + + public function getStreamType() + { + return $this->body->getStreamType(); + } + + public function getUri() + { + return $this->body->getUri(); + } + + public function getSize() + { + return $this->body->getSize(); + } + + public function isReadable() + { + return $this->body->isReadable(); + } + + public function isRepeatable() + { + return $this->isSeekable() && $this->isReadable(); + } + + public function isWritable() + { + return $this->body->isWritable(); + } + + public function isConsumed() + { + return $this->body->isConsumed(); + } + + /** + * Alias of isConsumed() + * {@inheritdoc} + */ + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->body->isLocal(); + } + + public function isSeekable() + { + return $this->body->isSeekable(); + } + + public function setSize($size) + { + $this->body->setSize($size); + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->body->seek($offset, $whence); + } + + public function read($length) + { + return $this->body->read($length); + } + + public function write($string) + { + return $this->body->write($string); + } + + public function readLine($maxLength = null) + { + return $this->body->readLine($maxLength); + } + + public function ftell() + { + return $this->body->ftell(); + } + + public function getCustomData($key) + { + return $this->body->getCustomData($key); + } + + public function setCustomData($key, $value) + { + $this->body->setCustomData($key, $value); + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php new file mode 100755 index 000000000..c65c13650 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php @@ -0,0 +1,229 @@ +remoteStream = $body; + $this->body = new EntityBody(fopen('php://temp', 'r+')); + } + + /** + * Will give the contents of the buffer followed by the exhausted remote stream. + * + * Warning: Loads the entire stream into memory + * + * @return string + */ + public function __toString() + { + $pos = $this->ftell(); + $this->rewind(); + + $str = ''; + while (!$this->isConsumed()) { + $str .= $this->read(16384); + } + + $this->seek($pos); + + return $str; + } + + public function getSize() + { + return max($this->body->getSize(), $this->remoteStream->getSize()); + } + + /** + * {@inheritdoc} + * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->ftell(); + } else { + throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations'); + } + + // You cannot skip ahead past where you've read from the remote stream + if ($byte > $this->body->getSize()) { + throw new RuntimeException( + "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes" + ); + } + + return $this->body->seek($byte); + } + + public function rewind() + { + return $this->seek(0); + } + + /** + * Does not support custom rewind functions + * + * @throws RuntimeException + */ + public function setRewindFunction($callable) + { + throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions'); + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->body->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have been filled from the remote stream, + // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This + // mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->body->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want to skip bytes from being read from + // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length. + $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->body->write($string); + } + + /** + * {@inheritdoc} + * @link http://php.net/manual/en/function.fgets.php + */ + public function readLine($maxLength = null) + { + $buffer = ''; + $size = 0; + while (!$this->isConsumed()) { + $byte = $this->read(1); + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte == PHP_EOL || ++$size == $maxLength - 1) { + break; + } + } + + return $buffer; + } + + public function isConsumed() + { + return $this->body->isConsumed() && $this->remoteStream->isConsumed(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + return $this->remoteStream->close() && $this->body->close(); + } + + public function setStream($stream, $size = 0) + { + $this->remoteStream->setStream($stream, $size); + } + + public function getContentType() + { + return $this->remoteStream->getContentType(); + } + + public function getContentEncoding() + { + return $this->remoteStream->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->remoteStream->getMetaData($key); + } + + public function getStream() + { + return $this->remoteStream->getStream(); + } + + public function getWrapper() + { + return $this->remoteStream->getWrapper(); + } + + public function getWrapperData() + { + return $this->remoteStream->getWrapperData(); + } + + public function getStreamType() + { + return $this->remoteStream->getStreamType(); + } + + public function getUri() + { + return $this->remoteStream->getUri(); + } + + /** + * Always retrieve custom data from the remote stream + * {@inheritdoc} + */ + public function getCustomData($key) + { + return $this->remoteStream->getCustomData($key); + } + + /** + * Always set custom data on the remote stream + * {@inheritdoc} + */ + public function setCustomData($key, $value) + { + $this->remoteStream->setCustomData($key, $value); + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php new file mode 100755 index 000000000..3d7298dcd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php @@ -0,0 +1,524 @@ +setConfig($config ?: new Collection()); + $this->initSsl(); + $this->setBaseUrl($baseUrl); + $this->defaultHeaders = new Collection(); + $this->setRequestFactory(RequestFactory::getInstance()); + $this->userAgent = $this->getDefaultUserAgent(); + if (!$this->config[self::DISABLE_REDIRECTS]) { + $this->addSubscriber(new RedirectPlugin()); + } + } + + final public function setConfig($config) + { + if ($config instanceof Collection) { + $this->config = $config; + } elseif (is_array($config)) { + $this->config = new Collection($config); + } else { + throw new InvalidArgumentException('Config must be an array or Collection'); + } + + return $this; + } + + final public function getConfig($key = false) + { + return $key ? $this->config[$key] : $this->config; + } + + /** + * Set a default request option on the client that will be used as a default for each request + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * @param mixed $value Value to set + * + * @return $this + */ + public function setDefaultOption($keyOrPath, $value) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + $this->config->setPath($keyOrPath, $value); + + return $this; + } + + /** + * Retrieve a default request option from the client + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * + * @return mixed|null + */ + public function getDefaultOption($keyOrPath) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + + return $this->config->getPath($keyOrPath); + } + + final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2) + { + $opts = $this->config[self::CURL_OPTIONS] ?: array(); + + if ($certificateAuthority === true) { + // use bundled CA bundle, set secure defaults + $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem'; + $opts[CURLOPT_SSL_VERIFYPEER] = true; + $opts[CURLOPT_SSL_VERIFYHOST] = 2; + } elseif ($certificateAuthority === false) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_SSL_VERIFYPEER] = false; + $opts[CURLOPT_SSL_VERIFYHOST] = 0; + } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) { + throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean'); + } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) { + throw new InvalidArgumentException('verifyHost must be 0, 1 or 2'); + } else { + $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer; + $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost; + if (is_file($certificateAuthority)) { + unset($opts[CURLOPT_CAPATH]); + $opts[CURLOPT_CAINFO] = $certificateAuthority; + } elseif (is_dir($certificateAuthority)) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_CAPATH] = $certificateAuthority; + } else { + throw new RuntimeException( + 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority + ); + } + } + + $this->config->set(self::CURL_OPTIONS, $opts); + + return $this; + } + + public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) + { + if (!$uri) { + $url = $this->getBaseUrl(); + } else { + if (!is_array($uri)) { + $templateVars = null; + } else { + list($uri, $templateVars) = $uri; + } + if (strpos($uri, '://')) { + // Use absolute URLs as-is + $url = $this->expandTemplate($uri, $templateVars); + } else { + $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); + } + } + + // If default headers are provided, then merge them under any explicitly provided headers for the request + if (count($this->defaultHeaders)) { + if (!$headers) { + $headers = $this->defaultHeaders->toArray(); + } elseif (is_array($headers)) { + $headers += $this->defaultHeaders->toArray(); + } elseif ($headers instanceof Collection) { + $headers = $headers->toArray() + $this->defaultHeaders->toArray(); + } + } + + return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); + } + + public function getBaseUrl($expand = true) + { + return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; + } + + public function setBaseUrl($url) + { + $this->baseUrl = $url; + + return $this; + } + + public function setUserAgent($userAgent, $includeDefault = false) + { + if ($includeDefault) { + $userAgent .= ' ' . $this->getDefaultUserAgent(); + } + $this->userAgent = $userAgent; + + return $this; + } + + /** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ + public function getDefaultUserAgent() + { + return 'Guzzle/' . Version::VERSION + . ' curl/' . CurlVersion::getInstance()->get('version') + . ' PHP/' . PHP_VERSION; + } + + public function get($uri = null, $headers = null, $options = array()) + { + // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded + return is_array($options) + ? $this->createRequest('GET', $uri, $headers, null, $options) + : $this->createRequest('GET', $uri, $headers, $options); + } + + public function head($uri = null, $headers = null, array $options = array()) + { + return $this->createRequest('HEAD', $uri, $headers, null, $options); + } + + public function delete($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('DELETE', $uri, $headers, $body, $options); + } + + public function put($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PUT', $uri, $headers, $body, $options); + } + + public function patch($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PATCH', $uri, $headers, $body, $options); + } + + public function post($uri = null, $headers = null, $postBody = null, array $options = array()) + { + return $this->createRequest('POST', $uri, $headers, $postBody, $options); + } + + public function options($uri = null, array $options = array()) + { + return $this->createRequest('OPTIONS', $uri, $options); + } + + public function send($requests) + { + if (!($requests instanceof RequestInterface)) { + return $this->sendMultiple($requests); + } + + try { + /** @var $requests RequestInterface */ + $this->getCurlMulti()->add($requests)->send(); + return $requests->getResponse(); + } catch (ExceptionCollection $e) { + throw $e->getFirst(); + } + } + + /** + * Set a curl multi object to be used internally by the client for transferring requests. + * + * @param CurlMultiInterface $curlMulti Multi object + * + * @return self + */ + public function setCurlMulti(CurlMultiInterface $curlMulti) + { + $this->curlMulti = $curlMulti; + + return $this; + } + + /** + * @return CurlMultiInterface|CurlMultiProxy + */ + public function getCurlMulti() + { + if (!$this->curlMulti) { + $this->curlMulti = new CurlMultiProxy( + self::MAX_HANDLES, + $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT + ); + } + + return $this->curlMulti; + } + + public function setRequestFactory(RequestFactoryInterface $factory) + { + $this->requestFactory = $factory; + + return $this; + } + + /** + * Set the URI template expander to use with the client + * + * @param UriTemplateInterface $uriTemplate URI template expander + * + * @return self + */ + public function setUriTemplate(UriTemplateInterface $uriTemplate) + { + $this->uriTemplate = $uriTemplate; + + return $this; + } + + /** + * Expand a URI template while merging client config settings into the template variables + * + * @param string $template Template to expand + * @param array $variables Variables to inject + * + * @return string + */ + protected function expandTemplate($template, array $variables = null) + { + $expansionVars = $this->getConfig()->toArray(); + if ($variables) { + $expansionVars = $variables + $expansionVars; + } + + return $this->getUriTemplate()->expand($template, $expansionVars); + } + + /** + * Get the URI template expander used by the client + * + * @return UriTemplateInterface + */ + protected function getUriTemplate() + { + if (!$this->uriTemplate) { + $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template'); + } + + return $this->uriTemplate; + } + + /** + * Send multiple requests in parallel + * + * @param array $requests Array of RequestInterface objects + * + * @return array Returns an array of Response objects + */ + protected function sendMultiple(array $requests) + { + $curlMulti = $this->getCurlMulti(); + foreach ($requests as $request) { + $curlMulti->add($request); + } + $curlMulti->send(); + + /** @var $request RequestInterface */ + $result = array(); + foreach ($requests as $request) { + $result[] = $request->getResponse(); + } + + return $result; + } + + /** + * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. + * + * @param RequestInterface $request Request to prepare for the client + * @param array $options Options to apply to the request + * + * @return RequestInterface + */ + protected function prepareRequest(RequestInterface $request, array $options = array()) + { + $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); + + if ($curl = $this->config[self::CURL_OPTIONS]) { + $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); + } + + if ($params = $this->config[self::REQUEST_PARAMS]) { + Version::warn('request.params is deprecated. Use request.options to add default request options.'); + $request->getParams()->overwriteWith($params); + } + + if ($this->userAgent && !$request->hasHeader('User-Agent')) { + $request->setHeader('User-Agent', $this->userAgent); + } + + if ($defaults = $this->config[self::REQUEST_OPTIONS]) { + $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); + } + + if ($options) { + $this->requestFactory->applyOptions($request, $options); + } + + $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); + + return $request; + } + + /** + * Initializes SSL settings + */ + protected function initSsl() + { + $authority = $this->config[self::SSL_CERT_AUTHORITY]; + + if ($authority === 'system') { + return; + } + + if ($authority === null) { + $authority = true; + } + + if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') { + $authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem'); + } + + $this->setSslVerification($authority); + } + + /** + * @deprecated + */ + public function getDefaultHeaders() + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options'); + return $this->defaultHeaders; + } + + /** + * @deprecated + */ + public function setDefaultHeaders($headers) + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options'); + if ($headers instanceof Collection) { + $this->defaultHeaders = $headers; + } elseif (is_array($headers)) { + $this->defaultHeaders = new Collection($headers); + } else { + throw new InvalidArgumentException('Headers must be an array or Collection'); + } + + return $this; + } + + /** + * @deprecated + */ + public function preparePharCacert($md5Check = true) + { + return sys_get_temp_dir() . '/guzzle-cacert.pem'; + } + + /** + * Copies the phar cacert from a phar into the temp directory. + * + * @param string $pharCacertPath Path to the phar cacert. For example: + * 'phar://aws.phar/Guzzle/Http/Resources/cacert.pem' + * + * @return string Returns the path to the extracted cacert file. + * @throws \RuntimeException Throws if the phar cacert cannot be found or + * the file cannot be copied to the temp dir. + */ + public static function extractPharCacert($pharCacertPath) + { + // Copy the cacert.pem file from the phar if it is not in the temp + // folder. + $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem'; + + if (!file_exists($pharCacertPath)) { + throw new \RuntimeException("Could not find $pharCacertPath"); + } + + if (!file_exists($certFile) || + filesize($certFile) != filesize($pharCacertPath) + ) { + if (!copy($pharCacertPath, $certFile)) { + throw new \RuntimeException( + "Could not copy {$pharCacertPath} to {$certFile}: " + . var_export(error_get_last(), true) + ); + } + } + + return $certFile; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php new file mode 100755 index 000000000..10e4de2ab --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php @@ -0,0 +1,223 @@ +getCurlOptions(); + $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io')); + $tempContentLength = null; + $method = $request->getMethod(); + $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING); + + // Prepare url + $url = (string)$request->getUrl(); + if(($pos = strpos($url, '#')) !== false ){ + // strip fragment from url + $url = substr($url, 0, $pos); + } + + // Array of default cURL options. + $curlOptions = array( + CURLOPT_URL => $url, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_PORT => $request->getPort(), + CURLOPT_HTTPHEADER => array(), + CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), + CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), + CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' + ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, + // Verifies the authenticity of the peer's certificate + CURLOPT_SSL_VERIFYPEER => 1, + // Certificate must indicate that the server is the server to which you meant to connect + CURLOPT_SSL_VERIFYHOST => 2 + ); + + if (defined('CURLOPT_PROTOCOLS')) { + // Allow only HTTP and HTTPS protocols + $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + // Add CURLOPT_ENCODING if Accept-Encoding header is provided + if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { + $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; + // Let cURL set the Accept-Encoding header, prevents duplicate values + $request->removeHeader('Accept-Encoding'); + } + + // Enable curl debug information if the 'debug' param was set + if ($requestCurlOptions->get('debug')) { + $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); + // @codeCoverageIgnoreStart + if (false === $curlOptions[CURLOPT_STDERR]) { + throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); + } + // @codeCoverageIgnoreEnd + $curlOptions[CURLOPT_VERBOSE] = true; + } + + // Specify settings according to the HTTP method + if ($method == 'GET') { + $curlOptions[CURLOPT_HTTPGET] = true; + } elseif ($method == 'HEAD') { + $curlOptions[CURLOPT_NOBODY] = true; + // HEAD requests do not use a write function + unset($curlOptions[CURLOPT_WRITEFUNCTION]); + } elseif (!($request instanceof EntityEnclosingRequest)) { + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + } else { + + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + + // Handle sending raw bodies in a request + if ($request->getBody()) { + // You can send the body as a string using curl's CURLOPT_POSTFIELDS + if ($bodyAsString) { + $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + // Allow curl to add the Content-Length for us to account for the times when + // POST redirects are followed by GET requests + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + } + // Remove the curl generated Content-Type header if none was set manually + if (!$request->hasHeader('Content-Type')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } else { + $curlOptions[CURLOPT_UPLOAD] = true; + // Let cURL handle setting the Content-Length header + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; + } + // Add a callback for curl to read data to send with the request only if a body was specified + $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); + // Attempt to seek to the start of the stream + $request->getBody()->seek(0); + } + + } else { + + // Special handling for POST specific fields and files + $postFields = false; + if (count($request->getPostFiles())) { + $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode(); + foreach ($request->getPostFiles() as $key => $data) { + $prefixKeys = count($data) > 1; + foreach ($data as $index => $file) { + // Allow multiple files in the same key + $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; + $postFields[$fieldKey] = $file->getCurlValue(); + } + } + } elseif (count($request->getPostFields())) { + $postFields = (string) $request->getPostFields()->useUrlEncoding(true); + } + + if ($postFields !== false) { + if ($method == 'POST') { + unset($curlOptions[CURLOPT_CUSTOMREQUEST]); + $curlOptions[CURLOPT_POST] = true; + } + $curlOptions[CURLOPT_POSTFIELDS] = $postFields; + $request->removeHeader('Content-Length'); + } + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + } + + // If a Content-Length header was specified but we want to allow curl to set one for us + if (null !== $tempContentLength) { + $request->removeHeader('Content-Length'); + } + + // Set custom cURL options + foreach ($requestCurlOptions->toArray() as $key => $value) { + if (is_numeric($key)) { + $curlOptions[$key] = $value; + } + } + + // Do not set an Accept header by default + if (!isset($curlOptions[CURLOPT_ENCODING])) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + + // Add any custom headers to the request. Empty headers will cause curl to not send the header at all. + foreach ($request->getHeaderLines() as $line) { + $curlOptions[CURLOPT_HTTPHEADER][] = $line; + } + + // Add the content-length header back if it was temporarily removed + if (null !== $tempContentLength) { + $request->setHeader('Content-Length', $tempContentLength); + } + + // Apply the options to a new cURL handle. + $handle = curl_init(); + + // Enable the progress function if the 'progress' param was set + if ($requestCurlOptions->get('progress')) { + // Wrap the function in a function that provides the curl handle to the mediator's progress function + // Using this rather than injecting the handle into the mediator prevents a circular reference + $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) { + $args = func_get_args(); + $args[] = $handle; + + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + + call_user_func_array(array($mediator, 'progress'), $args); + }; + $curlOptions[CURLOPT_NOPROGRESS] = false; + } + + curl_setopt_array($handle, $curlOptions); + + return new static($handle, $curlOptions); + } + + /** + * Construct a new CurlHandle object that wraps a cURL handle + * + * @param resource $handle Configured cURL handle resource + * @param Collection|array $options Curl options to use with the handle + * + * @throws InvalidArgumentException + */ + public function __construct($handle, $options) + { + if (!is_resource($handle)) { + throw new InvalidArgumentException('Invalid handle provided'); + } + if (is_array($options)) { + $this->options = new Collection($options); + } elseif ($options instanceof Collection) { + $this->options = $options; + } else { + throw new InvalidArgumentException('Expected array or Collection'); + } + $this->handle = $handle; + } + + /** + * Destructor + */ + public function __destruct() + { + $this->close(); + } + + /** + * Close the curl handle + */ + public function close() + { + if (is_resource($this->handle)) { + curl_close($this->handle); + } + $this->handle = null; + } + + /** + * Check if the handle is available and still OK + * + * @return bool + */ + public function isAvailable() + { + return is_resource($this->handle); + } + + /** + * Get the last error that occurred on the cURL handle + * + * @return string + */ + public function getError() + { + return $this->isAvailable() ? curl_error($this->handle) : ''; + } + + /** + * Get the last error number that occurred on the cURL handle + * + * @return int + */ + public function getErrorNo() + { + if ($this->errorNo) { + return $this->errorNo; + } + + return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK; + } + + /** + * Set the curl error number + * + * @param int $error Error number to set + * + * @return CurlHandle + */ + public function setErrorNo($error) + { + $this->errorNo = $error; + + return $this; + } + + /** + * Get cURL curl_getinfo data + * + * @param int $option Option to retrieve. Pass null to retrieve all data as an array. + * + * @return array|mixed + */ + public function getInfo($option = null) + { + if (!is_resource($this->handle)) { + return null; + } + + if (null !== $option) { + return curl_getinfo($this->handle, $option) ?: null; + } + + return curl_getinfo($this->handle) ?: array(); + } + + /** + * Get the stderr output + * + * @param bool $asResource Set to TRUE to get an fopen resource + * + * @return string|resource|null + */ + public function getStderr($asResource = false) + { + $stderr = $this->getOptions()->get(CURLOPT_STDERR); + if (!$stderr) { + return null; + } + + if ($asResource) { + return $stderr; + } + + fseek($stderr, 0); + $e = stream_get_contents($stderr); + fseek($stderr, 0, SEEK_END); + + return $e; + } + + /** + * Get the URL that this handle is connecting to + * + * @return Url + */ + public function getUrl() + { + return Url::factory($this->options->get(CURLOPT_URL)); + } + + /** + * Get the wrapped curl handle + * + * @return resource|null Returns the cURL handle or null if it was closed + */ + public function getHandle() + { + return $this->isAvailable() ? $this->handle : null; + } + + /** + * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl + * handle after it is created. + * + * @return Collection + */ + public function getOptions() + { + return $this->options; + } + + /** + * Update a request based on the log messages of the CurlHandle + * + * @param RequestInterface $request Request to update + */ + public function updateRequestFromTransfer(RequestInterface $request) + { + if (!$request->getResponse()) { + return; + } + + // Update the transfer stats of the response + $request->getResponse()->setInfo($this->getInfo()); + + if (!$log = $this->getStderr(true)) { + return; + } + + // Parse the cURL stderr output for outgoing requests + $headers = ''; + fseek($log, 0); + while (($line = fgets($log)) !== false) { + if ($line && $line[0] == '>') { + $headers = substr(trim($line), 2) . "\r\n"; + while (($line = fgets($log)) !== false) { + if ($line[0] == '*' || $line[0] == '<') { + break; + } else { + $headers .= trim($line) . "\r\n"; + } + } + } + } + + // Add request headers to the request exactly as they were sent + if ($headers) { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers); + if (!empty($parsed['headers'])) { + $request->setHeaders(array()); + foreach ($parsed['headers'] as $name => $value) { + $request->setHeader($name, $value); + } + } + if (!empty($parsed['version'])) { + $request->setProtocolVersion($parsed['version']); + } + } + } + + /** + * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere + * + * @param array|Collection $config The configuration we want to parse + * + * @return array + */ + public static function parseCurlConfig($config) + { + $curlOptions = array(); + foreach ($config as $key => $value) { + if (is_string($key) && defined($key)) { + // Convert constants represented as string to constant int values + $key = constant($key); + } + if (is_string($value) && defined($value)) { + $value = constant($value); + } + $curlOptions[$key] = $value; + } + + return $curlOptions; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php new file mode 100755 index 000000000..9e4e63722 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php @@ -0,0 +1,423 @@ + array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), + CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), + CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), + CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') + ); + + /** @var float */ + protected $selectTimeout; + + public function __construct($selectTimeout = 1.0) + { + $this->selectTimeout = $selectTimeout; + $this->multiHandle = curl_multi_init(); + // @codeCoverageIgnoreStart + if ($this->multiHandle === false) { + throw new CurlException('Unable to create multi handle'); + } + // @codeCoverageIgnoreEnd + $this->reset(); + } + + public function __destruct() + { + if (is_resource($this->multiHandle)) { + curl_multi_close($this->multiHandle); + } + } + + public function add(RequestInterface $request) + { + $this->requests[] = $request; + // If requests are currently transferring and this is async, then the + // request must be prepared now as the send() method is not called. + $this->beforeSend($request); + $this->dispatch(self::ADD_REQUEST, array('request' => $request)); + + return $this; + } + + public function all() + { + return $this->requests; + } + + public function remove(RequestInterface $request) + { + $this->removeHandle($request); + if (($index = array_search($request, $this->requests, true)) !== false) { + $request = $this->requests[$index]; + unset($this->requests[$index]); + $this->requests = array_values($this->requests); + $this->dispatch(self::REMOVE_REQUEST, array('request' => $request)); + return true; + } + + return false; + } + + public function reset($hard = false) + { + // Remove each request + if ($this->requests) { + foreach ($this->requests as $request) { + $this->remove($request); + } + } + + $this->handles = new \SplObjectStorage(); + $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array(); + } + + public function send() + { + $this->perform(); + $exceptions = $this->exceptions; + $successful = $this->successful; + $this->reset(); + + if ($exceptions) { + $this->throwMultiException($exceptions, $successful); + } + } + + public function count() + { + return count($this->requests); + } + + /** + * Build and throw a MultiTransferException + * + * @param array $exceptions Exceptions encountered + * @param array $successful Successful requests + * @throws MultiTransferException + */ + protected function throwMultiException(array $exceptions, array $successful) + { + $multiException = new MultiTransferException('Errors during multi transfer'); + + while ($e = array_shift($exceptions)) { + $multiException->addFailedRequestWithException($e['request'], $e['exception']); + } + + // Add successful requests + foreach ($successful as $request) { + if (!$multiException->containsRequest($request)) { + $multiException->addSuccessfulRequest($request); + } + } + + throw $multiException; + } + + /** + * Prepare for sending + * + * @param RequestInterface $request Request to prepare + * @throws \Exception on error preparing the request + */ + protected function beforeSend(RequestInterface $request) + { + try { + $state = $request->setState(RequestInterface::STATE_TRANSFER); + if ($state == RequestInterface::STATE_TRANSFER) { + $this->addHandle($request); + } else { + // Requests might decide they don't need to be sent just before + // transfer (e.g. CachePlugin) + $this->remove($request); + if ($state == RequestInterface::STATE_COMPLETE) { + $this->successful[] = $request; + } + } + } catch (\Exception $e) { + // Queue the exception to be thrown when sent + $this->removeErroredRequest($request, $e); + } + } + + private function addHandle(RequestInterface $request) + { + $handle = $this->createCurlHandle($request)->getHandle(); + $this->checkCurlResult( + curl_multi_add_handle($this->multiHandle, $handle) + ); + } + + /** + * Create a curl handle for a request + * + * @param RequestInterface $request Request + * + * @return CurlHandle + */ + protected function createCurlHandle(RequestInterface $request) + { + $wrapper = CurlHandle::factory($request); + $this->handles[$request] = $wrapper; + $this->resourceHash[(int) $wrapper->getHandle()] = $request; + + return $wrapper; + } + + /** + * Get the data from the multi handle + */ + protected function perform() + { + $event = new Event(array('curl_multi' => $this)); + + while ($this->requests) { + // Notify each request as polling + $blocking = $total = 0; + foreach ($this->requests as $request) { + ++$total; + $event['request'] = $request; + $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event); + // The blocking variable just has to be non-falsey to block the loop + if ($request->getParams()->hasKey(self::BLOCKING)) { + ++$blocking; + } + } + if ($blocking == $total) { + // Sleep to prevent eating CPU because no requests are actually pending a select call + usleep(500); + } else { + $this->executeHandles(); + } + } + } + + /** + * Execute and select curl handles + */ + private function executeHandles() + { + // The first curl_multi_select often times out no matter what, but is usually required for fast transfers + $selectTimeout = 0.001; + $active = false; + do { + while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM); + $this->checkCurlResult($mrc); + $this->processMessages(); + if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) { + // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141 + usleep(150); + } + $selectTimeout = $this->selectTimeout; + } while ($active); + } + + /** + * Process any received curl multi messages + */ + private function processMessages() + { + while ($done = curl_multi_info_read($this->multiHandle)) { + $request = $this->resourceHash[(int) $done['handle']]; + try { + $this->processResponse($request, $this->handles[$request], $done); + $this->successful[] = $request; + } catch (\Exception $e) { + $this->removeErroredRequest($request, $e); + } + } + } + + /** + * Remove a request that encountered an exception + * + * @param RequestInterface $request Request to remove + * @param \Exception $e Exception encountered + */ + protected function removeErroredRequest(RequestInterface $request, \Exception $e = null) + { + $this->exceptions[] = array('request' => $request, 'exception' => $e); + $this->remove($request); + $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions)); + } + + /** + * Check for errors and fix headers of a request based on a curl response + * + * @param RequestInterface $request Request to process + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @throws CurlException on Curl error + */ + protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) + { + // Set the transfer stats on the response + $handle->updateRequestFromTransfer($request); + // Check if a cURL exception occurred, and if so, notify things + $curlException = $this->isCurlException($request, $handle, $curl); + + // Always remove completed curl handles. They can be added back again + // via events if needed (e.g. ExponentialBackoffPlugin) + $this->removeHandle($request); + + if (!$curlException) { + if ($this->validateResponseWasSet($request)) { + $state = $request->setState( + RequestInterface::STATE_COMPLETE, + array('handle' => $handle) + ); + // Only remove the request if it wasn't resent as a result of + // the state change + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + } + return; + } + + // Set the state of the request to an error + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException)); + // Allow things to ignore the error if possible + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + + // The error was not handled, so fail + if ($state == RequestInterface::STATE_ERROR) { + /** @var CurlException $curlException */ + throw $curlException; + } + } + + /** + * Remove a curl handle from the curl multi object + * + * @param RequestInterface $request Request that owns the handle + */ + protected function removeHandle(RequestInterface $request) + { + if (isset($this->handles[$request])) { + $handle = $this->handles[$request]; + curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); + unset($this->handles[$request]); + unset($this->resourceHash[(int) $handle->getHandle()]); + $handle->close(); + } + } + + /** + * Check if a cURL transfer resulted in what should be an exception + * + * @param RequestInterface $request Request to check + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @return CurlException|bool + */ + private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) + { + if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) { + return false; + } + + $handle->setErrorNo($curl['result']); + $e = new CurlException(sprintf('[curl] %s: %s [url] %s', + $handle->getErrorNo(), $handle->getError(), $handle->getUrl())); + $e->setCurlHandle($handle) + ->setRequest($request) + ->setCurlInfo($handle->getInfo()) + ->setError($handle->getError(), $handle->getErrorNo()); + + return $e; + } + + /** + * Throw an exception for a cURL multi response if needed + * + * @param int $code Curl response code + * @throws CurlException + */ + private function checkCurlResult($code) + { + if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { + throw new CurlException(isset($this->multiErrors[$code]) + ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" + : 'Unexpected cURL error: ' . $code + ); + } + } + + /** + * @link https://github.com/guzzle/guzzle/issues/710 + */ + private function validateResponseWasSet(RequestInterface $request) + { + if ($request->getResponse()) { + return true; + } + + $body = $request instanceof EntityEnclosingRequestInterface + ? $request->getBody() + : null; + + if (!$body) { + $rex = new RequestException( + 'No response was received for a request with no body. This' + . ' could mean that you are saturating your network.' + ); + $rex->setRequest($request); + $this->removeErroredRequest($request, $rex); + } elseif (!$body->isSeekable() || !$body->seek(0)) { + // Nothing we can do with this. Sorry! + $rex = new RequestException( + 'The connection was unexpectedly closed. The request would' + . ' have been retried, but attempting to rewind the' + . ' request body failed.' + ); + $rex->setRequest($request); + $this->removeErroredRequest($request, $rex); + } else { + $this->remove($request); + // Add the request back to the batch to retry automatically. + $this->requests[] = $request; + $this->addHandle($request); + } + + return false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php new file mode 100755 index 000000000..0ead75735 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php @@ -0,0 +1,58 @@ +maxHandles = $maxHandles; + $this->selectTimeout = $selectTimeout; + // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel. + // These two statements autoload classes before a system runs out of file descriptors so that you can get back + // valuable error messages if you run out. + class_exists('Guzzle\Http\Message\Response'); + class_exists('Guzzle\Http\Exception\CurlException'); + } + + public function add(RequestInterface $request) + { + $this->queued[] = $request; + + return $this; + } + + public function all() + { + $requests = $this->queued; + foreach ($this->handles as $handle) { + $requests = array_merge($requests, $handle->all()); + } + + return $requests; + } + + public function remove(RequestInterface $request) + { + foreach ($this->queued as $i => $r) { + if ($request === $r) { + unset($this->queued[$i]); + return true; + } + } + + foreach ($this->handles as $handle) { + if ($handle->remove($request)) { + return true; + } + } + + return false; + } + + public function reset($hard = false) + { + $this->queued = array(); + $this->groups = array(); + foreach ($this->handles as $handle) { + $handle->reset(); + } + if ($hard) { + $this->handles = array(); + } + + return $this; + } + + public function send() + { + if ($this->queued) { + $group = $this->getAvailableHandle(); + // Add this handle to a list of handles than is claimed + $this->groups[] = $group; + while ($request = array_shift($this->queued)) { + $group->add($request); + } + try { + $group->send(); + array_pop($this->groups); + $this->cleanupHandles(); + } catch (\Exception $e) { + // Remove the group and cleanup if an exception was encountered and no more requests in group + if (!$group->count()) { + array_pop($this->groups); + $this->cleanupHandles(); + } + throw $e; + } + } + } + + public function count() + { + return count($this->all()); + } + + /** + * Get an existing available CurlMulti handle or create a new one + * + * @return CurlMulti + */ + protected function getAvailableHandle() + { + // Grab a handle that is not claimed + foreach ($this->handles as $h) { + if (!in_array($h, $this->groups, true)) { + return $h; + } + } + + // All are claimed, so create one + $handle = new CurlMulti($this->selectTimeout); + $handle->setEventDispatcher($this->getEventDispatcher()); + $this->handles[] = $handle; + + return $handle; + } + + /** + * Trims down unused CurlMulti handles to limit the number of open connections + */ + protected function cleanupHandles() + { + if ($diff = max(0, count($this->handles) - $this->maxHandles)) { + for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) { + if (!count($this->handles[$i])) { + unset($this->handles[$i]); + $diff--; + } + } + $this->handles = array_values($this->handles); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php new file mode 100755 index 000000000..c3f99dd25 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php @@ -0,0 +1,66 @@ +version) { + $this->version = curl_version(); + } + + return $this->version; + } + + /** + * Get a specific type of curl information + * + * @param string $type Version information to retrieve. This value is one of: + * - version_number: cURL 24 bit version number + * - version: cURL version number, as a string + * - ssl_version_number: OpenSSL 24 bit version number + * - ssl_version: OpenSSL version number, as a string + * - libz_version: zlib version number, as a string + * - host: Information about the host where cURL was built + * - features: A bitmask of the CURL_VERSION_XXX constants + * - protocols: An array of protocols names supported by cURL + * + * @return string|float|bool if the $type is found, and false if not found + */ + public function get($type) + { + $version = $this->getAll(); + + return isset($version[$type]) ? $version[$type] : false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php new file mode 100755 index 000000000..5d1a0cd87 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php @@ -0,0 +1,147 @@ +request = $request; + $this->emitIo = $emitIo; + } + + /** + * Receive a response header from curl + * + * @param resource $curl Curl handle + * @param string $header Received header + * + * @return int + */ + public function receiveResponseHeader($curl, $header) + { + static $normalize = array("\r", "\n"); + $length = strlen($header); + $header = str_replace($normalize, '', $header); + + if (strpos($header, 'HTTP/') === 0) { + + $startLine = explode(' ', $header, 3); + $code = $startLine[1]; + $status = isset($startLine[2]) ? $startLine[2] : ''; + + // Only download the body of the response to the specified response + // body when a successful response is received. + if ($code >= 200 && $code < 300) { + $body = $this->request->getResponseBody(); + } else { + $body = EntityBody::factory(); + } + + $response = new Response($code, null, $body); + $response->setStatus($code, $status); + $this->request->startResponse($response); + + $this->request->dispatch('request.receive.status_line', array( + 'request' => $this, + 'line' => $header, + 'status_code' => $code, + 'reason_phrase' => $status + )); + + } elseif ($pos = strpos($header, ':')) { + $this->request->getResponse()->addHeader( + trim(substr($header, 0, $pos)), + trim(substr($header, $pos + 1)) + ); + } + + return $length; + } + + /** + * Received a progress notification + * + * @param int $downloadSize Total download size + * @param int $downloaded Amount of bytes downloaded + * @param int $uploadSize Total upload size + * @param int $uploaded Amount of bytes uploaded + * @param resource $handle CurlHandle object + */ + public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null) + { + $this->request->dispatch('curl.callback.progress', array( + 'request' => $this->request, + 'handle' => $handle, + 'download_size' => $downloadSize, + 'downloaded' => $downloaded, + 'upload_size' => $uploadSize, + 'uploaded' => $uploaded + )); + } + + /** + * Write data to the response body of a request + * + * @param resource $curl Curl handle + * @param string $write Data that was received + * + * @return int + */ + public function writeResponseBody($curl, $write) + { + if ($this->emitIo) { + $this->request->dispatch('curl.callback.write', array( + 'request' => $this->request, + 'write' => $write + )); + } + + if ($response = $this->request->getResponse()) { + return $response->getBody()->write($write); + } else { + // Unexpected data received before response headers - abort transfer + return 0; + } + } + + /** + * Read data from the request body and send it to curl + * + * @param resource $ch Curl handle + * @param resource $fd File descriptor + * @param int $length Amount of data to read + * + * @return string + */ + public function readRequestBody($ch, $fd, $length) + { + if (!($body = $this->request->getBody())) { + return ''; + } + + $read = (string) $body->read($length); + if ($this->emitIo) { + $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); + } + + return $read; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php new file mode 100755 index 000000000..b60d170f0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php @@ -0,0 +1,201 @@ +rewindFunction = $callable; + + return $this; + } + + public function rewind() + { + return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind(); + } + + /** + * Create a new EntityBody from a string + * + * @param string $string String of data + * + * @return EntityBody + */ + public static function fromString($string) + { + $stream = fopen('php://temp', 'r+'); + if ($string !== '') { + fwrite($stream, $string); + rewind($stream); + } + + return new static($stream); + } + + public function compress($filter = 'zlib.deflate') + { + $result = $this->handleCompression($filter); + $this->contentEncoding = $result ? $filter : false; + + return $result; + } + + public function uncompress($filter = 'zlib.inflate') + { + $offsetStart = 0; + + // When inflating gzipped data, the first 10 bytes must be stripped + // if a gzip header is present + if ($filter == 'zlib.inflate') { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") { + $offsetStart = 10; + } + } + + $this->contentEncoding = false; + + return $this->handleCompression($filter, $offsetStart); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null; + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + if ($hash = self::getHash($this, 'md5', $rawOutput)) { + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } else { + return false; + } + } + + /** + * Calculate the MD5 hash of an entity body + * + * @param EntityBodyInterface $body Entity body to calculate the hash for + * @param bool $rawOutput Whether or not to use raw output + * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) + * + * @return bool|string Returns an MD5 string on success or FALSE on failure + * @deprecated This will be deprecated soon + * @codeCoverageIgnore + */ + public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false) + { + Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()'); + return $body->getContentMd5($rawOutput, $base64Encode); + } + + public function setStreamFilterContentEncoding($streamFilterContentEncoding) + { + $this->contentEncoding = $streamFilterContentEncoding; + + return $this; + } + + public function getContentEncoding() + { + return strtr($this->contentEncoding, array( + 'zlib.deflate' => 'gzip', + 'bzip2.compress' => 'compress' + )) ?: false; + } + + protected function handleCompression($filter, $offsetStart = 0) + { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + + $handle = fopen('php://temp', 'r+'); + $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); + if (!$filter) { + return false; + } + + // Seek to the offset start if possible + $this->seek($offsetStart); + while ($data = fread($this->stream, 8096)) { + fwrite($handle, $data); + } + + fclose($this->stream); + $this->stream = $handle; + stream_filter_remove($filter); + $stat = fstat($this->stream); + $this->size = $stat['size']; + $this->rebuildCache(); + $this->seek(0); + + // Remove any existing rewind function as the underlying stream has been replaced + $this->rewindFunction = null; + + return true; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php new file mode 100755 index 000000000..e640f5785 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php @@ -0,0 +1,73 @@ +isClientError()) { + $label = 'Client error response'; + $class = __NAMESPACE__ . '\\ClientErrorResponseException'; + } elseif ($response->isServerError()) { + $label = 'Server error response'; + $class = __NAMESPACE__ . '\\ServerErrorResponseException'; + } else { + $label = 'Unsuccessful response'; + $class = __CLASS__; + } + + $message = $label . PHP_EOL . implode(PHP_EOL, array( + '[status code] ' . $response->getStatusCode(), + '[reason phrase] ' . $response->getReasonPhrase(), + '[url] ' . $request->getUrl(), + )); + + $e = new $class($message); + $e->setResponse($response); + $e->setRequest($request); + + return $e; + } + + /** + * Set the response that caused the exception + * + * @param Response $response Response to set + */ + public function setResponse(Response $response) + { + $this->response = $response; + } + + /** + * Get the response that caused the exception + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php new file mode 100755 index 000000000..04d7ddc05 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php @@ -0,0 +1,8 @@ +curlError = $error; + $this->curlErrorNo = $number; + + return $this; + } + + /** + * Set the associated curl handle + * + * @param CurlHandle $handle Curl handle + * + * @return self + */ + public function setCurlHandle(CurlHandle $handle) + { + $this->handle = $handle; + + return $this; + } + + /** + * Get the associated cURL handle + * + * @return CurlHandle|null + */ + public function getCurlHandle() + { + return $this->handle; + } + + /** + * Get the associated cURL error message + * + * @return string|null + */ + public function getError() + { + return $this->curlError; + } + + /** + * Get the associated cURL error number + * + * @return int|null + */ + public function getErrorNo() + { + return $this->curlErrorNo; + } + + /** + * Returns curl information about the transfer + * + * @return array + */ + public function getCurlInfo() + { + return $this->curlInfo; + } + + /** + * Set curl transfer information + * + * @param array $info Array of curl transfer information + * + * @return self + * @link http://php.net/manual/en/function.curl-getinfo.php + */ + public function setCurlInfo(array $info) + { + $this->curlInfo = $info; + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php new file mode 100755 index 000000000..ee87295d3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php @@ -0,0 +1,10 @@ +successfulRequests, $this->failedRequests); + } + + /** + * Add to the array of successful requests + * + * @param RequestInterface $request Successful request + * + * @return self + */ + public function addSuccessfulRequest(RequestInterface $request) + { + $this->successfulRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests + * + * @param RequestInterface $request Failed request + * + * @return self + */ + public function addFailedRequest(RequestInterface $request) + { + $this->failedRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests and associate with exceptions + * + * @param RequestInterface $request Failed request + * @param \Exception $exception Exception to add and associate with + * + * @return self + */ + public function addFailedRequestWithException(RequestInterface $request, \Exception $exception) + { + $this->add($exception) + ->addFailedRequest($request) + ->exceptionForRequest[spl_object_hash($request)] = $exception; + + return $this; + } + + /** + * Get the Exception that caused the given $request to fail + * + * @param RequestInterface $request Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedRequest(RequestInterface $request) + { + $oid = spl_object_hash($request); + + return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null; + } + + /** + * Set all of the successful requests + * + * @param array Array of requests + * + * @return self + */ + public function setSuccessfulRequests(array $requests) + { + $this->successfulRequests = $requests; + + return $this; + } + + /** + * Set all of the failed requests + * + * @param array Array of requests + * + * @return self + */ + public function setFailedRequests(array $requests) + { + $this->failedRequests = $requests; + + return $this; + } + + /** + * Get an array of successful requests sent in the multi transfer + * + * @return array + */ + public function getSuccessfulRequests() + { + return $this->successfulRequests; + } + + /** + * Get an array of failed requests sent in the multi transfer + * + * @return array + */ + public function getFailedRequests() + { + return $this->failedRequests; + } + + /** + * Check if the exception object contains a request + * + * @param RequestInterface $request Request to check + * + * @return bool + */ + public function containsRequest(RequestInterface $request) + { + return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php new file mode 100755 index 000000000..274df2cb1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php @@ -0,0 +1,39 @@ +request = $request; + + return $this; + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php new file mode 100755 index 000000000..f0f7cfe48 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php @@ -0,0 +1,8 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + /** + * {@inheritdoc} + * @codeCoverageIgnore + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + public function read($length) + { + $event = array( + 'body' => $this, + 'length' => $length, + 'read' => $this->body->read($length) + ); + $this->dispatch('body.read', $event); + + return $event['read']; + } + + public function write($string) + { + $event = array( + 'body' => $this, + 'write' => $string, + 'result' => $this->body->write($string) + ); + $this->dispatch('body.write', $event); + + return $event['result']; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php new file mode 100755 index 000000000..0d066ffce --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php @@ -0,0 +1,220 @@ +params = new Collection(); + $this->headerFactory = new HeaderFactory(); + $this->headers = new HeaderCollection(); + } + + /** + * Set the header factory to use to create headers + * + * @param HeaderFactoryInterface $factory + * + * @return self + */ + public function setHeaderFactory(HeaderFactoryInterface $factory) + { + $this->headerFactory = $factory; + + return $this; + } + + public function getParams() + { + return $this->params; + } + + public function addHeader($header, $value) + { + if (isset($this->headers[$header])) { + $this->headers[$header]->add($value); + } elseif ($value instanceof HeaderInterface) { + $this->headers[$header] = $value; + } else { + $this->headers[$header] = $this->headerFactory->createHeader($header, $value); + } + + return $this; + } + + public function addHeaders(array $headers) + { + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function getHeader($header) + { + return $this->headers[$header]; + } + + public function getHeaders() + { + return $this->headers; + } + + public function getHeaderLines() + { + $headers = array(); + foreach ($this->headers as $value) { + $headers[] = $value->getName() . ': ' . $value; + } + + return $headers; + } + + public function setHeader($header, $value) + { + unset($this->headers[$header]); + $this->addHeader($header, $value); + + return $this; + } + + public function setHeaders(array $headers) + { + $this->headers->clear(); + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function hasHeader($header) + { + return isset($this->headers[$header]); + } + + public function removeHeader($header) + { + unset($this->headers[$header]); + + return $this; + } + + /** + * @deprecated Use $message->getHeader()->parseParams() + * @codeCoverageIgnore + */ + public function getTokenizedHeader($header, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()'); + if ($this->hasHeader($header)) { + $data = new Collection(); + foreach ($this->getHeader($header)->parseParams() as $values) { + foreach ($values as $key => $value) { + if ($value === '') { + $data->set($data->count(), $key); + } else { + $data->add($key, $value); + } + } + } + return $data; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setTokenizedHeader($header, $data, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated.'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + return null; + } + + return $header->getDirective($directive); + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + return $header->hasDirective($directive); + } else { + return false; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function addCacheControlDirective($directive, $value = true) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + $this->addHeader('Cache-Control', ''); + $header = $this->getHeader('Cache-Control'); + } + + $header->addDirective($directive, $value); + + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function removeCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + $header->removeDirective($directive); + } + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php new file mode 100755 index 000000000..212850a25 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php @@ -0,0 +1,247 @@ +postFields = new QueryString(); + parent::__construct($method, $url, $headers); + } + + /** + * @return string + */ + public function __toString() + { + // Only attempt to include the POST data if it's only fields + if (count($this->postFields) && empty($this->postFiles)) { + return parent::__toString() . (string) $this->postFields; + } + + return parent::__toString() . $this->body; + } + + public function setState($state, array $context = array()) + { + parent::setState($state, $context); + if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) { + $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding'); + } + + return $this->state; + } + + public function setBody($body, $contentType = null) + { + $this->body = EntityBody::factory($body); + + // Auto detect the Content-Type from the path of the request if possible + if ($contentType === null && !$this->hasHeader('Content-Type')) { + $contentType = $this->body->getContentType(); + } + + if ($contentType) { + $this->setHeader('Content-Type', $contentType); + } + + // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects. + if (!$this->body->isSeekable() && $this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + + // Set the Content-Length header if it can be determined + $size = $this->body->getContentLength(); + if ($size !== null && $size !== false) { + $this->setHeader('Content-Length', $size); + if ($size > $this->expectCutoff) { + $this->setHeader('Expect', '100-Continue'); + } + } elseif (!$this->hasHeader('Content-Length')) { + if ('1.1' == $this->protocolVersion) { + $this->setHeader('Transfer-Encoding', 'chunked'); + } else { + throw new RequestException( + 'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0' + ); + } + } + + return $this; + } + + public function getBody() + { + return $this->body; + } + + /** + * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header. + * + * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data) + * + * @return self + */ + public function setExpectHeaderCutoff($size) + { + $this->expectCutoff = $size; + if ($size === false || !$this->body) { + $this->removeHeader('Expect'); + } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) { + $this->setHeader('Expect', '100-Continue'); + } + + return $this; + } + + public function configureRedirects($strict = false, $maxRedirects = 5) + { + $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict); + if ($maxRedirects == 0) { + $this->getParams()->set(RedirectPlugin::DISABLE, true); + } else { + $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects); + } + + return $this; + } + + public function getPostField($field) + { + return $this->postFields->get($field); + } + + public function getPostFields() + { + return $this->postFields; + } + + public function setPostField($key, $value) + { + $this->postFields->set($key, $value); + $this->processPostFields(); + + return $this; + } + + public function addPostFields($fields) + { + $this->postFields->merge($fields); + $this->processPostFields(); + + return $this; + } + + public function removePostField($field) + { + $this->postFields->remove($field); + $this->processPostFields(); + + return $this; + } + + public function getPostFiles() + { + return $this->postFiles; + } + + public function getPostFile($fieldName) + { + return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; + } + + public function removePostFile($fieldName) + { + unset($this->postFiles[$fieldName]); + $this->processPostFields(); + + return $this; + } + + public function addPostFile($field, $filename = null, $contentType = null, $postname = null) + { + $data = null; + + if ($field instanceof PostFileInterface) { + $data = $field; + } elseif (is_array($filename)) { + // Allow multiple values to be set in a single key + foreach ($filename as $file) { + $this->addPostFile($field, $file, $contentType); + } + return $this; + } elseif (!is_string($filename)) { + throw new RequestException('The path to a file must be a string'); + } elseif (!empty($filename)) { + // Adding an empty file will cause cURL to error out + $data = new PostFile($field, $filename, $contentType, $postname); + } + + if ($data) { + if (!isset($this->postFiles[$data->getFieldName()])) { + $this->postFiles[$data->getFieldName()] = array($data); + } else { + $this->postFiles[$data->getFieldName()][] = $data; + } + $this->processPostFields(); + } + + return $this; + } + + public function addPostFiles(array $files) + { + foreach ($files as $key => $file) { + if ($file instanceof PostFileInterface) { + $this->addPostFile($file, null, null, false); + } elseif (is_string($file)) { + // Convert non-associative array keys into 'file' + if (is_numeric($key)) { + $key = 'file'; + } + $this->addPostFile($key, $file, null, false); + } else { + throw new RequestException('File must be a string or instance of PostFileInterface'); + } + } + + return $this; + } + + /** + * Determine what type of request should be sent based on post fields + */ + protected function processPostFields() + { + if (!$this->postFiles) { + $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED); + } else { + $this->setHeader('Content-Type', self::MULTIPART); + if ($this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php new file mode 100755 index 000000000..49ad4595d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php @@ -0,0 +1,137 @@ + filenames where filename can be a string or PostFileInterface + * + * @return self + */ + public function addPostFiles(array $files); + + /** + * Configure how redirects are handled for the request + * + * @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most + * browsers with follow a 301-302 redirect for a POST request with a GET request. This is + * the default behavior of Guzzle. Enable strict redirects to redirect these responses + * with a POST rather than a GET request. + * @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects. + * + * @return self + */ + public function configureRedirects($strict = false, $maxRedirects = 5); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php new file mode 100755 index 000000000..50597b2a6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php @@ -0,0 +1,182 @@ +header = trim($header); + $this->glue = $glue; + + foreach ((array) $values as $value) { + foreach ((array) $value as $v) { + $this->values[] = $v; + } + } + } + + public function __toString() + { + return implode($this->glue . ' ', $this->toArray()); + } + + public function add($value) + { + $this->values[] = $value; + + return $this; + } + + public function getName() + { + return $this->header; + } + + public function setName($name) + { + $this->header = $name; + + return $this; + } + + public function setGlue($glue) + { + $this->glue = $glue; + + return $this; + } + + public function getGlue() + { + return $this->glue; + } + + /** + * Normalize the header to be a single header with an array of values. + * + * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into + * multiple entries in the header. + * + * @return self + */ + public function normalize() + { + $values = $this->toArray(); + + for ($i = 0, $total = count($values); $i < $total; $i++) { + if (strpos($values[$i], $this->glue) !== false) { + // Explode on glue when the glue is not inside of a comma + foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) { + $values[] = trim($v); + } + unset($values[$i]); + } + } + + $this->values = array_values($values); + + return $this; + } + + public function hasValue($searchValue) + { + return in_array($searchValue, $this->toArray()); + } + + public function removeValue($searchValue) + { + $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) { + return $value != $searchValue; + })); + + return $this; + } + + public function toArray() + { + return $this->values; + } + + public function count() + { + return count($this->toArray()); + } + + public function getIterator() + { + return new \ArrayIterator($this->toArray()); + } + + public function parseParams() + { + $params = $matches = array(); + $callback = array($this, 'trimHeader'); + + // Normalize the header into a single array and iterate over all values + foreach ($this->normalize()->toArray() as $val) { + $part = array(); + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + continue; + } + $pieces = array_map($callback, $matches[0]); + $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : ''; + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasExactHeader($header) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this->header == $header; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function raw() + { + Version::warn(__METHOD__ . ' is deprecated. Use toArray()'); + return $this->toArray(); + } + + /** + * Trim a header by removing excess spaces and wrapping quotes + * + * @param $str + * + * @return string + */ + protected function trimHeader($str) + { + static $trimmed = "\"' \n\t"; + + return trim($str, $trimmed); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php new file mode 100755 index 000000000..77789e51f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php @@ -0,0 +1,121 @@ +directives = null; + } + + public function removeValue($searchValue) + { + parent::removeValue($searchValue); + $this->directives = null; + } + + /** + * Check if a specific cache control directive exists + * + * @param string $param Directive to retrieve + * + * @return bool + */ + public function hasDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]); + } + + /** + * Get a specific cache control directive + * + * @param string $param Directive to retrieve + * + * @return string|bool|null + */ + public function getDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]) ? $directives[$param] : null; + } + + /** + * Add a cache control directive + * + * @param string $param Directive to add + * @param string $value Value to set + * + * @return self + */ + public function addDirective($param, $value) + { + $directives = $this->getDirectives(); + $directives[$param] = $value; + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Remove a cache control directive by name + * + * @param string $param Directive to remove + * + * @return self + */ + public function removeDirective($param) + { + $directives = $this->getDirectives(); + unset($directives[$param]); + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Get an associative array of cache control directives + * + * @return array + */ + public function getDirectives() + { + if ($this->directives === null) { + $this->directives = array(); + foreach ($this->parseParams() as $collection) { + foreach ($collection as $key => $value) { + $this->directives[$key] = $value === '' ? true : $value; + } + } + } + + return $this->directives; + } + + /** + * Updates the header value based on the parsed directives + * + * @param array $directives Array of cache control directives + */ + protected function updateFromDirectives(array $directives) + { + $this->directives = $directives; + $this->values = array(); + + foreach ($directives as $key => $value) { + $this->values[] = $value === true ? $key : "{$key}={$value}"; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php new file mode 100755 index 000000000..8c7f6aefb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php @@ -0,0 +1,108 @@ +headers = $headers; + } + + public function __clone() + { + foreach ($this->headers as &$header) { + $header = clone $header; + } + } + + /** + * Clears the header collection + */ + public function clear() + { + $this->headers = array(); + } + + /** + * Set a header on the collection + * + * @param HeaderInterface $header Header to add + * + * @return self + */ + public function add(HeaderInterface $header) + { + $this->headers[strtolower($header->getName())] = $header; + + return $this; + } + + /** + * Get an array of header objects + * + * @return array + */ + public function getAll() + { + return $this->headers; + } + + /** + * Alias of offsetGet + */ + public function get($key) + { + return $this->offsetGet($key); + } + + public function count() + { + return count($this->headers); + } + + public function offsetExists($offset) + { + return isset($this->headers[strtolower($offset)]); + } + + public function offsetGet($offset) + { + $l = strtolower($offset); + + return isset($this->headers[$l]) ? $this->headers[$l] : null; + } + + public function offsetSet($offset, $value) + { + $this->add($value); + } + + public function offsetUnset($offset) + { + unset($this->headers[strtolower($offset)]); + } + + public function getIterator() + { + return new \ArrayIterator($this->headers); + } + + public function toArray() + { + $result = array(); + foreach ($this->headers as $header) { + $result[$header->getName()] = $header->toArray(); + } + + return $result; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php new file mode 100755 index 000000000..0273be52f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php @@ -0,0 +1,26 @@ + 'Guzzle\Http\Message\Header\CacheControl', + 'link' => 'Guzzle\Http\Message\Header\Link', + ); + + public function createHeader($header, $value = null) + { + $lowercase = strtolower($header); + + return isset($this->mapping[$lowercase]) + ? new $this->mapping[$lowercase]($header, $value) + : new Header($header, $value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php new file mode 100755 index 000000000..9457cf64a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php @@ -0,0 +1,19 @@ +", "rel=\"{$rel}\""); + + foreach ($params as $k => $v) { + $values[] = "{$k}=\"{$v}\""; + } + + return $this->add(implode('; ', $values)); + } + + /** + * Check if a specific link exists for a given rel attribute + * + * @param string $rel rel value + * + * @return bool + */ + public function hasLink($rel) + { + return $this->getLink($rel) !== null; + } + + /** + * Get a specific link for a given rel attribute + * + * @param string $rel Rel value + * + * @return array|null + */ + public function getLink($rel) + { + foreach ($this->getLinks() as $link) { + if (isset($link['rel']) && $link['rel'] == $rel) { + return $link; + } + } + + return null; + } + + /** + * Get an associative array of links + * + * For example: + * Link: ; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg" + * + * + * var_export($response->getLinks()); + * array( + * array( + * 'url' => 'http:/.../front.jpeg', + * 'rel' => 'back', + * 'type' => 'image/jpeg', + * ) + * ) + * + * + * @return array + */ + public function getLinks() + { + $links = $this->parseParams(); + + foreach ($links as &$link) { + $key = key($link); + unset($link[$key]); + $link['url'] = trim($key, '<> '); + } + + return $links; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php new file mode 100755 index 000000000..62bcd4391 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php @@ -0,0 +1,102 @@ +fieldName = $fieldName; + $this->setFilename($filename); + $this->postname = $postname ? $postname : basename($filename); + $this->contentType = $contentType ?: $this->guessContentType(); + } + + public function setFieldName($name) + { + $this->fieldName = $name; + + return $this; + } + + public function getFieldName() + { + return $this->fieldName; + } + + public function setFilename($filename) + { + // Remove leading @ symbol + if (strpos($filename, '@') === 0) { + $filename = substr($filename, 1); + } + + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + + $this->filename = $filename; + + return $this; + } + + public function setPostname($postname) + { + $this->postname = $postname; + + return $this; + } + + public function getFilename() + { + return $this->filename; + } + + public function getPostname() + { + return $this->postname; + } + + public function setContentType($type) + { + $this->contentType = $type; + + return $this; + } + + public function getContentType() + { + return $this->contentType; + } + + public function getCurlValue() + { + // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax + // See: https://wiki.php.net/rfc/curl-file-upload + if (function_exists('curl_file_create')) { + return curl_file_create($this->filename, $this->contentType, $this->postname); + } + + // Use the old style if using an older version of PHP + $value = "@{$this->filename};filename=" . $this->postname; + if ($this->contentType) { + $value .= ';type=' . $this->contentType; + } + + return $value; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCurlString() + { + Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()'); + return $this->getCurlValue(); + } + + /** + * Determine the Content-Type of the file + */ + protected function guessContentType() + { + return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream'; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php new file mode 100755 index 000000000..7f0779d1e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php @@ -0,0 +1,83 @@ +method = strtoupper($method); + $this->curlOptions = new Collection(); + $this->setUrl($url); + + if ($headers) { + // Special handling for multi-value headers + foreach ($headers as $key => $value) { + // Deal with collisions with Host and Authorization + if ($key == 'host' || $key == 'Host') { + $this->setHeader($key, $value); + } elseif ($value instanceof HeaderInterface) { + $this->addHeader($key, $value); + } else { + foreach ((array) $value as $v) { + $this->addHeader($key, $v); + } + } + } + } + + $this->setState(self::STATE_NEW); + } + + public function __clone() + { + if ($this->eventDispatcher) { + $this->eventDispatcher = clone $this->eventDispatcher; + } + $this->curlOptions = clone $this->curlOptions; + $this->params = clone $this->params; + $this->url = clone $this->url; + $this->response = $this->responseBody = null; + $this->headers = clone $this->headers; + + $this->setState(RequestInterface::STATE_NEW); + $this->dispatch('request.clone', array('request' => $this)); + } + + /** + * Get the HTTP request as a string + * + * @return string + */ + public function __toString() + { + return $this->getRawHeaders() . "\r\n\r\n"; + } + + /** + * Default method that will throw exceptions if an unsuccessful response is received. + * + * @param Event $event Received + * @throws BadResponseException if the response is not successful + */ + public static function onRequestError(Event $event) + { + $e = BadResponseException::factory($event['request'], $event['response']); + $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray()); + throw $e; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getClient() + { + return $this->client; + } + + public function getRawHeaders() + { + $protocolVersion = $this->protocolVersion ?: '1.1'; + + return trim($this->method . ' ' . $this->getResource()) . ' ' + . strtoupper(str_replace('https', 'http', $this->url->getScheme())) + . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines()); + } + + public function setUrl($url) + { + if ($url instanceof Url) { + $this->url = $url; + } else { + $this->url = Url::factory($url); + } + + // Update the port and host header + $this->setPort($this->url->getPort()); + + if ($this->url->getUsername() || $this->url->getPassword()) { + $this->setAuth($this->url->getUsername(), $this->url->getPassword()); + // Remove the auth info from the URL + $this->url->setUsername(null); + $this->url->setPassword(null); + } + + return $this; + } + + public function send() + { + if (!$this->client) { + throw new RuntimeException('A client must be set on the request'); + } + + return $this->client->send($this); + } + + public function getResponse() + { + return $this->response; + } + + public function getQuery($asString = false) + { + return $asString + ? (string) $this->url->getQuery() + : $this->url->getQuery(); + } + + public function getMethod() + { + return $this->method; + } + + public function getScheme() + { + return $this->url->getScheme(); + } + + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + + return $this; + } + + public function getHost() + { + return $this->url->getHost(); + } + + public function setHost($host) + { + $this->url->setHost($host); + $this->setPort($this->url->getPort()); + + return $this; + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function setProtocolVersion($protocol) + { + $this->protocolVersion = $protocol; + + return $this; + } + + public function getPath() + { + return '/' . ltrim($this->url->getPath(), '/'); + } + + public function setPath($path) + { + $this->url->setPath($path); + + return $this; + } + + public function getPort() + { + return $this->url->getPort(); + } + + public function setPort($port) + { + $this->url->setPort($port); + + // Include the port in the Host header if it is not the default port for the scheme of the URL + $scheme = $this->url->getScheme(); + if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port); + } else { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost()); + } + + return $this; + } + + public function getUsername() + { + return $this->username; + } + + public function getPassword() + { + return $this->password; + } + + public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) + { + static $authMap = array( + 'basic' => CURLAUTH_BASIC, + 'digest' => CURLAUTH_DIGEST, + 'ntlm' => CURLAUTH_NTLM, + 'any' => CURLAUTH_ANY + ); + + // If we got false or null, disable authentication + if (!$user) { + $this->password = $this->username = null; + $this->removeHeader('Authorization'); + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + return $this; + } + + if (!is_numeric($scheme)) { + $scheme = strtolower($scheme); + if (!isset($authMap[$scheme])) { + throw new InvalidArgumentException($scheme . ' is not a valid authentication type'); + } + $scheme = $authMap[$scheme]; + } + + $this->username = $user; + $this->password = $password; + + // Bypass CURL when using basic auth to promote connection reuse + if ($scheme == CURLAUTH_BASIC) { + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password)); + } else { + $this->getCurlOptions() + ->set(CURLOPT_HTTPAUTH, $scheme) + ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + + return $this; + } + + public function getResource() + { + $resource = $this->getPath(); + if ($query = (string) $this->url->getQuery()) { + $resource .= '?' . $query; + } + + return $resource; + } + + public function getUrl($asObject = false) + { + return $asObject ? clone $this->url : (string) $this->url; + } + + public function getState() + { + return $this->state; + } + + public function setState($state, array $context = array()) + { + $oldState = $this->state; + $this->state = $state; + + switch ($state) { + case self::STATE_NEW: + $this->response = null; + break; + case self::STATE_TRANSFER: + if ($oldState !== $state) { + // Fix Content-Length and Transfer-Encoding collisions + if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) { + $this->removeHeader('Transfer-Encoding'); + } + $this->dispatch('request.before_send', array('request' => $this)); + } + break; + case self::STATE_COMPLETE: + if ($oldState !== $state) { + $this->processResponse($context); + $this->responseBody = null; + } + break; + case self::STATE_ERROR: + if (isset($context['exception'])) { + $this->dispatch('request.exception', array( + 'request' => $this, + 'response' => isset($context['response']) ? $context['response'] : $this->response, + 'exception' => isset($context['exception']) ? $context['exception'] : null + )); + } + } + + return $this->state; + } + + public function getCurlOptions() + { + return $this->curlOptions; + } + + public function startResponse(Response $response) + { + $this->state = self::STATE_TRANSFER; + $response->setEffectiveUrl((string) $this->getUrl()); + $this->response = $response; + + return $this; + } + + public function setResponse(Response $response, $queued = false) + { + $response->setEffectiveUrl((string) $this->url); + + if ($queued) { + $ed = $this->getEventDispatcher(); + $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) { + $e['request']->setResponse($response); + $ed->removeListener('request.before_send', $f); + }, -9999); + } else { + $this->response = $response; + // If a specific response body is specified, then use it instead of the response's body + if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) { + $this->getResponseBody()->write((string) $this->response->getBody()); + } else { + $this->responseBody = $this->response->getBody(); + } + $this->setState(self::STATE_COMPLETE); + } + + return $this; + } + + public function setResponseBody($body) + { + // Attempt to open a file for writing if a string was passed + if (is_string($body)) { + // @codeCoverageIgnoreStart + if (!($body = fopen($body, 'w+'))) { + throw new InvalidArgumentException('Could not open ' . $body . ' for writing'); + } + // @codeCoverageIgnoreEnd + } + + $this->responseBody = EntityBody::factory($body); + + return $this; + } + + public function getResponseBody() + { + if ($this->responseBody === null) { + $this->responseBody = EntityBody::factory()->setCustomData('default', true); + } + + return $this->responseBody; + } + + /** + * Determine if the response body is repeatable (readable + seekable) + * + * @return bool + * @deprecated Use getResponseBody()->isSeekable() + * @codeCoverageIgnore + */ + public function isResponseBodyRepeatable() + { + Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()'); + return !$this->responseBody ? true : $this->responseBody->isRepeatable(); + } + + public function getCookies() + { + if ($cookie = $this->getHeader('Cookie')) { + $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie); + return $data['cookies']; + } + + return array(); + } + + public function getCookie($name) + { + $cookies = $this->getCookies(); + + return isset($cookies[$name]) ? $cookies[$name] : null; + } + + public function addCookie($name, $value) + { + if (!$this->hasHeader('Cookie')) { + $this->setHeader('Cookie', "{$name}={$value}"); + } else { + $this->getHeader('Cookie')->add("{$name}={$value}"); + } + + // Always use semicolons to separate multiple cookie headers + $this->getHeader('Cookie')->setGlue(';'); + + return $this; + } + + public function removeCookie($name) + { + if ($cookie = $this->getHeader('Cookie')) { + foreach ($cookie as $cookieValue) { + if (strpos($cookieValue, $name . '=') === 0) { + $cookie->removeValue($cookieValue); + } + } + } + + return $this; + } + + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) + { + $this->eventDispatcher = $eventDispatcher; + $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255); + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->setEventDispatcher(new EventDispatcher()); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + $context['request'] = $this; + + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + /** + * Get an array containing the request and response for event notifications + * + * @return array + */ + protected function getEventArray() + { + return array( + 'request' => $this, + 'response' => $this->response + ); + } + + /** + * Process a received response + * + * @param array $context Contextual information + * @throws RequestException|BadResponseException on unsuccessful responses + */ + protected function processResponse(array $context = array()) + { + if (!$this->response) { + // If no response, then processResponse shouldn't have been called + $e = new RequestException('Error completing request'); + $e->setRequest($this); + throw $e; + } + + $this->state = self::STATE_COMPLETE; + + // A request was sent, but we don't know if we'll send more or if the final response will be successful + $this->dispatch('request.sent', $this->getEventArray() + $context); + + // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin) + if ($this->state == RequestInterface::STATE_COMPLETE) { + + // The request completed, so the HTTP transaction is complete + $this->dispatch('request.complete', $this->getEventArray()); + + // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by + // modifying the Event object in your listeners or calling setResponse() on the request + if ($this->response->isError()) { + $event = new Event($this->getEventArray()); + $this->getEventDispatcher()->dispatch('request.error', $event); + // Allow events of request.error to quietly change the response + if ($event['response'] !== $this->response) { + $this->response = $event['response']; + } + } + + // If a successful response was received, dispatch an event + if ($this->response->isSuccessful()) { + $this->dispatch('request.success', $this->getEventArray()); + } + } + } + + /** + * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy + * @codeCoverageIgnore + */ + public function canCache() + { + Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.'); + if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) { + $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy(); + return $canCache->canCacheRequest($this); + } else { + return false; + } + } + + /** + * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now) + * @codeCoverageIgnore + */ + public function setIsRedirect($isRedirect) + { + $this->isRedirect = $isRedirect; + + return $this; + } + + /** + * @deprecated Use the history plugin + * @codeCoverageIgnore + */ + public function isRedirect() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.'); + return $this->isRedirect; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php new file mode 100755 index 000000000..ba00a7676 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php @@ -0,0 +1,359 @@ +methods = array_flip(get_class_methods(__CLASS__)); + } + + public function fromMessage($message) + { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message); + + if (!$parsed) { + return false; + } + + $request = $this->fromParts($parsed['method'], $parsed['request_url'], + $parsed['headers'], $parsed['body'], $parsed['protocol'], + $parsed['version']); + + // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST + // requests. This factory method should accurately reflect the message, so here we are removing the Expect + // header if one was not supplied in the message. + if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) { + $request->removeHeader('Expect'); + } + + return $request; + } + + public function fromParts( + $method, + array $urlParts, + $headers = null, + $body = null, + $protocol = 'HTTP', + $protocolVersion = '1.1' + ) { + return $this->create($method, Url::buildUrl($urlParts), $headers, $body) + ->setProtocolVersion($protocolVersion); + } + + public function create($method, $url, $headers = null, $body = null, array $options = array()) + { + $method = strtoupper($method); + + if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE') { + // Handle non-entity-enclosing request methods + $request = new $this->requestClass($method, $url, $headers); + if ($body) { + // The body is where the response body will be stored + $type = gettype($body); + if ($type == 'string' || $type == 'resource' || $type == 'object') { + $request->setResponseBody($body); + } + } + } else { + // Create an entity enclosing request by default + $request = new $this->entityEnclosingRequestClass($method, $url, $headers); + if ($body || $body === '0') { + // Add POST fields and files to an entity enclosing request if an array is used + if (is_array($body) || $body instanceof Collection) { + // Normalize PHP style cURL uploads with a leading '@' symbol + foreach ($body as $key => $value) { + if (is_string($value) && substr($value, 0, 1) == '@') { + $request->addPostFile($key, $value); + unset($body[$key]); + } + } + // Add the fields if they are still present and not all files + $request->addPostFields($body); + } else { + // Add a raw entity body body to the request + $request->setBody($body, (string) $request->getHeader('Content-Type')); + if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') { + $request->removeHeader('Content-Length'); + } + } + } + } + + if ($options) { + $this->applyOptions($request, $options); + } + + return $request; + } + + /** + * Clone a request while changing the method. Emulates the behavior of + * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method. + * + * @param RequestInterface $request Request to clone + * @param string $method Method to set + * + * @return RequestInterface + */ + public function cloneRequestWithMethod(RequestInterface $request, $method) + { + // Create the request with the same client if possible + if ($request->getClient()) { + $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders()); + } else { + $cloned = $this->create($method, $request->getUrl(), $request->getHeaders()); + } + + $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray()); + $cloned->setEventDispatcher(clone $request->getEventDispatcher()); + // Ensure that that the Content-Length header is not copied if changing to GET or HEAD + if (!($cloned instanceof EntityEnclosingRequestInterface)) { + $cloned->removeHeader('Content-Length'); + } elseif ($request instanceof EntityEnclosingRequestInterface) { + $cloned->setBody($request->getBody()); + } + $cloned->getParams()->replace($request->getParams()->toArray()); + $cloned->dispatch('request.clone', array('request' => $cloned)); + + return $cloned; + } + + public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE) + { + // Iterate over each key value pair and attempt to apply a config using function visitors + foreach ($options as $key => $value) { + $method = "visit_{$key}"; + if (isset($this->methods[$method])) { + $this->{$method}($request, $value, $flags); + } + } + } + + protected function visit_headers(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('headers value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge headers in but do not overwrite existing values + foreach ($value as $key => $header) { + if (!$request->hasHeader($key)) { + $request->setHeader($key, $header); + } + } + } else { + $request->addHeaders($value); + } + } + + protected function visit_body(RequestInterface $request, $value, $flags) + { + if ($request instanceof EntityEnclosingRequestInterface) { + $request->setBody($value); + } else { + throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request'); + } + } + + protected function visit_allow_redirects(RequestInterface $request, $value, $flags) + { + if ($value === false) { + $request->getParams()->set(RedirectPlugin::DISABLE, true); + } + } + + protected function visit_auth(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('auth value must be an array'); + } + + $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic'); + } + + protected function visit_query(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('query value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge query string values in but do not overwrite existing values + $query = $request->getQuery(); + $query->overwriteWith(array_diff_key($value, $query->toArray())); + } else { + $request->getQuery()->overwriteWith($value); + } + } + + protected function visit_cookies(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('cookies value must be an array'); + } + + foreach ($value as $name => $v) { + $request->addCookie($name, $v); + } + } + + protected function visit_events(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('events value must be an array'); + } + + foreach ($value as $name => $method) { + if (is_array($method)) { + $request->getEventDispatcher()->addListener($name, $method[0], $method[1]); + } else { + $request->getEventDispatcher()->addListener($name, $method); + } + } + } + + protected function visit_plugins(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('plugins value must be an array'); + } + + foreach ($value as $plugin) { + $request->addSubscriber($plugin); + } + } + + protected function visit_exceptions(RequestInterface $request, $value, $flags) + { + if ($value === false || $value === 0) { + $dispatcher = $request->getEventDispatcher(); + foreach ($dispatcher->getListeners('request.error') as $listener) { + if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') { + $dispatcher->removeListener('request.error', $listener); + break; + } + } + } + } + + protected function visit_save_to(RequestInterface $request, $value, $flags) + { + $request->setResponseBody($value); + } + + protected function visit_params(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('params value must be an array'); + } + + $request->getParams()->overwriteWith($value); + } + + protected function visit_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_TIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value); + } + } + + protected function visit_connect_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value); + } + } + + protected function visit_debug(RequestInterface $request, $value, $flags) + { + if ($value) { + $request->getCurlOptions()->set(CURLOPT_VERBOSE, true); + } + } + + protected function visit_verify(RequestInterface $request, $value, $flags) + { + $curl = $request->getCurlOptions(); + if ($value === true || is_string($value)) { + $curl[CURLOPT_SSL_VERIFYHOST] = 2; + $curl[CURLOPT_SSL_VERIFYPEER] = true; + if ($value !== true) { + $curl[CURLOPT_CAINFO] = $value; + } + } elseif ($value === false) { + unset($curl[CURLOPT_CAINFO]); + $curl[CURLOPT_SSL_VERIFYHOST] = 0; + $curl[CURLOPT_SSL_VERIFYPEER] = false; + } + } + + protected function visit_proxy(RequestInterface $request, $value, $flags) + { + $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags); + } + + protected function visit_cert(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value); + } + } + + protected function visit_ssl_key(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php new file mode 100755 index 000000000..6088f10e9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php @@ -0,0 +1,105 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Reserved for WebDAV advanced collections expired proposal', + 426 => 'Upgrade required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ); + + /** @var EntityBodyInterface The response body */ + protected $body; + + /** @var string The reason phrase of the response (human readable code) */ + protected $reasonPhrase; + + /** @var string The status code of the response */ + protected $statusCode; + + /** @var array Information about the request */ + protected $info = array(); + + /** @var string The effective URL that returned this response */ + protected $effectiveUrl; + + /** @var array Cacheable response codes (see RFC 2616:13.4) */ + protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); + + /** + * Create a new Response based on a raw response message + * + * @param string $message Response message + * + * @return self|bool Returns false on error + */ + public static function fromMessage($message) + { + $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message); + if (!$data) { + return false; + } + + $response = new static($data['code'], $data['headers'], $data['body']); + $response->setProtocol($data['protocol'], $data['version']) + ->setStatus($data['code'], $data['reason_phrase']); + + // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X) + $contentLength = (string) $response->getHeader('Content-Length'); + $actualLength = strlen($data['body']); + if (strlen($data['body']) > 0 && $contentLength != $actualLength) { + $response->setHeader('Content-Length', $actualLength); + } + + return $response; + } + + /** + * Construct the response + * + * @param string $statusCode The response status code (e.g. 200, 404, etc) + * @param ToArrayInterface|array $headers The response headers + * @param string|resource|EntityBodyInterface $body The body of the response + * + * @throws BadResponseException if an invalid response code is given + */ + public function __construct($statusCode, $headers = null, $body = null) + { + parent::__construct(); + $this->setStatus($statusCode); + $this->body = EntityBody::factory($body !== null ? $body : ''); + + if ($headers) { + if (is_array($headers)) { + $this->setHeaders($headers); + } elseif ($headers instanceof ToArrayInterface) { + $this->setHeaders($headers->toArray()); + } else { + throw new BadResponseException('Invalid headers argument received'); + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } + + public function serialize() + { + return json_encode(array( + 'status' => $this->statusCode, + 'body' => (string) $this->body, + 'headers' => $this->headers->toArray() + )); + } + + public function unserialize($serialize) + { + $data = json_decode($serialize, true); + $this->__construct($data['status'], $data['headers'], $data['body']); + } + + /** + * Get the response entity body + * + * @param bool $asString Set to TRUE to return a string of the body rather than a full body object + * + * @return EntityBodyInterface|string + */ + public function getBody($asString = false) + { + return $asString ? (string) $this->body : $this->body; + } + + /** + * Set the response entity body + * + * @param EntityBodyInterface|string $body Body to set + * + * @return self + */ + public function setBody($body) + { + $this->body = EntityBody::factory($body); + + return $this; + } + + /** + * Set the protocol and protocol version of the response + * + * @param string $protocol Response protocol + * @param string $version Protocol version + * + * @return self + */ + public function setProtocol($protocol, $version) + { + $this->protocol = $protocol; + $this->protocolVersion = $version; + + return $this; + } + + /** + * Get the protocol used for the response (e.g. HTTP) + * + * @return string + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * Get the HTTP protocol version + * + * @return string + */ + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + /** + * Get a cURL transfer information + * + * @param string $key A single statistic to check + * + * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key + * is set and not found + * @link http://www.php.net/manual/en/function.curl-getinfo.php + */ + public function getInfo($key = null) + { + if ($key === null) { + return $this->info; + } elseif (array_key_exists($key, $this->info)) { + return $this->info[$key]; + } else { + return null; + } + } + + /** + * Set the transfer information + * + * @param array $info Array of cURL transfer stats + * + * @return self + */ + public function setInfo(array $info) + { + $this->info = $info; + + return $this; + } + + /** + * Set the response status + * + * @param int $statusCode Response status code to set + * @param string $reasonPhrase Response reason phrase + * + * @return self + * @throws BadResponseException when an invalid response code is received + */ + public function setStatus($statusCode, $reasonPhrase = '') + { + $this->statusCode = (int) $statusCode; + + if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } else { + $this->reasonPhrase = $reasonPhrase; + } + + return $this; + } + + /** + * Get the response status code + * + * @return integer + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Get the entire response as a string + * + * @return string + */ + public function getMessage() + { + $message = $this->getRawHeaders(); + + // Only include the body in the message if the size is < 2MB + $size = $this->body->getSize(); + if ($size < 2097152) { + $message .= (string) $this->body; + } + + return $message; + } + + /** + * Get the the raw message headers as a string + * + * @return string + */ + public function getRawHeaders() + { + $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n"; + $lines = $this->getHeaderLines(); + if (!empty($lines)) { + $headers .= implode("\r\n", $lines) . "\r\n"; + } + + return $headers . "\r\n"; + } + + /** + * Get the response reason phrase- a human readable version of the numeric + * status code + * + * @return string + */ + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + /** + * Get the Accept-Ranges HTTP header + * + * @return string Returns what partial content range types this server supports. + */ + public function getAcceptRanges() + { + return (string) $this->getHeader('Accept-Ranges'); + } + + /** + * Calculate the age of the response + * + * @return integer + */ + public function calculateAge() + { + $age = $this->getHeader('Age'); + + if ($age === null && $this->getDate()) { + $age = time() - strtotime($this->getDate()); + } + + return $age === null ? null : (int) (string) $age; + } + + /** + * Get the Age HTTP header + * + * @return integer|null Returns the age the object has been in a proxy cache in seconds. + */ + public function getAge() + { + return (string) $this->getHeader('Age'); + } + + /** + * Get the Allow HTTP header + * + * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed. + */ + public function getAllow() + { + return (string) $this->getHeader('Allow'); + } + + /** + * Check if an HTTP method is allowed by checking the Allow response header + * + * @param string $method Method to check + * + * @return bool + */ + public function isMethodAllowed($method) + { + $allow = $this->getHeader('Allow'); + if ($allow) { + foreach (explode(',', $allow) as $allowable) { + if (!strcasecmp(trim($allowable), $method)) { + return true; + } + } + } + + return false; + } + + /** + * Get the Cache-Control HTTP header + * + * @return string + */ + public function getCacheControl() + { + return (string) $this->getHeader('Cache-Control'); + } + + /** + * Get the Connection HTTP header + * + * @return string + */ + public function getConnection() + { + return (string) $this->getHeader('Connection'); + } + + /** + * Get the Content-Encoding HTTP header + * + * @return string|null + */ + public function getContentEncoding() + { + return (string) $this->getHeader('Content-Encoding'); + } + + /** + * Get the Content-Language HTTP header + * + * @return string|null Returns the language the content is in. + */ + public function getContentLanguage() + { + return (string) $this->getHeader('Content-Language'); + } + + /** + * Get the Content-Length HTTP header + * + * @return integer Returns the length of the response body in bytes + */ + public function getContentLength() + { + return (int) (string) $this->getHeader('Content-Length'); + } + + /** + * Get the Content-Location HTTP header + * + * @return string|null Returns an alternate location for the returned data (e.g /index.htm) + */ + public function getContentLocation() + { + return (string) $this->getHeader('Content-Location'); + } + + /** + * Get the Content-Disposition HTTP header + * + * @return string|null Returns the Content-Disposition header + */ + public function getContentDisposition() + { + return (string) $this->getHeader('Content-Disposition'); + } + + /** + * Get the Content-MD5 HTTP header + * + * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response. + */ + public function getContentMd5() + { + return (string) $this->getHeader('Content-MD5'); + } + + /** + * Get the Content-Range HTTP header + * + * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022). + */ + public function getContentRange() + { + return (string) $this->getHeader('Content-Range'); + } + + /** + * Get the Content-Type HTTP header + * + * @return string Returns the mime type of this content. + */ + public function getContentType() + { + return (string) $this->getHeader('Content-Type'); + } + + /** + * Checks if the Content-Type is of a certain type. This is useful if the + * Content-Type header contains charset information and you need to know if + * the Content-Type matches a particular type. + * + * @param string $type Content type to check against + * + * @return bool + */ + public function isContentType($type) + { + return stripos($this->getHeader('Content-Type'), $type) !== false; + } + + /** + * Get the Date HTTP header + * + * @return string|null Returns the date and time that the message was sent. + */ + public function getDate() + { + return (string) $this->getHeader('Date'); + } + + /** + * Get the ETag HTTP header + * + * @return string|null Returns an identifier for a specific version of a resource, often a Message digest. + */ + public function getEtag() + { + return (string) $this->getHeader('ETag'); + } + + /** + * Get the Expires HTTP header + * + * @return string|null Returns the date/time after which the response is considered stale. + */ + public function getExpires() + { + return (string) $this->getHeader('Expires'); + } + + /** + * Get the Last-Modified HTTP header + * + * @return string|null Returns the last modified date for the requested object, in RFC 2822 format + * (e.g. Tue, 15 Nov 1994 12:45:26 GMT) + */ + public function getLastModified() + { + return (string) $this->getHeader('Last-Modified'); + } + + /** + * Get the Location HTTP header + * + * @return string|null Used in redirection, or when a new resource has been created. + */ + public function getLocation() + { + return (string) $this->getHeader('Location'); + } + + /** + * Get the Pragma HTTP header + * + * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along + * the request-response chain. + */ + public function getPragma() + { + return (string) $this->getHeader('Pragma'); + } + + /** + * Get the Proxy-Authenticate HTTP header + * + * @return string|null Authentication to access the proxy (e.g. Basic) + */ + public function getProxyAuthenticate() + { + return (string) $this->getHeader('Proxy-Authenticate'); + } + + /** + * Get the Retry-After HTTP header + * + * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a + * specified period of time. + */ + public function getRetryAfter() + { + return (string) $this->getHeader('Retry-After'); + } + + /** + * Get the Server HTTP header + * + * @return string|null A name for the server + */ + public function getServer() + { + return (string) $this->getHeader('Server'); + } + + /** + * Get the Set-Cookie HTTP header + * + * @return string|null An HTTP cookie. + */ + public function getSetCookie() + { + return (string) $this->getHeader('Set-Cookie'); + } + + /** + * Get the Trailer HTTP header + * + * @return string|null The Trailer general field value indicates that the given set of header fields is present in + * the trailer of a message encoded with chunked transfer-coding. + */ + public function getTrailer() + { + return (string) $this->getHeader('Trailer'); + } + + /** + * Get the Transfer-Encoding HTTP header + * + * @return string|null The form of encoding used to safely transfer the entity to the user + */ + public function getTransferEncoding() + { + return (string) $this->getHeader('Transfer-Encoding'); + } + + /** + * Get the Vary HTTP header + * + * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached + * response can be used rather than requesting a fresh one from the origin server. + */ + public function getVary() + { + return (string) $this->getHeader('Vary'); + } + + /** + * Get the Via HTTP header + * + * @return string|null Informs the client of proxies through which the response was sent. + */ + public function getVia() + { + return (string) $this->getHeader('Via'); + } + + /** + * Get the Warning HTTP header + * + * @return string|null A general warning about possible problems with the entity body + */ + public function getWarning() + { + return (string) $this->getHeader('Warning'); + } + + /** + * Get the WWW-Authenticate HTTP header + * + * @return string|null Indicates the authentication scheme that should be used to access the requested entity + */ + public function getWwwAuthenticate() + { + return (string) $this->getHeader('WWW-Authenticate'); + } + + /** + * Checks if HTTP Status code is a Client Error (4xx) + * + * @return bool + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) + * + * @return boolean + */ + public function isError() + { + return $this->isClientError() || $this->isServerError(); + } + + /** + * Checks if HTTP Status code is Information (1xx) + * + * @return bool + */ + public function isInformational() + { + return $this->statusCode < 200; + } + + /** + * Checks if HTTP Status code is a Redirect (3xx) + * + * @return bool + */ + public function isRedirect() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Checks if HTTP Status code is Server Error (5xx) + * + * @return bool + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Checks if HTTP Status code is Successful (2xx | 304) + * + * @return bool + */ + public function isSuccessful() + { + return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304; + } + + /** + * Check if the response can be cached based on the response headers + * + * @return bool Returns TRUE if the response can be cached or false if not + */ + public function canCache() + { + // Check if the response is cacheable based on the code + if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) { + return false; + } + + // Make sure a valid body was returned and can be cached + if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable()) + && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) { + return false; + } + + // Never cache no-store resources (this is a private cache, so private + // can be cached) + if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return $this->isFresh() || $this->getFreshness() === null || $this->canValidate(); + } + + /** + * Gets the number of seconds from the current time in which this response is still considered fresh + * + * @return int|null Returns the number of seconds + */ + public function getMaxAge() + { + if ($header = $this->getHeader('Cache-Control')) { + // s-max-age, then max-age, then Expires + if ($age = $header->getDirective('s-maxage')) { + return $age; + } + if ($age = $header->getDirective('max-age')) { + return $age; + } + } + + if ($this->getHeader('Expires')) { + return strtotime($this->getExpires()) - time(); + } + + return null; + } + + /** + * Check if the response is considered fresh. + * + * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the + * response. + * + * @return bool|null + */ + public function isFresh() + { + $fresh = $this->getFreshness(); + + return $fresh === null ? null : $fresh >= 0; + } + + /** + * Check if the response can be validated against the origin server using a conditional GET request. + * + * @return bool + */ + public function canValidate() + { + return $this->getEtag() || $this->getLastModified(); + } + + /** + * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the + * age of the response (max-age - age). + * + * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired. + * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL + * result means that no freshness information is available. + * + * @return int + */ + public function getFreshness() + { + $maxAge = $this->getMaxAge(); + $age = $this->calculateAge(); + + return $maxAge && $age ? ($maxAge - $age) : null; + } + + /** + * Parse the JSON response body and return an array + * + * @return array|string|int|bool|float + * @throws RuntimeException if the response body is not in JSON format + */ + public function json() + { + $data = json_decode((string) $this->body, true); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); + } + + return $data === null ? array() : $data; + } + + /** + * Parse the XML response body and return a \SimpleXMLElement. + * + * In order to prevent XXE attacks, this method disables loading external + * entities. If you rely on external entities, then you must parse the + * XML response manually by accessing the response body directly. + * + * @return \SimpleXMLElement + * @throws RuntimeException if the response body is not in XML format + * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + */ + public function xml() + { + $errorMessage = null; + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + try { + $xml = new \SimpleXMLElement((string) $this->body ?: '', LIBXML_NONET); + if ($error = libxml_get_last_error()) { + $errorMessage = $error->message; + } + } catch (\Exception $e) { + $errorMessage = $e->getMessage(); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + if ($errorMessage) { + throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage); + } + + return $xml; + } + + /** + * Get the redirect count of this response + * + * @return int + */ + public function getRedirectCount() + { + return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT); + } + + /** + * Set the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @param string $url The effective URL + * + * @return self + */ + public function setEffectiveUrl($url) + { + $this->effectiveUrl = $url; + + return $this; + } + + /** + * Get the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @return string + */ + public function getEffectiveUrl() + { + return $this->effectiveUrl; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getPreviousResponse() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.'); + return null; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setRequest($request) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getRequest() + { + Version::warn(__METHOD__ . ' is deprecated'); + return null; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php new file mode 100755 index 000000000..d71586a05 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php @@ -0,0 +1,962 @@ + 'text/vnd.in3d.3dml', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'application' => 'application/x-ms-application', + 'apr' => 'application/vnd.lotus-approach', + 'asa' => 'text/plain', + 'asax' => 'application/octet-stream', + 'asc' => 'application/pgp-signature', + 'ascx' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'ashx' => 'text/plain', + 'asm' => 'text/x-asm', + 'asmx' => 'text/plain', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asp' => 'text/plain', + 'aspx' => 'text/plain', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'axd' => 'text/plain', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfc' => 'application/x-coldfusion', + 'cfm' => 'application/x-coldfusion', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'cryptonote' => 'application/vnd.rig.cryptonote', + 'cs' => 'text/plain', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxt' => 'application/vnd.geonext', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hta' => 'application/octet-stream', + 'htc' => 'text/html', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/octet-stream', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/octet-stream', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/octet-stream', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/mp4', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsf' => 'application/vnd.lotus-notes', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'text/x-php', + 'phps' => 'application/x-httpd-phps', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rb' => 'text/plain', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'resx' => 'text/xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sig' => 'application/pgp-signature', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'src' => 'application/x-wais-source', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'image/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvx' => 'application/vnd.dece.unspecified', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-ms-wmz', + 'woff' => 'application/x-font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'yaml' => 'text/yaml', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'yml' => 'text/yaml', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml' + ); + + /** + * Get a singleton instance of the class + * + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get a mimetype value from a file extension + * + * @param string $extension File extension + * + * @return string|null + * + */ + public function fromExtension($extension) + { + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null; + } + + /** + * Get a mimetype from a filename + * + * @param string $filename Filename to generate a mimetype from + * + * @return string|null + */ + public function fromFilename($filename) + { + return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php new file mode 100755 index 000000000..4b4e49d05 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php @@ -0,0 +1,20 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value))); + } else { + return array($key => implode(',', $value)); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php new file mode 100755 index 000000000..1bf1730e4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php @@ -0,0 +1,22 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value)); + } else { + return array($key => $value); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php new file mode 100755 index 000000000..133ea2bd9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php @@ -0,0 +1,27 @@ + $v) { + $k = "{$key}[{$k}]"; + if (is_array($v)) { + $ret = array_merge($ret, self::aggregate($k, $v, $query)); + } else { + $ret[$query->encodeValue($k)] = $query->encodeValue($v); + } + } + + return $ret; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php new file mode 100755 index 000000000..72bee620c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php @@ -0,0 +1,22 @@ +add($key, $value); + $foundDuplicates = true; + } elseif ($paramIsPhpStyleArray) { + $q[$key] = array($value); + } else { + $q[$key] = $value; + } + } else { + // Uses false by default to represent keys with no trailing "=" sign. + $q->add($key, false); + } + } + + // Use the duplicate aggregator if duplicates were found and not using PHP style arrays + if ($foundDuplicates && !$foundPhpStyle) { + $q->setAggregator(new DuplicateAggregator()); + } + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + * @throws RuntimeException + */ + public function __toString() + { + if (!$this->data) { + return ''; + } + + $queryList = array(); + foreach ($this->prepareData($this->data) as $name => $value) { + $queryList[] = $this->convertKvp($name, $value); + } + + return implode($this->fieldSeparator, $queryList); + } + + /** + * Get the query string field separator + * + * @return string + */ + public function getFieldSeparator() + { + return $this->fieldSeparator; + } + + /** + * Get the query string value separator + * + * @return string + */ + public function getValueSeparator() + { + return $this->valueSeparator; + } + + /** + * Returns the type of URL encoding used by the query string + * + * One of: false, "RFC 3986", or "application/x-www-form-urlencoded" + * + * @return bool|string + */ + public function getUrlEncoding() + { + return $this->urlEncode; + } + + /** + * Returns true or false if using URL encoding + * + * @return bool + */ + public function isUrlEncoding() + { + return $this->urlEncode !== false; + } + + /** + * Provide a function for combining multi-valued query string parameters into a single or multiple fields + * + * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting + * deeply nested query string variables into a flattened array. + * Pass null to use the default PHP style aggregator. For legacy + * reasons, this function accepts a callable that must accepts a + * $key, $value, and query object. + * @return self + * @see \Guzzle\Http\QueryString::aggregateUsingComma() + */ + public function setAggregator(QueryAggregatorInterface $aggregator = null) + { + // Use the default aggregator if none was set + if (!$aggregator) { + if (!self::$defaultAggregator) { + self::$defaultAggregator = new PhpAggregator(); + } + $aggregator = self::$defaultAggregator; + } + + $this->aggregator = $aggregator; + + return $this; + } + + /** + * Set whether or not field names and values should be rawurlencoded + * + * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or + * form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode) + * @return self + */ + public function useUrlEncoding($encode) + { + $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode; + + return $this; + } + + /** + * Set the query string separator + * + * @param string $separator The query string separator that will separate fields + * + * @return self + */ + public function setFieldSeparator($separator) + { + $this->fieldSeparator = $separator; + + return $this; + } + + /** + * Set the query string value separator + * + * @param string $separator The query string separator that will separate values from fields + * + * @return self + */ + public function setValueSeparator($separator) + { + $this->valueSeparator = $separator; + + return $this; + } + + /** + * Returns an array of url encoded field names and values + * + * @return array + */ + public function urlEncode() + { + return $this->prepareData($this->data); + } + + /** + * URL encodes a value based on the url encoding type of the query string object + * + * @param string $value Value to encode + * + * @return string + */ + public function encodeValue($value) + { + if ($this->urlEncode == self::RFC_3986) { + return rawurlencode($value); + } elseif ($this->urlEncode == self::FORM_URLENCODED) { + return urlencode($value); + } else { + return (string) $value; + } + } + + /** + * Url encode parameter data and convert nested query strings into a flattened hash. + * + * @param array $data The data to encode + * + * @return array Returns an array of encoded values and keys + */ + protected function prepareData(array $data) + { + // If no aggregator is present then set the default + if (!$this->aggregator) { + $this->setAggregator(null); + } + + $temp = array(); + foreach ($data as $key => $value) { + if ($value === false || $value === null) { + // False and null will not include the "=". Use an empty string to include the "=". + $temp[$this->encodeValue($key)] = $value; + } elseif (is_array($value)) { + $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this)); + } else { + $temp[$this->encodeValue($key)] = $this->encodeValue($value); + } + } + + return $temp; + } + + /** + * Converts a key value pair that can contain strings, nulls, false, or arrays + * into a single string. + * + * @param string $name Name of the field + * @param mixed $value Value of the field + * @return string + */ + private function convertKvp($name, $value) + { + if ($value === self::BLANK || $value === null || $value === false) { + return $name; + } elseif (!is_array($value)) { + return $name . $this->valueSeparator . $value; + } + + $result = ''; + foreach ($value as $v) { + $result .= $this->convertKvp($name, $v) . $this->fieldSeparator; + } + + return rtrim($result, $this->fieldSeparator); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php new file mode 100755 index 000000000..ef282733b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php @@ -0,0 +1,122 @@ +setLimit($limit)->setOffset($offset); + } + + /** + * Returns only a subset of the decorated entity body when cast as a string + * {@inheritdoc} + */ + public function __toString() + { + if (!$this->body->isReadable() || + (!$this->body->isSeekable() && $this->body->isConsumed()) + ) { + return ''; + } + + $originalPos = $this->body->ftell(); + $this->body->seek($this->offset); + $data = ''; + while (!$this->feof()) { + $data .= $this->read(1048576); + } + $this->body->seek($originalPos); + + return (string) $data ?: ''; + } + + public function isConsumed() + { + return $this->body->isConsumed() || + ($this->body->ftell() >= $this->offset + $this->limit); + } + + /** + * Returns the Content-Length of the limited subset of data + * {@inheritdoc} + */ + public function getContentLength() + { + $length = $this->body->getContentLength(); + + return $length === false + ? $this->limit + : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset); + } + + /** + * Allow for a bounded seek on the read limited entity body + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + return $whence === SEEK_SET + ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset))) + : false; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @return self + */ + public function setOffset($offset) + { + $this->body->seek($offset); + $this->offset = $offset; + + return $this; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the stream + * + * @param int $limit Total number of bytes to allow to be read from the stream + * + * @return self + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + public function read($length) + { + // Check if the current position is less than the total allowed bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->body->ftell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte limit is not exceeded + return $this->body->read(min($remaining, $length)); + } else { + return false; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php new file mode 100755 index 000000000..1a824b8b7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php @@ -0,0 +1,250 @@ + array('onRequestSent', 100), + 'request.clone' => 'cleanupRequest', + 'request.before_send' => 'cleanupRequest' + ); + } + + /** + * Clean up the parameters of a request when it is cloned + * + * @param Event $event Event emitted + */ + public function cleanupRequest(Event $event) + { + $params = $event['request']->getParams(); + unset($params[self::REDIRECT_COUNT]); + unset($params[self::PARENT_REQUEST]); + } + + /** + * Called when a request receives a redirect response + * + * @param Event $event Event emitted + */ + public function onRequestSent(Event $event) + { + $response = $event['response']; + $request = $event['request']; + + // Only act on redirect requests with Location headers + if (!$response || $request->getParams()->get(self::DISABLE)) { + return; + } + + // Trace the original request based on parameter history + $original = $this->getOriginalRequest($request); + + // Terminating condition to set the effective response on the original request + if (!$response->isRedirect() || !$response->hasHeader('Location')) { + if ($request !== $original) { + // This is a terminating redirect response, so set it on the original request + $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT)); + $original->setResponse($response); + $response->setEffectiveUrl($request->getUrl()); + } + return; + } + + $this->sendRedirectRequest($original, $request, $response); + } + + /** + * Get the original request that initiated a series of redirects + * + * @param RequestInterface $request Request to get the original request from + * + * @return RequestInterface + */ + protected function getOriginalRequest(RequestInterface $request) + { + $original = $request; + // The number of redirects is held on the original request, so determine which request that is + while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) { + $original = $parent; + } + + return $original; + } + + /** + * Create a redirect request for a specific request object + * + * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do + * (e.g. redirect POST with GET). + * + * @param RequestInterface $request Request being redirected + * @param RequestInterface $original Original request + * @param int $statusCode Status code of the redirect + * @param string $location Location header of the redirect + * + * @return RequestInterface Returns a new redirect request + * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot + */ + protected function createRedirectRequest( + RequestInterface $request, + $statusCode, + $location, + RequestInterface $original + ) { + $redirectRequest = null; + $strict = $original->getParams()->get(self::STRICT_REDIRECTS); + + // Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC + // compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST. + if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) { + $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET'); + } else { + $redirectRequest = clone $request; + } + + $redirectRequest->setIsRedirect(true); + // Always use the same response body when redirecting + $redirectRequest->setResponseBody($request->getResponseBody()); + + $location = Url::factory($location); + // If the location is not absolute, then combine it with the original URL + if (!$location->isAbsolute()) { + $originalUrl = $redirectRequest->getUrl(true); + // Remove query string parameters and just take what is present on the redirect Location header + $originalUrl->getQuery()->clear(); + $location = $originalUrl->combine((string) $location, true); + } + + $redirectRequest->setUrl($location); + + // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too) + $redirectRequest->getEventDispatcher()->addListener( + 'request.before_send', + $func = function ($e) use (&$func, $request, $redirectRequest) { + $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func); + $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request); + } + ); + + // Rewind the entity body of the request if needed + if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) { + $body = $redirectRequest->getBody(); + // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails + if ($body->ftell() && !$body->rewind()) { + throw new CouldNotRewindStreamException( + 'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably ' + . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the ' + . 'entity body of the request using setRewindFunction().' + ); + } + } + + return $redirectRequest; + } + + /** + * Prepare the request for redirection and enforce the maximum number of allowed redirects per client + * + * @param RequestInterface $original Original request + * @param RequestInterface $request Request to prepare and validate + * @param Response $response The current response + * + * @return RequestInterface + */ + protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) + { + $params = $original->getParams(); + // This is a new redirect, so increment the redirect counter + $current = $params[self::REDIRECT_COUNT] + 1; + $params[self::REDIRECT_COUNT] = $current; + // Use a provided maximum value or default to a max redirect count of 5 + $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; + + // Throw an exception if the redirect count is exceeded + if ($current > $max) { + $this->throwTooManyRedirectsException($original, $max); + return false; + } else { + // Create a redirect request based on the redirect rules set on the request + return $this->createRedirectRequest( + $request, + $response->getStatusCode(), + trim($response->getLocation()), + $original + ); + } + } + + /** + * Send a redirect request and handle any errors + * + * @param RequestInterface $original The originating request + * @param RequestInterface $request The current request being redirected + * @param Response $response The response of the current request + * + * @throws BadResponseException|\Exception + */ + protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response) + { + // Validate and create a redirect request based on the original request and current response + if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) { + try { + $redirectRequest->send(); + } catch (BadResponseException $e) { + $e->getResponse(); + if (!$e->getResponse()) { + throw $e; + } + } + } + } + + /** + * Throw a too many redirects exception for a request + * + * @param RequestInterface $original Request + * @param int $max Max allowed redirects + * + * @throws TooManyRedirectsException when too many redirects have been issued + */ + protected function throwTooManyRedirectsException(RequestInterface $original, $max) + { + $original->getEventDispatcher()->addListener( + 'request.complete', + $func = function ($e) use (&$func, $original, $max) { + $original->getEventDispatcher()->removeListener('request.complete', $func); + $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders(); + throw new TooManyRedirectsException($str); + } + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem new file mode 100755 index 000000000..18ce70381 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem @@ -0,0 +1,3870 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla downloaded on: Wed Aug 13 21:49:32 2014 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl verison 1.22. +## SHA1: bf2c15b3019e696660321d2227d942936dc50aa7 +## + + +GTE CyberTrust Global Root +========================== +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg +Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG +A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz +MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL +Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 +IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u +sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql +HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW +M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF +NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +Thawte Server CA +================ +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE +AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j +b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV +BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u +c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG +A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl +/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 +1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J +GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ +GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte Premium Server CA +======================== +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE +AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl +ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU +VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 +aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ +cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh +Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ +qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm +SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf +8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t +UCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +Equifax Secure CA +================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE +ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT +B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR +fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW +8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE +CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS +spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 +zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB +BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 +70+sB3c4 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +ValiCert Class 1 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy +MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi +GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm +DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG +lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX +icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP +Orf1LXLI +-----END CERTIFICATE----- + +ValiCert Class 2 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC +CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf +ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ +SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV +UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 +W9ViH0Pd +-----END CERTIFICATE----- + +RSA Root Certificate 1 +====================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td +3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H +BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs +3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF +V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r +on+jjBXu +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Entrust.net Secure Server CA +============================ +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg +cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl +ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG +A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi +eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p +dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ +aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 +gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw +ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l +dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw +NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow +HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN +Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 +n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Equifax Secure Global eBusiness CA +================================== +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp +bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds +b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV +PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN +qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn +hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs +MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN +I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY +NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 1 +============================= +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB +LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE +ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz +IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ +1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a +IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk +MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW +Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF +AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 +lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ +KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +RSA Security 2048 v3 +==================== +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK +ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy +MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb +BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 +Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb +WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH +KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP ++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E +FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY +v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj +0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj +VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 +nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +America Online Root Certification Authority 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG +v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z +DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh +sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP +8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z +o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf +GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF +VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft +3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds +sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America Online Root Certification Authority 2 +============================================= +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en +fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 +f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO +qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN +RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 +gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn +6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid +FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 +Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj +B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY +T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p ++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg +JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy +zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO +ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh +1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf +GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff +Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP +cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA +============================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE +ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w +HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh +bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt +vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P +jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca +C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth +vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 +22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV +HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v +dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN +BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR +EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw +MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +TDC Internet Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE +ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx +NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu +ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j +xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL +znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc +5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 +otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI +AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM +VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM +MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe +UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G +CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb +O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU +Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +UTN DATACorp SGC Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ +BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa +MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w +HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy +dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys +raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo +wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA +9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv +33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud +DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 +BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD +LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 +I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx +EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP +DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +NetLock Notary (Class A) Root +============================= +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI +EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j +ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX +DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH +EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD +VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz +cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM +D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ +z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC +/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 +tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 +4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG +A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC +Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv +bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn +LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 +ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz +IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh +IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu +b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg +Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp +bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 +ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP +ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB +CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr +KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM +8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +NetLock Business (Class B) Root +=============================== +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg +VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD +VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv +bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg +VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S +o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr +1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV +HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ +RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh +dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 +ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv +c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg +YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz +Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA +bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl +IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 +YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj +cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM +43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR +stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +NetLock Express (Class C) Root +============================== +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ +BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j +ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z +W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 +euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw +DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN +RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn +YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB +IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i +aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 +ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y +emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k +IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ +UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg +YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 +xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW +gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +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 +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 1 +============================================== +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP +MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 +acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx +MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB +TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC +aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX +yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i +Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ +8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 +W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 +sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE +q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY +nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2 +============================================== +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN +MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr +dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe +LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI +x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g +QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr +5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB +AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt +Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ +hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P +9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 +UrbnBEI= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +WellsSecure Public Root Certificate Authority +============================================= +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM +F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw +NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl +bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD +VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 +iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 +i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 +bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB +K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB +AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu +cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm +lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB +i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww +GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI +K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 +bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj +qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es +E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ +tylv2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +IGC/A +===== +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD +VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE +Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy +MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI +EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT +STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 +TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW +So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy +HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd +frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ +tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB +egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC +iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK +q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q +MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI +lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF +0mBWWg== +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE +BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL +EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 +MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz +dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT +GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG +d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N +oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc +QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ +PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb +MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG +IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD +VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 +LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A +dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA +4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg +AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA +egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 +Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO +PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv +c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h +cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw +IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT +WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV +MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER +MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp +Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal +HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT +nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE +aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK +yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB +S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. +====================================== +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT +AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg +LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w +HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ +U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh +IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN +yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU +2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 +4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP +2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm +8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf +HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa +Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK +5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b +czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g +ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF +BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug +cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf +AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX +EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v +/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 +MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 +3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk +eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f +/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h +RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU +Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 2 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw +MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw +IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 +xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ +Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u +SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G +dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ +KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj +TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP +JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk +vQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 3 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw +MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W +yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo +6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ +uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk +2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE +O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 +yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 +IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal +092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc +5A== +-----END CERTIFICATE----- + +TC TrustCenter Universal CA I +============================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN +MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg +VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw +JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC +qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv +xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw +ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O +gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j +BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG +1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy +vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 +ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a +7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +ComSign Secured CA +================== +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE +AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w +NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD +QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs +49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH +7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB +kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 +9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw +AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t +U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA +j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC +AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a +BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp +FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP +51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +Buypass Class 2 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 +MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M +cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 +0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 +0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R +uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV +1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt +7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 +fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w +wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +Buypass Class 3 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 +MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx +ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 +n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia +AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c +1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 +pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA +EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 +htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj +el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 +========================================================================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +ApplicationCA - Japanese Government +=================================== +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT +SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw +MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl +cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 +fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN +wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE +jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu +nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU +WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV +BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD +vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs +o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g +/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD +io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW +dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +============================================ +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +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 +66+KAQ== +-----END CERTIFICATE----- + +CA Disig +======== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK +QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw +MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz +bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm +GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD +Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo +hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt +ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w +gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P +AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz +aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff +ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa +BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t +WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 +mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K +ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA +4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +Juur-SK +======= +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA +c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw +DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG +SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy +aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf +TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC ++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw +UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa +Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF +MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD +HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh +AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA +cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr +AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw +cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G +A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo +ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL +abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 +IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh +Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 +yyqcjg== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi +=================================================== +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz +ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 +MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 +cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u +aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY +8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y +jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI +JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk +9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG +SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d +F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq +D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 +Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================= +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +Root CA Generalitat Valenciana +============================== +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE +ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 +IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 +WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE +CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 +F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B +ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ +D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte +JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB +AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n +dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB +ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl +AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA +YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy +AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt +AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA +YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu +AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA +OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 +dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV +BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S +b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh +TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz +Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 +NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH +iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt ++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +A-Trust-nQual-03 +================ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE +Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy +a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R +dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw +RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 +ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 +c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA +zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n +yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE +SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 +iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V +cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV +eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 +ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr +sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd +JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 +ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ +Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 +dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu +c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv +bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 +aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t +L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 +fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm +N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN +Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T +tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX +e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA +2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs +HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib +D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +StartCom Certification Authority G2 +=================================== +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE +ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O +o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG +4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi +Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul +Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs +O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H +vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L +nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS +FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa +z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ +KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk +J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ +JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG +/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc +nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld +blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc +l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm +7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm +obp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2007 +================================================= +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X +DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl +a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N +YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv +KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya +KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT +rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC +AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s +Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO +Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb +BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK +poRq0Tl9 +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +PSCProcert +========== +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk +ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ +MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz +dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl +cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw +IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw +MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w +DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD +ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp +Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC +wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA +3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh +RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO +EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 +0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU +td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw +Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp +r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ +AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz +Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId +xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp +ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH +EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h +Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k +ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG +9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG +MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG +LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 +ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy +YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o +dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq +T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN +g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q +uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 +n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn +FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo +5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq +3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 +poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y +eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +China Internet Network Information Center EV Certificates Root +============================================================== +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D +aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg +Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG +A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM +PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl +cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y +jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV +98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H +klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 +KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC +7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD +glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 +0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM +7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 +5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +Swisscom Root CA 2 +================== +-----BEGIN CERTIFICATE----- +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 +5OfNeOI5wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +Swisscom Root EV CA 2 +===================== +-----BEGIN CERTIFICATE----- +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 +l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +CA Disig Root R1 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy +3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 +u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 +m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk +CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa +YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 +vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL +LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX +ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is +XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ +04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B +LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM +CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb +VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 +YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS +ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix +lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N +UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ +a7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php new file mode 100755 index 000000000..dbd4c1841 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php @@ -0,0 +1,157 @@ +createRequest($method, $url, null, null, $options); + + if (isset($options['stream'])) { + if ($options['stream'] instanceof StreamRequestFactoryInterface) { + return $options['stream']->fromRequest($request); + } elseif ($options['stream'] == true) { + $streamFactory = new PhpStreamRequestFactory(); + return $streamFactory->fromRequest($request); + } + } + + return $request->send(); + } + + /** + * Send a GET request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function get($url, $options = array()) + { + return self::request('GET', $url, $options); + } + + /** + * Send a HEAD request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function head($url, $options = array()) + { + return self::request('HEAD', $url, $options); + } + + /** + * Send a DELETE request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function delete($url, $options = array()) + { + return self::request('DELETE', $url, $options); + } + + /** + * Send a POST request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function post($url, $options = array()) + { + return self::request('POST', $url, $options); + } + + /** + * Send a PUT request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function put($url, $options = array()) + { + return self::request('PUT', $url, $options); + } + + /** + * Send a PATCH request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function patch($url, $options = array()) + { + return self::request('PATCH', $url, $options); + } + + /** + * Send an OPTIONS request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function options($url, $options = array()) + { + return self::request('OPTIONS', $url, $options); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php new file mode 100755 index 000000000..6a4e77245 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php @@ -0,0 +1,554 @@ + null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + if (false === ($parts = parse_url($url))) { + throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url); + } + + $parts += $defaults; + + // Convert the query string into a QueryString object + if ($parts['query'] || 0 !== strlen($parts['query'])) { + $parts['query'] = QueryString::fromString($parts['query']); + } + + return new static($parts['scheme'], $parts['host'], $parts['user'], + $parts['pass'], $parts['port'], $parts['path'], $parts['query'], + $parts['fragment']); + } + + /** + * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided. + * + * @param array $parts Array of parse_url parts + * + * @return string + */ + public static function buildUrl(array $parts) + { + $url = $scheme = ''; + + if (isset($parts['scheme'])) { + $scheme = $parts['scheme']; + $url .= $scheme . ':'; + } + + if (isset($parts['host'])) { + $url .= '//'; + if (isset($parts['user'])) { + $url .= $parts['user']; + if (isset($parts['pass'])) { + $url .= ':' . $parts['pass']; + } + $url .= '@'; + } + + $url .= $parts['host']; + + // Only include the port if it is not the default port of the scheme + if (isset($parts['port']) + && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443)) + ) { + $url .= ':' . $parts['port']; + } + } + + // Add the path component if present + if (isset($parts['path']) && 0 !== strlen($parts['path'])) { + // Always ensure that the path begins with '/' if set and something is before the path + if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') { + $url .= '/'; + } + $url .= $parts['path']; + } + + // Add the query string if present + if (isset($parts['query'])) { + $url .= '?' . $parts['query']; + } + + // Ensure that # is only added to the url if fragment contains anything. + if (isset($parts['fragment'])) { + $url .= '#' . $parts['fragment']; + } + + return $url; + } + + /** + * Create a new URL from URL parts + * + * @param string $scheme Scheme of the URL + * @param string $host Host of the URL + * @param string $username Username of the URL + * @param string $password Password of the URL + * @param int $port Port of the URL + * @param string $path Path of the URL + * @param QueryString|array|string $query Query string of the URL + * @param string $fragment Fragment of the URL + */ + public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null) + { + $this->scheme = $scheme; + $this->host = $host; + $this->port = $port; + $this->username = $username; + $this->password = $password; + $this->fragment = $fragment; + if (!$query) { + $this->query = new QueryString(); + } else { + $this->setQuery($query); + } + $this->setPath($path); + } + + /** + * Clone the URL + */ + public function __clone() + { + $this->query = clone $this->query; + } + + /** + * Returns the URL as a URL string + * + * @return string + */ + public function __toString() + { + return self::buildUrl($this->getParts()); + } + + /** + * Get the parts of the URL as an array + * + * @return array + */ + public function getParts() + { + $query = (string) $this->query; + + return array( + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->getPath(), + 'query' => $query !== '' ? $query : null, + 'fragment' => $this->fragment, + ); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) + * + * @return Url + */ + public function setHost($host) + { + if (strpos($host, ':') === false) { + $this->host = $host; + } else { + list($host, $port) = explode(':', $host); + $this->host = $host; + $this->setPort($port); + } + + return $this; + } + + /** + * Get the host part of the URL + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the scheme part of the URL (http, https, ftp, etc) + * + * @param string $scheme Scheme to set + * + * @return Url + */ + public function setScheme($scheme) + { + if ($this->scheme == 'http' && $this->port == 80) { + $this->port = null; + } elseif ($this->scheme == 'https' && $this->port == 443) { + $this->port = null; + } + + $this->scheme = $scheme; + + return $this; + } + + /** + * Get the scheme part of the URL + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Set the port part of the URL + * + * @param int $port Port to set + * + * @return Url + */ + public function setPort($port) + { + $this->port = $port; + + return $this; + } + + /** + * Get the port part of the URl. Will return the default port for a given scheme if no port has been set. + * + * @return int|null + */ + public function getPort() + { + if ($this->port) { + return $this->port; + } elseif ($this->scheme == 'http') { + return 80; + } elseif ($this->scheme == 'https') { + return 443; + } + + return null; + } + + /** + * Set the path part of the URL + * + * @param array|string $path Path string or array of path segments + * + * @return Url + */ + public function setPath($path) + { + static $pathReplace = array(' ' => '%20', '?' => '%3F'); + if (is_array($path)) { + $path = '/' . implode('/', $path); + } + + $this->path = strtr($path, $pathReplace); + + return $this; + } + + /** + * Normalize the URL so that double slashes and relative paths are removed + * + * @return Url + */ + public function normalizePath() + { + if (!$this->path || $this->path == '/' || $this->path == '*') { + return $this; + } + + $results = array(); + $segments = $this->getPathSegments(); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif ($segment != '.' && $segment != '') { + $results[] = $segment; + } + } + + // Combine the normalized parts and add the leading slash if needed + $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results); + + // Add the trailing slash if necessary + if ($this->path != '/' && end($segments) == '') { + $this->path .= '/'; + } + + return $this; + } + + /** + * Add a relative path to the currently set path. + * + * @param string $relativePath Relative path to add + * + * @return Url + */ + public function addPath($relativePath) + { + if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) { + // Add a leading slash if needed + if ($relativePath[0] != '/') { + $relativePath = '/' . $relativePath; + } + $this->setPath(str_replace('//', '/', $this->path . $relativePath)); + } + + return $this; + } + + /** + * Get the path part of the URL + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get the path segments of the URL as an array + * + * @return array + */ + public function getPathSegments() + { + return array_slice(explode('/', $this->getPath()), 1); + } + + /** + * Set the password part of the URL + * + * @param string $password Password to set + * + * @return Url + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get the password part of the URL + * + * @return null|string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the username part of the URL + * + * @param string $username Username to set + * + * @return Url + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get the username part of the URl + * + * @return null|string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the query part of the URL as a QueryString object + * + * @return QueryString + */ + public function getQuery() + { + return $this->query; + } + + /** + * Set the query part of the URL + * + * @param QueryString|string|array $query Query to set + * + * @return Url + */ + public function setQuery($query) + { + if (is_string($query)) { + $output = null; + parse_str($query, $output); + $this->query = new QueryString($output); + } elseif (is_array($query)) { + $this->query = new QueryString($query); + } elseif ($query instanceof QueryString) { + $this->query = $query; + } + + return $this; + } + + /** + * Get the fragment part of the URL + * + * @return null|string + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * Set the fragment part of the URL + * + * @param string $fragment Fragment to set + * + * @return Url + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + + return $this; + } + + /** + * Check if this is an absolute URL + * + * @return bool + */ + public function isAbsolute() + { + return $this->scheme && $this->host; + } + + /** + * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4. + * + * @param string $url Relative URL to combine with + * @param bool $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first + * released, Guzzle used an incorrect algorithm for combining relative URL paths. In + * order to not break users, we introduced this flag to allow the merging of URLs based + * on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with + * "bar" would become "http://a.com/foo/bar". When this value is set to false, it would + * become "http://a.com/foo/baz/bar". + * @return Url + * @throws InvalidArgumentException + * @link http://tools.ietf.org/html/rfc3986#section-5.4 + */ + public function combine($url, $strictRfc3986 = false) + { + $url = self::factory($url); + + // Use the more absolute URL as the base URL + if (!$this->isAbsolute() && $url->isAbsolute()) { + $url = $url->combine($this); + } + + // Passing a URL with a scheme overrides everything + if ($buffer = $url->getScheme()) { + $this->scheme = $buffer; + $this->host = $url->getHost(); + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + // Setting a host overrides the entire rest of the URL + if ($buffer = $url->getHost()) { + $this->host = $buffer; + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + $path = $url->getPath(); + $query = $url->getQuery(); + + if (!$path) { + if (count($query)) { + $this->addQuery($query, $strictRfc3986); + } + } else { + if ($path[0] == '/') { + $this->path = $path; + } elseif ($strictRfc3986) { + $this->path .= '/../' . $path; + } else { + $this->path .= '/' . $path; + } + $this->normalizePath(); + $this->addQuery($query, $strictRfc3986); + } + + $this->fragment = $url->getFragment(); + + return $this; + } + + private function addQuery(QueryString $new, $strictRfc386) + { + if (!$strictRfc386) { + $new->merge($this->query); + } + + $this->query = $new; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json new file mode 100755 index 000000000..9384a5bf9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json @@ -0,0 +1,32 @@ +{ + "name": "guzzle/http", + "description": "HTTP libraries used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": ["http client", "http", "client", "Guzzle", "curl"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version", + "guzzle/parser": "self.version", + "guzzle/stream": "self.version" + }, + "suggest": { + "ext-curl": "*" + }, + "autoload": { + "psr-0": { "Guzzle\\Http": "" } + }, + "target-dir": "Guzzle/Http", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php new file mode 100755 index 000000000..c6997734c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php @@ -0,0 +1,38 @@ + array(), + 'camel' => array() + ); + + /** @var int Max entries per cache */ + protected $maxCacheSize; + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param int $maxCacheSize Maximum number of cached items to hold per cache + */ + public function __construct(InflectorInterface $inflector, $maxCacheSize = 500) + { + $this->decoratedInflector = $inflector; + $this->maxCacheSize = $maxCacheSize; + } + + public function snake($word) + { + if (!isset($this->cache['snake'][$word])) { + $this->pruneCache('snake'); + $this->cache['snake'][$word] = $this->decoratedInflector->snake($word); + } + + return $this->cache['snake'][$word]; + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + if (!isset($this->cache['camel'][$word])) { + $this->pruneCache('camel'); + $this->cache['camel'][$word] = $this->decoratedInflector->camel($word); + } + + return $this->cache['camel'][$word]; + } + + /** + * Prune one of the named caches by removing 20% of the cache if it is full + * + * @param string $cache Type of cache to prune + */ + protected function pruneCache($cache) + { + if (count($this->cache[$cache]) == $this->maxCacheSize) { + $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php new file mode 100755 index 000000000..db37e4fe4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php @@ -0,0 +1,59 @@ + array(), + 'camel' => array() + ); + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param array $snake Hash of pre-computed camel to snake + * @param array $camel Hash of pre-computed snake to camel + * @param bool $mirror Mirror snake and camel reflections + */ + public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false) + { + if ($mirror) { + $camel = array_merge(array_flip($snake), $camel); + $snake = array_merge(array_flip($camel), $snake); + } + + $this->decoratedInflector = $inflector; + $this->mapping = array( + 'snake' => $snake, + 'camel' => $camel + ); + } + + public function snake($word) + { + return isset($this->mapping['snake'][$word]) + ? $this->mapping['snake'][$word] + : $this->decoratedInflector->snake($word); + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + return isset($this->mapping['camel'][$word]) + ? $this->mapping['camel'][$word] + : $this->decoratedInflector->camel($word); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json new file mode 100755 index 000000000..93f9e7b72 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json @@ -0,0 +1,26 @@ +{ + "name": "guzzle/inflection", + "description": "Guzzle inflection component", + "homepage": "http://guzzlephp.org/", + "keywords": ["inflection", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Inflection": "" } + }, + "target-dir": "Guzzle/Inflection", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php new file mode 100755 index 000000000..1b6bd7e53 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php @@ -0,0 +1,19 @@ +getArrayIterator()->append($iterator); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php new file mode 100755 index 000000000..d76cdd439 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php @@ -0,0 +1,56 @@ +chunkSize = $chunkSize; + } + + public function rewind() + { + parent::rewind(); + $this->next(); + } + + public function next() + { + $this->chunk = array(); + for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) { + $this->chunk[] = parent::current(); + parent::next(); + } + } + + public function current() + { + return $this->chunk; + } + + public function valid() + { + return (bool) $this->chunk; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php new file mode 100755 index 000000000..b103367b6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php @@ -0,0 +1,36 @@ +callback = $callback; + } + + public function accept() + { + return call_user_func($this->callback, $this->current()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php new file mode 100755 index 000000000..7e586bda6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php @@ -0,0 +1,34 @@ +callback = $callback; + } + + public function current() + { + return call_user_func($this->callback, parent::current()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php new file mode 100755 index 000000000..de4ab0360 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php @@ -0,0 +1,27 @@ +getInnerIterator(); + while ($i instanceof \OuterIterator) { + $i = $i->getInnerIterator(); + } + + return call_user_func_array(array($i, $name), $args); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md new file mode 100755 index 000000000..8bb7e08e2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md @@ -0,0 +1,25 @@ +Guzzle Iterator +=============== + +Provides useful Iterators and Iterator decorators + +- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays +- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available +- MapIterator: Maps values before yielding +- MethodProxyIterator: Proxies missing method calls to the innermost iterator + +### Installing via Composer + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/iterator:~3.0 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json new file mode 100755 index 000000000..ee1737987 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/iterator", + "description": "Provides helpful iterators and iterator decorators", + "keywords": ["iterator", "guzzle"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": ">=2.8.0" + }, + "autoload": { + "psr-0": { "Guzzle\\Iterator": "/" } + }, + "target-dir": "Guzzle/Iterator", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php new file mode 100755 index 000000000..7f6271bcb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php @@ -0,0 +1,16 @@ +log; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php new file mode 100755 index 000000000..a70fc8d42 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php @@ -0,0 +1,34 @@ +logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras); + } + + /** + * Get logged entries + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Clears logged entries + */ + public function clearLogs() + { + $this->logs = array(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php new file mode 100755 index 000000000..d4bb73f21 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php @@ -0,0 +1,23 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + call_user_func($this->log, $message, $priority, $extras); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php new file mode 100755 index 000000000..d7ac4ea7c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php @@ -0,0 +1,18 @@ +>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}"; + const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}'; + + /** + * @var string Template used to format log messages + */ + protected $template; + + /** + * @param string $template Log message template + */ + public function __construct($template = self::DEFAULT_FORMAT) + { + $this->template = $template ?: self::DEFAULT_FORMAT; + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->template = $template; + + return $this; + } + + /** + * Returns a formatted message + * + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received + * @param CurlHandle $handle Curl handle associated with the message + * @param array $customData Associative array of custom template data + * + * @return string + */ + public function format( + RequestInterface $request, + Response $response = null, + CurlHandle $handle = null, + array $customData = array() + ) { + $cache = $customData; + + return preg_replace_callback( + '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', + function (array $matches) use ($request, $response, $handle, &$cache) { + + if (array_key_exists($matches[1], $cache)) { + return $cache[$matches[1]]; + } + + $result = ''; + switch ($matches[1]) { + case 'request': + $result = (string) $request; + break; + case 'response': + $result = (string) $response; + break; + case 'req_body': + $result = $request instanceof EntityEnclosingRequestInterface + ? (string) $request->getBody() : ''; + break; + case 'res_body': + $result = $response ? $response->getBody(true) : ''; + break; + case 'ts': + $result = gmdate('c'); + break; + case 'method': + $result = $request->getMethod(); + break; + case 'url': + $result = (string) $request->getUrl(); + break; + case 'resource': + $result = $request->getResource(); + break; + case 'protocol': + $result = 'HTTP'; + break; + case 'version': + $result = $request->getProtocolVersion(); + break; + case 'host': + $result = $request->getHost(); + break; + case 'hostname': + $result = gethostname(); + break; + case 'port': + $result = $request->getPort(); + break; + case 'code': + $result = $response ? $response->getStatusCode() : ''; + break; + case 'phrase': + $result = $response ? $response->getReasonPhrase() : ''; + break; + case 'connect_time': + $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME) + ? $handle->getInfo(CURLINFO_CONNECT_TIME) + : ($response ? $response->getInfo('connect_time') : ''); + break; + case 'total_time': + $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME) + ? $handle->getInfo(CURLINFO_TOTAL_TIME) + : ($response ? $response->getInfo('total_time') : ''); + break; + case 'curl_error': + $result = $handle ? $handle->getError() : ''; + break; + case 'curl_code': + $result = $handle ? $handle->getErrorNo() : ''; + break; + case 'curl_stderr': + $result = $handle ? $handle->getStderr() : ''; + break; + default: + if (strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeader(substr($matches[1], 11)); + } elseif ($response && strpos($matches[1], 'res_header_') === 0) { + $result = $response->getHeader(substr($matches[1], 11)); + } + } + + $cache[$matches[1]] = $result; + return $result; + }, + $this->template + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php new file mode 100755 index 000000000..6afe7b62a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php @@ -0,0 +1,34 @@ + Logger::DEBUG, + LOG_INFO => Logger::INFO, + LOG_WARNING => Logger::WARNING, + LOG_ERR => Logger::ERROR, + LOG_CRIT => Logger::CRITICAL, + LOG_ALERT => Logger::ALERT + ); + + public function __construct(Logger $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->addRecord(self::$mapping[$priority], $message, $extras); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php new file mode 100755 index 000000000..38a2b600d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php @@ -0,0 +1,36 @@ + LogLevel::DEBUG, + LOG_INFO => LogLevel::INFO, + LOG_WARNING => LogLevel::WARNING, + LOG_ERR => LogLevel::ERROR, + LOG_CRIT => LogLevel::CRITICAL, + LOG_ALERT => LogLevel::ALERT + ); + + public function __construct(LoggerInterface $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log(self::$mapping[$priority], $message, $extras); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php new file mode 100755 index 000000000..0ea8e3b1d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php @@ -0,0 +1,24 @@ +log = $logObject; + Version::warn(__CLASS__ . ' is deprecated'); + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($message, $priority, $extras); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php new file mode 100755 index 000000000..863f6a1c4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php @@ -0,0 +1,21 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($priority, $message, $extras); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json new file mode 100755 index 000000000..a8213e8b4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json @@ -0,0 +1,29 @@ +{ + "name": "guzzle/log", + "description": "Guzzle log adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["log", "adapter", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Log": "" } + }, + "suggest": { + "guzzle/http": "self.version" + }, + "target-dir": "Guzzle/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php new file mode 100755 index 000000000..4349eeb38 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php @@ -0,0 +1,131 @@ + 'Domain', + 'path' => 'Path', + 'max_age' => 'Max-Age', + 'expires' => 'Expires', + 'version' => 'Version', + 'secure' => 'Secure', + 'port' => 'Port', + 'discard' => 'Discard', + 'comment' => 'Comment', + 'comment_url' => 'Comment-Url', + 'http_only' => 'HttpOnly' + ); + + public function parseCookie($cookie, $host = null, $path = null, $decode = false) + { + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return false; + } + + // Create the default return array + $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( + 'cookies' => array(), + 'data' => array(), + 'path' => null, + 'http_only' => false, + 'discard' => false, + 'domain' => $host + )); + $foundNonCookies = 0; + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + + if (count($cookieParts) == 1) { + // Can be a single value (e.g. secure, httpOnly) + $value = true; + } else { + // Be sure to strip wrapping quotes + $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); + if ($decode) { + $value = urldecode($value); + } + } + + // Only check for non-cookies when cookies have been found + if (!empty($data['cookies'])) { + foreach (self::$cookieParts as $mapValue => $search) { + if (!strcasecmp($search, $key)) { + $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; + $foundNonCookies++; + continue 2; + } + } + } + + // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a + // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data. + $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; + } + + // Calculate the expires date + if (!$data['expires'] && $data['max_age']) { + $data['expires'] = time() + (int) $data['max_age']; + } + + // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4 + // "If the attribute-value is empty or if the first character of the + // attribute-value is not %x2F ("/"): + // Let cookie-path be the default-path. + // Otherwise: + // Let cookie-path be the attribute-value." + if (!$data['path'] || substr($data['path'], 0, 1) !== '/') { + $data['path'] = $this->getDefaultPath($path); + } + + return $data; + } + + /** + * Get default cookie path according to RFC 6265 + * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match + * + * @param string $path Request uri-path + * + * @return string + */ + protected function getDefaultPath($path) { + // "The user agent MUST use an algorithm equivalent to the following algorithm + // to compute the default-path of a cookie:" + + // "2. If the uri-path is empty or if the first character of the uri-path is not + // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. + if (empty($path) || substr($path, 0, 1) !== '/') { + return '/'; + } + + // "3. If the uri-path contains no more than one %x2F ("/") character, output + // %x2F ("/") and skip the remaining step." + if ($path === "/") { + return $path; + } + + $rightSlashPos = strrpos($path, '/'); + if ($rightSlashPos === 0) { + return "/"; + } + + // "4. Output the characters of the uri-path from the first character up to, + // but not including, the right-most %x2F ("/")." + return substr($path, 0, $rightSlashPos); + + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php new file mode 100755 index 000000000..d21ffe21c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php @@ -0,0 +1,33 @@ + $requestUrl, + 'scheme' => 'http' + ); + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = null; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php new file mode 100755 index 000000000..efc1aa322 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php @@ -0,0 +1,110 @@ +parseMessage($message); + + // Parse the protocol and protocol version + if (isset($parts['start_line'][2])) { + $startParts = explode('/', $parts['start_line'][2]); + $protocol = strtoupper($startParts[0]); + $version = isset($startParts[1]) ? $startParts[1] : '1.1'; + } else { + $protocol = 'HTTP'; + $version = '1.1'; + } + + $parsed = array( + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => $protocol, + 'version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage(isset($parts['start_line'][1]) ? $parts['start_line'][1] : '' , $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = $this->parseMessage($message); + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return array( + 'protocol' => $protocol, + 'version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array + */ + protected function parseMessage($message) + { + $startLine = null; + $headers = array(); + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + $startLine = explode(' ', $line, 3); + } elseif (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = array($headers[$key], $value); + } else { + $headers[$key][] = $value; + } + } + } + + return array( + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php new file mode 100755 index 000000000..cc448088d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php @@ -0,0 +1,27 @@ + $parts->requestMethod, + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'headers' => $parts->headers, + 'body' => $parts->body + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = http_parse_message($message); + + return array( + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'code' => $parts->responseCode, + 'reason_phrase' => $parts->responseStatus, + 'headers' => $parts->headers, + 'body' => $parts->body + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php new file mode 100755 index 000000000..f8386831c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php @@ -0,0 +1,75 @@ + 'Guzzle\\Parser\\Message\\MessageParser', + 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser', + 'url' => 'Guzzle\\Parser\\Url\\UrlParser', + 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate', + ); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new static; + } + + return self::$instance; + } + + public function __construct() + { + // Use the PECL URI template parser if available + if (extension_loaded('uri_template')) { + $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate'; + } + } + + /** + * Get a parser by name from an instance + * + * @param string $name Name of the parser to retrieve + * + * @return mixed|null + */ + public function getParser($name) + { + if (!isset($this->instances[$name])) { + if (!isset($this->mapping[$name])) { + return null; + } + $class = $this->mapping[$name]; + $this->instances[$name] = new $class(); + } + + return $this->instances[$name]; + } + + /** + * Register a custom parser by name with the register + * + * @param string $name Name or handle of the parser to register + * @param mixed $parser Instantiated parser to register + */ + public function registerParser($name, $parser) + { + $this->instances[$name] = $parser; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php new file mode 100755 index 000000000..b0764e837 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php @@ -0,0 +1,26 @@ + true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true + ); + + /** @var array Delimiters */ + private static $delims = array( + ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' + ); + + /** @var array Percent encoded delimiters */ + private static $delimsPct = array( + '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D' + ); + + public function expand($template, array $variables) + { + if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template); + } + + /** + * Set the regex patten used to expand URI templates + * + * @param string $regexPattern + */ + public function setRegex($regexPattern) + { + $this->regex = $regexPattern; + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + // Check for URI operators + $operator = ''; + + if (isset(self::$operatorHash[$expression[0]])) { + $operator = $expression[0]; + $expression = substr($expression, 1); + } + + $values = explode(',', $expression); + foreach ($values as &$value) { + $value = trim($value); + $varspec = array(); + $substrPos = strpos($value, ':'); + if ($substrPos) { + $varspec['value'] = substr($value, 0, $substrPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $substrPos + 1); + } elseif (substr($value, -1) == '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $value = $varspec; + } + + return array( + 'operator' => $operator, + 'values' => $values + ); + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = array( + '+' => '%20', + '%7e' => '~' + ); + + $parsed = self::parseExpression($matches[1]); + $replacements = array(); + + $prefix = $parsed['operator']; + $joiner = $parsed['operator']; + $useQueryString = false; + if ($parsed['operator'] == '?') { + $joiner = '&'; + $useQueryString = true; + } elseif ($parsed['operator'] == '&') { + $useQueryString = true; + } elseif ($parsed['operator'] == '#') { + $joiner = ','; + } elseif ($parsed['operator'] == ';') { + $useQueryString = true; + } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') { + $joiner = ','; + $prefix = ''; + } + + foreach ($parsed['values'] as $value) { + + if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQueryString = $useQueryString; + $expanded = ''; + + if (is_array($variable)) { + + $isAssoc = $this->isAssoc($variable); + $kvp = array(); + foreach ($variable as $key => $var) { + + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] == '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested structures + $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQueryString) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQueryString = false; + } elseif ($value['modifier'] == '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode modifier with an associative array + $actuallyUseQueryString = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the explode modifier is not set, then the + // result must be a comma separated list of keys followed by their respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + + } else { + if ($value['modifier'] == ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQueryString) { + if (!$expanded && $joiner != '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } + + /** + * Removes percent encoding on reserved characters (used with + and # modifiers) + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php new file mode 100755 index 000000000..c81d51548 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php @@ -0,0 +1,21 @@ +utf8 = $utf8; + } + + public function parseUrl($url) + { + Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()'); + + static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + $parts = parse_url($url); + + // Need to handle query parsing specially for UTF-8 requirements + if ($this->utf8 && isset($parts['query'])) { + $queryPos = strpos($url, '?'); + if (isset($parts['fragment'])) { + $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1); + } else { + $parts['query'] = substr($url, $queryPos + 1); + } + } + + return $parts + $defaults; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php new file mode 100755 index 000000000..89ac4b307 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php @@ -0,0 +1,19 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Parser": "" } + }, + "target-dir": "Guzzle/Parser", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php new file mode 100755 index 000000000..ae5941873 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php @@ -0,0 +1,84 @@ + 'onBeforeSend', + 'request.exception' => 'onRequestTimeout', + 'request.sent' => 'onRequestSent', + 'curl.callback.progress' => 'onCurlProgress' + ); + } + + /** + * Event used to ensure that progress callback are emitted from the curl handle's request mediator. + * + * @param Event $event + */ + public function onBeforeSend(Event $event) + { + // Ensure that progress callbacks are dispatched + $event['request']->getCurlOptions()->set('progress', true); + } + + /** + * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to + * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with + * transmitting the request, and tell curl not download a body. + * + * @param Event $event + */ + public function onCurlProgress(Event $event) + { + if ($event['handle'] && + ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded'])) + ) { + // Timeout after 1ms + curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1); + // Even if the response is quick, tell curl not to download the body. + // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the + // request method is not converted to a HEAD request before the request was sent via curl. + if ($event['uploaded']) { + curl_setopt($event['handle'], CURLOPT_NOBODY, true); + } + } + } + + /** + * Event emitted when a curl exception occurs. Ignore the exception and set a mock response. + * + * @param Event $event + */ + public function onRequestTimeout(Event $event) + { + if ($event['exception'] instanceof CurlException) { + $event['request']->setResponse(new Response(200, array( + 'X-Guzzle-Async' => 'Did not wait for the response' + ))); + } + } + + /** + * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the + * caller that there is no body in the message. + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + // Let the caller know this was meant to be async + $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json new file mode 100755 index 000000000..dc3fc5bf8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-async", + "description": "Guzzle async request plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Async": "" } + }, + "target-dir": "Guzzle/Plugin/Async", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php new file mode 100755 index 000000000..0a8598345 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php @@ -0,0 +1,91 @@ +next = $next; + } + + /** + * Get the next backoff strategy in the chain + * + * @return AbstractBackoffStrategy|null + */ + public function getNext() + { + return $this->next; + } + + public function getBackoffPeriod( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ) { + $delay = $this->getDelay($retries, $request, $response, $e); + if ($delay === false) { + // The strategy knows that this must not be retried + return false; + } elseif ($delay === null) { + // If the strategy is deferring a decision and the next strategy will not make a decision then return false + return !$this->next || !$this->next->makesDecision() + ? false + : $this->next->getBackoffPeriod($retries, $request, $response, $e); + } elseif ($delay === true) { + // if the strategy knows that it must retry but is deferring to the next to determine the delay + if (!$this->next) { + return 0; + } else { + $next = $this->next; + while ($next->makesDecision() && $next->getNext()) { + $next = $next->getNext(); + } + return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0; + } + } else { + return $delay; + } + } + + /** + * Check if the strategy does filtering and makes decisions on whether or not to retry. + * + * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff + * decision. + * + * @return bool + */ + abstract public function makesDecision(); + + /** + * Implement the concrete strategy + * + * @param int $retries Number of retries of the request + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received. Note that there may not be a response + * @param HttpException $e Exception that was encountered if any + * + * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true + * or null to defer to the next strategy if available, and if not, return 0. + */ + abstract protected function getDelay( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php new file mode 100755 index 000000000..6ebee6c1a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php @@ -0,0 +1,40 @@ +errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1); + $this->next = $next; + } + + /** + * Get the default failure codes to retry + * + * @return array + */ + public static function getDefaultFailureCodes() + { + return static::$defaultErrorCodes; + } + + public function makesDecision() + { + return true; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php new file mode 100755 index 000000000..ec54c289e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php @@ -0,0 +1,76 @@ +logger = $logger; + $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT); + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->formatter->setTemplate($template); + + return $this; + } + + /** + * Called when a request is being retried + * + * @param Event $event Event emitted + */ + public function onRequestRetry(Event $event) + { + $this->logger->log($this->formatter->format( + $event['request'], + $event['response'], + $event['handle'], + array( + 'retries' => $event['retries'], + 'delay' => $event['delay'] + ) + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php new file mode 100755 index 000000000..99ace0538 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php @@ -0,0 +1,126 @@ +strategy = $strategy; + } + + /** + * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors + * + * @param int $maxRetries Maximum number of retries + * @param array $httpCodes HTTP response codes to retry + * @param array $curlCodes cURL error codes to retry + * + * @return self + */ + public static function getExponentialBackoff( + $maxRetries = 3, + array $httpCodes = null, + array $curlCodes = null + ) { + return new self(new TruncatedBackoffStrategy($maxRetries, + new HttpBackoffStrategy($httpCodes, + new CurlBackoffStrategy($curlCodes, + new ExponentialBackoffStrategy() + ) + ) + )); + } + + public static function getAllEvents() + { + return array(self::RETRY_EVENT); + } + + public static function getSubscribedEvents() + { + return array( + 'request.sent' => 'onRequestSent', + 'request.exception' => 'onRequestSent', + CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll' + ); + } + + /** + * Called when a request has been sent and isn't finished processing + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $exception = $event['exception']; + + $params = $request->getParams(); + $retries = (int) $params->get(self::RETRY_PARAM); + $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception); + + if ($delay !== false) { + // Calculate how long to wait until the request should be retried + $params->set(self::RETRY_PARAM, ++$retries) + ->set(self::DELAY_PARAM, microtime(true) + $delay); + // Send the request again + $request->setState(RequestInterface::STATE_TRANSFER); + $this->dispatch(self::RETRY_EVENT, array( + 'request' => $request, + 'response' => $response, + 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null, + 'retries' => $retries, + 'delay' => $delay + )); + } + } + + /** + * Called when a request is polling in the curl multi object + * + * @param Event $event + */ + public function onRequestPoll(Event $event) + { + $request = $event['request']; + $delay = $request->getParams()->get(self::DELAY_PARAM); + + // If the duration of the delay has passed, retry the request using the pool + if (null !== $delay && microtime(true) >= $delay) { + // Remove the request from the pool and then add it back again. This is required for cURL to know that we + // want to retry sending the easy handle. + $request->getParams()->remove(self::DELAY_PARAM); + // Rewind the request body if possible + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) { + $request->getBody()->seek(0); + } + $multi = $event['curl_multi']; + $multi->remove($request); + $multi->add($request); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php new file mode 100755 index 000000000..4e590dbe0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php @@ -0,0 +1,30 @@ +callback = $callback; + $this->decision = (bool) $decision; + $this->next = $next; + } + + public function makesDecision() + { + return $this->decision; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return call_user_func($this->callback, $retries, $request, $response, $e); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php new file mode 100755 index 000000000..061d2a407 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php @@ -0,0 +1,34 @@ +delay = $delay; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $this->delay; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php new file mode 100755 index 000000000..a584ed4a2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php @@ -0,0 +1,28 @@ +errorCodes[$e->getErrorNo()]) ? true : null; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php new file mode 100755 index 000000000..fb2912d50 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php @@ -0,0 +1,25 @@ +isSuccessful()) { + return false; + } else { + return isset($this->errorCodes[$response->getStatusCode()]) ? true : null; + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php new file mode 100755 index 000000000..b35e8a490 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php @@ -0,0 +1,36 @@ +step = $step; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries * $this->step; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php new file mode 100755 index 000000000..4fd73fedf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php @@ -0,0 +1,25 @@ +errorCodes[$response->getReasonPhrase()]) ? true : null; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php new file mode 100755 index 000000000..3608f3584 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php @@ -0,0 +1,36 @@ +max = $maxRetries; + $this->next = $next; + } + + public function makesDecision() + { + return true; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries < $this->max ? null : false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json new file mode 100755 index 000000000..91c122cb4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-backoff", + "description": "Guzzle backoff retry plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Backoff": "" } + }, + "target-dir": "Guzzle/Plugin/Backoff", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php new file mode 100755 index 000000000..7790f8844 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php @@ -0,0 +1,11 @@ + new DefaultCacheStorage($options)); + } elseif ($options instanceof CacheStorageInterface) { + $options = array('storage' => $options); + } elseif ($options) { + $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options))); + } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) { + // @codeCoverageIgnoreStart + throw new InvalidArgumentException('No cache was provided and Doctrine is not installed'); + // @codeCoverageIgnoreEnd + } + } + + $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false; + + // Add a cache storage if a cache adapter was provided + $this->storage = isset($options['storage']) + ? $options['storage'] + : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); + + if (!isset($options['can_cache'])) { + $this->canCache = new DefaultCanCacheStrategy(); + } else { + $this->canCache = is_callable($options['can_cache']) + ? new CallbackCanCacheStrategy($options['can_cache']) + : $options['can_cache']; + } + + // Use the provided revalidation strategy or the default + $this->revalidation = isset($options['revalidation']) + ? $options['revalidation'] + : new DefaultRevalidation($this->storage, $this->canCache); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -255), + 'request.sent' => array('onRequestSent', 255), + 'request.error' => array('onRequestError', 0), + 'request.exception' => array('onRequestException', 0), + ); + } + + /** + * Check if a response in cache will satisfy the request before sending + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + if (!$this->canCache->canCacheRequest($request)) { + switch ($request->getMethod()) { + case 'PURGE': + $this->purge($request); + $request->setResponse(new Response(200, array(), 'purged')); + break; + case 'PUT': + case 'POST': + case 'DELETE': + case 'PATCH': + if ($this->autoPurge) { + $this->purge($request); + } + } + return; + } + + if ($response = $this->storage->fetch($request)) { + $params = $request->getParams(); + $params['cache.lookup'] = true; + $response->setHeader( + 'Age', + time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now') + ); + // Validate that the response satisfies the request + if ($this->canResponseSatisfyRequest($request, $response)) { + if (!isset($params['cache.hit'])) { + $params['cache.hit'] = true; + } + $request->setResponse($response); + } + } + } + + /** + * If possible, store a response in cache after sending + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + + if ($request->getParams()->get('cache.hit') === null && + $this->canCache->canCacheRequest($request) && + $this->canCache->canCacheResponse($response) + ) { + $this->storage->cache($request, $response); + } + + $this->addResponseHeaders($request, $response); + } + + /** + * If possible, return a cache response on an error + * + * @param Event $event + */ + public function onRequestError(Event $event) + { + $request = $event['request']; + + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader( + 'Age', + time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now') + ); + + if ($this->canResponseSatisfyFailedRequest($request, $response)) { + $request->getParams()->set('cache.hit', 'error'); + $this->addResponseHeaders($request, $response); + $event['response'] = $response; + $event->stopPropagation(); + } + } + } + + /** + * If possible, set a cache response on a cURL exception + * + * @param Event $event + * + * @return null + */ + public function onRequestException(Event $event) + { + if (!$event['exception'] instanceof CurlException) { + return; + } + + $request = $event['request']; + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now')); + if (!$this->canResponseSatisfyFailedRequest($request, $response)) { + return; + } + $request->getParams()->set('cache.hit', 'error'); + $request->setResponse($response); + $this->addResponseHeaders($request, $response); + $event->stopPropagation(); + } + } + + /** + * Check if a cache response satisfies a request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyRequest(RequestInterface $request, Response $response) + { + $responseAge = $response->calculateAge(); + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + + // Check the request's max-age header against the age of the response + if ($reqc && $reqc->hasDirective('max-age') && + $responseAge > $reqc->getDirective('max-age')) { + return false; + } + + // Check the response's max-age header + if ($response->isFresh() === false) { + $maxStale = $reqc ? $reqc->getDirective('max-stale') : null; + if (null !== $maxStale) { + if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) { + return false; + } + } elseif ($resc && $resc->hasDirective('max-age') + && $responseAge > $resc->getDirective('max-age') + ) { + return false; + } + } + + if ($this->revalidation->shouldRevalidate($request, $response)) { + try { + return $this->revalidation->revalidate($request, $response); + } catch (CurlException $e) { + $request->getParams()->set('cache.hit', 'error'); + return $this->canResponseSatisfyFailedRequest($request, $response); + } + } + + return true; + } + + /** + * Check if a cache response satisfies a failed request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response) + { + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null; + $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null; + + if (!$requestStaleIfError && !$responseStaleIfError) { + return false; + } + + if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) { + return false; + } + + if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) { + return false; + } + + return true; + } + + /** + * Purge all cache entries for a given URL + * + * @param string $url URL to purge + */ + public function purge($url) + { + // BC compatibility with previous version that accepted a Request object + $url = $url instanceof RequestInterface ? $url->getUrl() : $url; + $this->storage->purge($url); + } + + /** + * Add the plugin's headers to a response + * + * @param RequestInterface $request Request + * @param Response $response Response to add headers to + */ + protected function addResponseHeaders(RequestInterface $request, Response $response) + { + $params = $request->getParams(); + $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache'; + if ($header = $response->getHeader('X-Cache-Lookup')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $lookup; + $response->setHeader('X-Cache-Lookup', array_unique($values)); + } else { + $response->setHeader('X-Cache-Lookup', $lookup); + } + + if ($params['cache.hit'] === true) { + $xcache = 'HIT from GuzzleCache'; + } elseif ($params['cache.hit'] == 'error') { + $xcache = 'HIT_ERROR from GuzzleCache'; + } else { + $xcache = 'MISS from GuzzleCache'; + } + + if ($header = $response->getHeader('X-Cache')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $xcache; + $response->setHeader('X-Cache', array_unique($values)); + } else { + $response->setHeader('X-Cache', $xcache); + } + + if ($response->isFresh() === false) { + $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); + if ($params['cache.hit'] === 'error') { + $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php new file mode 100755 index 000000000..f3d915458 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php @@ -0,0 +1,43 @@ +requestCallback = $requestCallback; + $this->responseCallback = $responseCallback; + } + + public function canCacheRequest(RequestInterface $request) + { + return $this->requestCallback + ? call_user_func($this->requestCallback, $request) + : parent::canCacheRequest($request); + } + + public function canCacheResponse(Response $response) + { + return $this->responseCallback + ? call_user_func($this->responseCallback, $response) + : parent::canCacheResponse($response); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php new file mode 100755 index 000000000..6e01a8e74 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php @@ -0,0 +1,30 @@ +getParams()->get(self::CACHE_KEY); + + if (!$key) { + + $cloned = clone $request; + $cloned->removeHeader('Cache-Control'); + + // Check to see how and if the key should be filtered + foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) { + $pieces = array_map('trim', explode('=', $part)); + if (isset($pieces[1])) { + foreach (array_map('trim', explode(',', $pieces[1])) as $remove) { + if ($pieces[0] == 'header') { + $cloned->removeHeader($remove); + } elseif ($pieces[0] == 'query') { + $cloned->getQuery()->remove($remove); + } + } + } + } + + $raw = (string) $cloned; + $key = 'GZ' . md5($raw); + $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw); + } + + return $key; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php new file mode 100755 index 000000000..26d7a8b27 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php @@ -0,0 +1,266 @@ +cache = CacheAdapterFactory::fromCache($cache); + $this->defaultTtl = $defaultTtl; + $this->keyPrefix = $keyPrefix; + } + + public function cache(RequestInterface $request, Response $response) + { + $currentTime = time(); + + $overrideTtl = $request->getParams()->get('cache.override_ttl'); + if ($overrideTtl) { + $ttl = $overrideTtl; + } else { + $maxAge = $response->getMaxAge(); + if ($maxAge !== null) { + $ttl = $maxAge; + } else { + $ttl = $this->defaultTtl; + } + } + + if ($cacheControl = $response->getHeader('Cache-Control')) { + $stale = $cacheControl->getDirective('stale-if-error'); + if ($stale === true) { + $ttl += $ttl; + } else if (is_numeric($stale)) { + $ttl += $stale; + } + } + + // Determine which manifest key should be used + $key = $this->getCacheKey($request); + $persistedRequest = $this->persistHeaders($request); + $entries = array(); + + if ($manifest = $this->cache->fetch($key)) { + // Determine which cache entries should still be in the cache + $vary = $response->getVary(); + foreach (unserialize($manifest) as $entry) { + // Check if the entry is expired + if ($entry[4] < $currentTime) { + continue; + } + $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : ''; + if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) { + $entries[] = $entry; + } + } + } + + // Persist the response body if needed + $bodyDigest = null; + if ($response->getBody() && $response->getBody()->getContentLength() > 0) { + $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody()); + $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl); + } + + array_unshift($entries, array( + $persistedRequest, + $this->persistHeaders($response), + $response->getStatusCode(), + $bodyDigest, + $currentTime + $ttl + )); + + $this->cache->save($key, serialize($entries)); + } + + public function delete(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if ($entries = $this->cache->fetch($key)) { + // Delete each cached body + foreach (unserialize($entries) as $entry) { + if ($entry[3]) { + $this->cache->delete($entry[3]); + } + } + $this->cache->delete($key); + } + } + + public function purge($url) + { + foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) { + $this->delete(new Request($method, $url)); + } + } + + public function fetch(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if (!($entries = $this->cache->fetch($key))) { + return null; + } + + $match = null; + $headers = $this->persistHeaders($request); + $entries = unserialize($entries); + foreach ($entries as $index => $entry) { + if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) { + $match = $entry; + break; + } + } + + if (!$match) { + return null; + } + + // Ensure that the response is not expired + $response = null; + if ($match[4] < time()) { + $response = -1; + } else { + $response = new Response($match[2], $match[1]); + if ($match[3]) { + if ($body = $this->cache->fetch($match[3])) { + $response->setBody($body); + } else { + // The response is not valid because the body was somehow deleted + $response = -1; + } + } + } + + if ($response === -1) { + // Remove the entry from the metadata and update the cache + unset($entries[$index]); + if ($entries) { + $this->cache->save($key, serialize($entries)); + } else { + $this->cache->delete($key); + } + return null; + } + + return $response; + } + + /** + * Hash a request URL into a string that returns cache metadata + * + * @param RequestInterface $request + * + * @return string + */ + protected function getCacheKey(RequestInterface $request) + { + // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth) + if ($filter = $request->getParams()->get('cache.key_filter')) { + $url = $request->getUrl(true); + foreach (explode(',', $filter) as $remove) { + $url->getQuery()->remove(trim($remove)); + } + } else { + $url = $request->getUrl(); + } + + return $this->keyPrefix . md5($request->getMethod() . ' ' . $url); + } + + /** + * Create a cache key for a response's body + * + * @param string $url URL of the entry + * @param EntityBodyInterface $body Response body + * + * @return string + */ + protected function getBodyKey($url, EntityBodyInterface $body) + { + return $this->keyPrefix . md5($url) . $body->getContentMd5(); + } + + /** + * Determines whether two Request HTTP header sets are non-varying + * + * @param string $vary Response vary header + * @param array $r1 HTTP header array + * @param array $r2 HTTP header array + * + * @return bool + */ + private function requestsMatch($vary, $r1, $r2) + { + if ($vary) { + foreach (explode(',', $vary) as $header) { + $key = trim(strtolower($header)); + $v1 = isset($r1[$key]) ? $r1[$key] : null; + $v2 = isset($r2[$key]) ? $r2[$key] : null; + if ($v1 !== $v2) { + return false; + } + } + } + + return true; + } + + /** + * Creates an array of cacheable and normalized message headers + * + * @param MessageInterface $message + * + * @return array + */ + private function persistHeaders(MessageInterface $message) + { + // Headers are excluded from the caching (see RFC 2616:13.5.1) + static $noCache = array( + 'age' => true, + 'connection' => true, + 'keep-alive' => true, + 'proxy-authenticate' => true, + 'proxy-authorization' => true, + 'te' => true, + 'trailers' => true, + 'transfer-encoding' => true, + 'upgrade' => true, + 'set-cookie' => true, + 'set-cookie2' => true + ); + + // Clone the response to not destroy any necessary headers when caching + $headers = $message->getHeaders()->getAll(); + $headers = array_diff_key($headers, $noCache); + // Cast the headers to a string + $headers = array_map(function ($h) { return (string) $h; }, $headers); + + return $headers; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php new file mode 100755 index 000000000..3ca1fbf19 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php @@ -0,0 +1,32 @@ +getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) { + return false; + } + + // Never cache requests when using no-store + if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return true; + } + + public function canCacheResponse(Response $response) + { + return $response->isSuccessful() && $response->canCache(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php new file mode 100755 index 000000000..af33234ee --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php @@ -0,0 +1,174 @@ +storage = $cache; + $this->canCache = $canCache ?: new DefaultCanCacheStrategy(); + } + + public function revalidate(RequestInterface $request, Response $response) + { + try { + $revalidate = $this->createRevalidationRequest($request, $response); + $validateResponse = $revalidate->send(); + if ($validateResponse->getStatusCode() == 200) { + return $this->handle200Response($request, $validateResponse); + } elseif ($validateResponse->getStatusCode() == 304) { + return $this->handle304Response($request, $validateResponse, $response); + } + } catch (BadResponseException $e) { + $this->handleBadResponse($e); + } + + // Other exceptions encountered in the revalidation request are ignored + // in hopes that sending a request to the origin server will fix it + return false; + } + + public function shouldRevalidate(RequestInterface $request, Response $response) + { + if ($request->getMethod() != RequestInterface::GET) { + return false; + } + + $reqCache = $request->getHeader('Cache-Control'); + $resCache = $response->getHeader('Cache-Control'); + + $revalidate = $request->getHeader('Pragma') == 'no-cache' || + ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) || + ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate'))); + + // Use the strong ETag validator if available and the response contains no Cache-Control directive + if (!$revalidate && !$resCache && $response->hasHeader('ETag')) { + $revalidate = true; + } + + return $revalidate; + } + + /** + * Handles a bad response when attempting to revalidate + * + * @param BadResponseException $e Exception encountered + * + * @throws BadResponseException + */ + protected function handleBadResponse(BadResponseException $e) + { + // 404 errors mean the resource no longer exists, so remove from + // cache, and prevent an additional request by throwing the exception + if ($e->getResponse()->getStatusCode() == 404) { + $this->storage->delete($e->getRequest()); + throw $e; + } + } + + /** + * Creates a request to use for revalidation + * + * @param RequestInterface $request Request + * @param Response $response Response to revalidate + * + * @return RequestInterface returns a revalidation request + */ + protected function createRevalidationRequest(RequestInterface $request, Response $response) + { + $revalidate = clone $request; + $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control'); + + if ($response->getLastModified()) { + $revalidate->setHeader('If-Modified-Since', $response->getLastModified()); + } + + if ($response->getEtag()) { + $revalidate->setHeader('If-None-Match', $response->getEtag()); + } + + // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations + $dispatcher = $revalidate->getEventDispatcher(); + foreach ($dispatcher->getListeners() as $eventName => $listeners) { + foreach ($listeners as $listener) { + if (is_array($listener) && $listener[0] instanceof CachePlugin) { + $dispatcher->removeListener($eventName, $listener); + } + } + } + + return $revalidate; + } + + /** + * Handles a 200 response response from revalidating. The server does not support validation, so use this response. + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle200Response(RequestInterface $request, Response $validateResponse) + { + $request->setResponse($validateResponse); + if ($this->canCache->canCacheResponse($validateResponse)) { + $this->storage->cache($request, $validateResponse); + } + + return false; + } + + /** + * Handle a 304 response and ensure that it is still valid + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * @param Response $response Original cached response + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response) + { + static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'); + + // Make sure that this response has the same ETag + if ($validateResponse->getEtag() != $response->getEtag()) { + return false; + } + + // Replace cached headers with any of these headers from the + // origin server that might be more up to date + $modified = false; + foreach ($replaceHeaders as $name) { + if ($validateResponse->hasHeader($name)) { + $modified = true; + $response->setHeader($name, $validateResponse->getHeader($name)); + } + } + + // Store the updated response in cache + if ($modified && $this->canCache->canCacheResponse($response)) { + $this->storage->cache($request, $response); + } + + return true; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php new file mode 100755 index 000000000..88b86f3ca --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php @@ -0,0 +1,19 @@ +=5.3.2", + "guzzle/http": "self.version", + "guzzle/cache": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cache": "" } + }, + "target-dir": "Guzzle/Plugin/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php new file mode 100755 index 000000000..5218e5f0e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php @@ -0,0 +1,538 @@ + '', + 'value' => '', + 'domain' => '', + 'path' => '/', + 'expires' => null, + 'max_age' => 0, + 'comment' => null, + 'comment_url' => null, + 'port' => array(), + 'version' => null, + 'secure' => false, + 'discard' => false, + 'http_only' => false + ); + + $this->data = array_merge($defaults, $data); + // Extract the expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the expires date + $this->setExpires(time() + (int) $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires(strtotime($this->getExpires())); + } + } + + /** + * Get the cookie as an array + * + * @return array + */ + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + * + * @return Cookie + */ + public function setName($name) + { + return $this->setData('name', $name); + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + * + * @return Cookie + */ + public function setValue($value) + { + return $this->setData('value', $value); + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + * + * @return Cookie + */ + public function setDomain($domain) + { + return $this->setData('domain', $domain); + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + * + * @return Cookie + */ + public function setPath($path) + { + return $this->setData('path', $path); + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['max_age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + * + * @return Cookie + */ + public function setMaxAge($maxAge) + { + return $this->setData('max_age', $maxAge); + } + + /** + * The UNIX timestamp when the cookie expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + * + * @return Cookie + */ + public function setExpires($timestamp) + { + return $this->setData('expires', $timestamp); + } + + /** + * Version of the cookie specification. RFC 2965 is 1 + * + * @return mixed + */ + public function getVersion() + { + return $this->data['version']; + } + + /** + * Set the cookie version + * + * @param string|int $version Version to set + * + * @return Cookie + */ + public function setVersion($version) + { + return $this->setData('version', $version); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + * + * @return Cookie + */ + public function setSecure($secure) + { + return $this->setData('secure', (bool) $secure); + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + * + * @return Cookie + */ + public function setDiscard($discard) + { + return $this->setData('discard', $discard); + } + + /** + * Get the comment + * + * @return string|null + */ + public function getComment() + { + return $this->data['comment']; + } + + /** + * Set the comment of the cookie + * + * @param string $comment Cookie comment + * + * @return Cookie + */ + public function setComment($comment) + { + return $this->setData('comment', $comment); + } + + /** + * Get the comment URL of the cookie + * + * @return string|null + */ + public function getCommentUrl() + { + return $this->data['comment_url']; + } + + /** + * Set the comment URL of the cookie + * + * @param string $commentUrl Cookie comment URL for more information + * + * @return Cookie + */ + public function setCommentUrl($commentUrl) + { + return $this->setData('comment_url', $commentUrl); + } + + /** + * Get an array of acceptable ports this cookie can be used with + * + * @return array + */ + public function getPorts() + { + return $this->data['port']; + } + + /** + * Set a list of acceptable ports this cookie can be used with + * + * @param array $ports Array of acceptable ports + * + * @return Cookie + */ + public function setPorts(array $ports) + { + return $this->setData('port', $ports); + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['http_only']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + * + * @return Cookie + */ + public function setHttpOnly($httpOnly) + { + return $this->setData('http_only', $httpOnly); + } + + /** + * Get an array of extra cookie data + * + * @return array + */ + public function getAttributes() + { + return $this->data['data']; + } + + /** + * Get a specific data point from the extra cookie data + * + * @param string $name Name of the data point to retrieve + * + * @return null|string + */ + public function getAttribute($name) + { + return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null; + } + + /** + * Set a cookie data attribute + * + * @param string $name Name of the attribute to set + * @param string $value Value to set + * + * @return Cookie + */ + public function setAttribute($name, $value) + { + $this->data['data'][$name] = $value; + + return $this; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4 + // A request-path path-matches a given cookie-path if at least one of + // the following conditions holds: + + // o The cookie-path and the request-path are identical. + if ($path == $this->getPath()) { + return true; + } + + $pos = stripos($path, $this->getPath()); + if ($pos === 0) { + // o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + if (substr($this->getPath(), -1, 1) === "/") { + return true; + } + + // o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- + // path is a %x2F ("/") character. + if (substr($path, strlen($this->getPath()), 1) === "/") { + return true; + } + } + + return false; + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain); + } + + /** + * Check if the cookie is compatible with a specific port + * + * @param int $port Port to check + * + * @return bool + */ + public function matchesPort($port) + { + return count($this->getPorts()) == 0 || in_array($port, $this->getPorts()); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (strpbrk($name, self::getInvalidCharacters()) !== false) { + return 'The cookie name must not contain invalid characters: ' . $name; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name in a private network + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } + + /** + * Set a value and return the cookie object + * + * @param string $key Key to set + * @param string $value Value to set + * + * @return Cookie + */ + private function setData($key, $value) + { + $this->data[$key] = $value; + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php new file mode 100755 index 000000000..6b675039f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php @@ -0,0 +1,237 @@ +strictMode = $strictMode; + } + + /** + * Enable or disable strict mode on the cookie jar + * + * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them. + * + * @return self + */ + public function setStrictMode($strictMode) + { + $this->strictMode = $strictMode; + } + + public function remove($domain = null, $path = null, $name = null) + { + $cookies = $this->all($domain, $path, $name, false, false); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) { + return !in_array($cookie, $cookies, true); + }); + + return $this; + } + + public function removeTemporary() + { + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + }); + + return $this; + } + + public function removeExpired() + { + $currentTime = time(); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) { + return !$cookie->getExpires() || $currentTime < $cookie->getExpires(); + }); + + return $this; + } + + public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true) + { + return array_values(array_filter($this->cookies, function (Cookie $cookie) use ( + $domain, + $path, + $name, + $skipDiscardable, + $skipExpired + ) { + return false === (($name && $cookie->getName() != $name) || + ($skipExpired && $cookie->isExpired()) || + ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) || + ($path && !$cookie->matchesPath($path)) || + ($domain && !$cookie->matchesDomain($domain))); + })); + } + + public function add(Cookie $cookie) + { + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new InvalidCookieException($result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, domain, port and name are identical + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getPorts() != $cookie->getPorts() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + /** + * Serializes the cookie cookieJar + * + * @return string + */ + public function serialize() + { + // Only serialize long term cookies and unexpired cookies + return json_encode(array_map(function (Cookie $cookie) { + return $cookie->toArray(); + }, $this->all(null, null, null, true, true))); + } + + /** + * Unserializes the cookie cookieJar + */ + public function unserialize($data) + { + $data = json_decode($data, true); + if (empty($data)) { + $this->cookies = array(); + } else { + $this->cookies = array_map(function (array $cookie) { + return new Cookie($cookie); + }, $data); + } + } + + /** + * Returns the total number of stored cookies + * + * @return int + */ + public function count() + { + return count($this->cookies); + } + + /** + * Returns an iterator + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->cookies); + } + + public function addCookiesFromResponse(Response $response, RequestInterface $request = null) + { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { + $parser = ParserRegistry::getInstance()->getParser('cookie'); + foreach ($cookieHeader as $cookie) { + if ($parsed = $request + ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath()) + : $parser->parseCookie($cookie) + ) { + // Break up cookie v2 into multiple cookies + foreach ($parsed['cookies'] as $key => $value) { + $row = $parsed; + $row['name'] = $key; + $row['value'] = $value; + unset($row['cookies']); + $this->add(new Cookie($row)); + } + } + } + } + } + + public function getMatchingCookies(RequestInterface $request) + { + // Find cookies that match this request + $cookies = $this->all($request->getHost(), $request->getPath()); + // Remove ineligible cookies + foreach ($cookies as $index => $cookie) { + if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) { + unset($cookies[$index]); + } + }; + + return $cookies; + } + + /** + * If a cookie already exists and the server asks to set it again with a null value, the + * cookie must be deleted. + * + * @param \Guzzle\Plugin\Cookie\Cookie $cookie + */ + private function removeCookieIfEmpty(Cookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName()); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php new file mode 100755 index 000000000..7faa7d21f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php @@ -0,0 +1,85 @@ +filename = $cookieFile; + $this->load(); + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->persist(); + } + + /** + * Save the contents of the data array to the file + * + * @throws RuntimeException if the file cannot be found or created + */ + protected function persist() + { + if (false === file_put_contents($this->filename, $this->serialize())) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load the contents of the json formatted file into the data array and discard any unsaved state + */ + protected function load() + { + $json = file_get_contents($this->filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + + $this->unserialize($json); + $this->cookies = $this->cookies ?: array(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php new file mode 100755 index 000000000..df3210ee1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php @@ -0,0 +1,70 @@ +cookieJar = $cookieJar ?: new ArrayCookieJar(); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', 125), + 'request.sent' => array('onRequestSent', 125) + ); + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Add cookies before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + if (!$request->getParams()->get('cookies.disable')) { + $request->removeHeader('Cookie'); + // Find cookies that match this request + foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) { + $request->addCookie($cookie->getName(), $cookie->getValue()); + } + } + } + + /** + * Extract cookies from a sent request + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php new file mode 100755 index 000000000..b1fa6fd89 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cookie": "" } + }, + "target-dir": "Guzzle/Plugin/Cookie", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php new file mode 100755 index 000000000..610e60cad --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php @@ -0,0 +1,46 @@ +getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest'); + */ +class CurlAuthPlugin implements EventSubscriberInterface +{ + private $username; + private $password; + private $scheme; + + /** + * @param string $username HTTP basic auth username + * @param string $password Password + * @param int $scheme Curl auth scheme + */ + public function __construct($username, $password, $scheme=CURLAUTH_BASIC) + { + Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');"); + $this->username = $username; + $this->password = $password; + $this->scheme = $scheme; + } + + public static function getSubscribedEvents() + { + return array('client.create_request' => array('onRequestCreate', 255)); + } + + /** + * Add basic auth + * + * @param Event $event + */ + public function onRequestCreate(Event $event) + { + $event['request']->setAuth($this->username, $this->password, $this->scheme); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json new file mode 100755 index 000000000..edc8b24e5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-curlauth", + "description": "Guzzle cURL authorization plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "curl", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" } + }, + "target-dir": "Guzzle/Plugin/CurlAuth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php new file mode 100755 index 000000000..5dce8bd6c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php @@ -0,0 +1,22 @@ + array('onCommandBeforeSend', -1)); + } + + /** + * Adds a listener to requests before they sent from a command + * + * @param Event $event Event emitted + */ + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + if ($operation = $command->getOperation()) { + if ($operation->getErrorResponses()) { + $request = $command->getRequest(); + $request->getEventDispatcher() + ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation)); + } + } + } + + /** + * @param RequestInterface $request Request that received an error + * @param CommandInterface $command Command that created the request + * @param Operation $operation Operation that defines the request and errors + * + * @return \Closure Returns a closure + * @throws ErrorResponseException + */ + protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation) + { + return function (Event $event) use ($request, $command, $operation) { + $response = $event['response']; + foreach ($operation->getErrorResponses() as $error) { + if (!isset($error['class'])) { + continue; + } + if (isset($error['code']) && $response->getStatusCode() != $error['code']) { + continue; + } + if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) { + continue; + } + $className = $error['class']; + $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface'; + if (!class_exists($className)) { + throw new ErrorResponseException("{$className} does not exist"); + } elseif (!(in_array($errorClassInterface, class_implements($className)))) { + throw new ErrorResponseException("{$className} must implement {$errorClassInterface}"); + } + throw $className::fromCommand($command, $response); + } + }; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php new file mode 100755 index 000000000..1d89e40e7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/service": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" } + }, + "target-dir": "Guzzle/Plugin/ErrorResponse", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php new file mode 100755 index 000000000..7375e892b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php @@ -0,0 +1,163 @@ + array('onRequestSent', 9999)); + } + + /** + * Convert to a string that contains all request and response headers + * + * @return string + */ + public function __toString() + { + $lines = array(); + foreach ($this->transactions as $entry) { + $response = isset($entry['response']) ? $entry['response'] : ''; + $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n"; + } + + return implode("\n", $lines); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * @param Response $response Response of the request + * + * @return HistoryPlugin + */ + public function add(RequestInterface $request, Response $response = null) + { + if (!$response && $request->getResponse()) { + $response = $request->getResponse(); + } + + $this->transactions[] = array('request' => $request, 'response' => $response); + if (count($this->transactions) > $this->getlimit()) { + array_shift($this->transactions); + } + + return $this; + } + + /** + * Set the max number of requests to store + * + * @param int $limit Limit + * + * @return HistoryPlugin + */ + public function setLimit($limit) + { + $this->limit = (int) $limit; + + return $this; + } + + /** + * Get the request limit + * + * @return int + */ + public function getLimit() + { + return $this->limit; + } + + /** + * Get all of the raw transactions in the form of an array of associative arrays containing + * 'request' and 'response' keys. + * + * @return array + */ + public function getAll() + { + return $this->transactions; + } + + /** + * Get the requests in the history + * + * @return \ArrayIterator + */ + public function getIterator() + { + // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll()) + return new \ArrayIterator(array_map(function ($entry) { + $entry['request']->getParams()->set('actual_response', $entry['response']); + return $entry['request']; + }, $this->transactions)); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->transactions); + } + + /** + * Get the last request sent + * + * @return RequestInterface + */ + public function getLastRequest() + { + $last = end($this->transactions); + + return $last['request']; + } + + /** + * Get the last response in the history + * + * @return Response|null + */ + public function getLastResponse() + { + $last = end($this->transactions); + + return isset($last['response']) ? $last['response'] : null; + } + + /** + * Clears the history + * + * @return HistoryPlugin + */ + public function clear() + { + $this->transactions = array(); + + return $this; + } + + public function onRequestSent(Event $event) + { + $this->add($event['request'], $event['response']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json new file mode 100755 index 000000000..ba0bf2c4d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-history", + "description": "Guzzle history plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\History": "" } + }, + "target-dir": "Guzzle/Plugin/History", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php new file mode 100755 index 000000000..cabdea854 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php @@ -0,0 +1,161 @@ +logAdapter = $logAdapter; + $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter); + $this->wireBodies = $wireBodies; + } + + /** + * Get a log plugin that outputs full request, response, and curl error information to stderr + * + * @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable + * @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available + * + * @return self + */ + public static function getDebugPlugin($wireBodies = true, $stream = null) + { + if ($stream === null) { + if (defined('STDERR')) { + $stream = STDERR; + } else { + $stream = fopen('php://output', 'w'); + } + } + + return new self(new ClosureLogAdapter(function ($m) use ($stream) { + fwrite($stream, $m . PHP_EOL); + }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies); + } + + public static function getSubscribedEvents() + { + return array( + 'curl.callback.write' => array('onCurlWrite', 255), + 'curl.callback.read' => array('onCurlRead', 255), + 'request.before_send' => array('onRequestBeforeSend', 255), + 'request.sent' => array('onRequestSent', 255) + ); + } + + /** + * Event triggered when curl data is read from a request + * + * @param Event $event + */ + public function onCurlRead(Event $event) + { + // Stream the request body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('request_wire')) { + $wire->write($event['read']); + } + } + + /** + * Event triggered when curl data is written to a response + * + * @param Event $event + */ + public function onCurlWrite(Event $event) + { + // Stream the response body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('response_wire')) { + $wire->write($event['write']); + } + } + + /** + * Called before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + if ($this->wireBodies) { + $request = $event['request']; + // Ensure that curl IO events are emitted + $request->getCurlOptions()->set('emit_io', true); + // We need to make special handling for content wiring and non-repeatable streams. + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable()) + ) { + // The body of the request cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('request_wire', EntityBody::factory()); + } + if (!$request->getResponseBody()->isRepeatable()) { + // The body of the response cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('response_wire', EntityBody::factory()); + } + } + } + + /** + * Triggers the actual log write when a request completes + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $handle = $event['handle']; + + if ($wire = $request->getParams()->get('request_wire')) { + $request = clone $request; + $request->setBody($wire); + } + + if ($wire = $request->getParams()->get('response_wire')) { + $response = clone $response; + $response->setBody($wire); + } + + // Send the log message to the adapter, adding a category and host + $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG; + $message = $this->formatter->format($request, $response, $handle); + $this->logAdapter->log($message, $priority, array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json new file mode 100755 index 000000000..130e6da0a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-log", + "description": "Guzzle log plugin for over the wire logging", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "log", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Log": "" } + }, + "target-dir": "Guzzle/Plugin/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php new file mode 100755 index 000000000..851242433 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php @@ -0,0 +1,57 @@ +contentMd5Param = $contentMd5Param; + $this->validateMd5Param = $validateMd5Param; + } + + public static function getSubscribedEvents() + { + return array('command.before_send' => array('onCommandBeforeSend', -255)); + } + + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + $request = $command->getRequest(); + + // Only add an MD5 is there is a MD5 option on the operation and it has a payload + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && $command->getOperation()->hasParam($this->contentMd5Param)) { + // Check if an MD5 checksum value should be passed along to the request + if ($command[$this->contentMd5Param] === true) { + if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) { + $request->setHeader('Content-MD5', $md5); + } + } + } + + // Check if MD5 validation should be used with the response + if ($command[$this->validateMd5Param] === true) { + $request->addSubscriber(new Md5ValidatorPlugin(true, false)); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php new file mode 100755 index 000000000..5d7a3785e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php @@ -0,0 +1,88 @@ +contentLengthCutoff = $contentLengthCutoff; + $this->contentEncoded = $contentEncoded; + } + + public static function getSubscribedEvents() + { + return array('request.complete' => array('onRequestComplete', 255)); + } + + /** + * {@inheritdoc} + * @throws UnexpectedValueException + */ + public function onRequestComplete(Event $event) + { + $response = $event['response']; + + if (!$contentMd5 = $response->getContentMd5()) { + return; + } + + $contentEncoding = $response->getContentEncoding(); + if ($contentEncoding && !$this->contentEncoded) { + return false; + } + + // Make sure that the size of the request is under the cutoff size + if ($this->contentLengthCutoff) { + $size = $response->getContentLength() ?: $response->getBody()->getSize(); + if (!$size || $size > $this->contentLengthCutoff) { + return; + } + } + + if (!$contentEncoding) { + $hash = $response->getBody()->getContentMd5(); + } elseif ($contentEncoding == 'gzip') { + $response->getBody()->compress('zlib.deflate'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } elseif ($contentEncoding == 'compress') { + $response->getBody()->compress('bzip2.compress'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } else { + return; + } + + if ($contentMd5 !== $hash) { + throw new UnexpectedValueException( + "The response entity body may have been modified over the wire. The Content-MD5 " + . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})." + ); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json new file mode 100755 index 000000000..0602d0609 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-md5", + "description": "Guzzle MD5 plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Md5": "" } + }, + "target-dir": "Guzzle/Plugin/Md5", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php new file mode 100755 index 000000000..2440578cf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php @@ -0,0 +1,245 @@ +readBodies = $readBodies; + $this->temporary = $temporary; + if ($items) { + foreach ($items as $item) { + if ($item instanceof \Exception) { + $this->addException($item); + } else { + $this->addResponse($item); + } + } + } + } + + public static function getSubscribedEvents() + { + // Use a number lower than the CachePlugin + return array('request.before_send' => array('onRequestBeforeSend', -999)); + } + + public static function getAllEvents() + { + return array('mock.request'); + } + + /** + * Get a mock response from a file + * + * @param string $path File to retrieve a mock response from + * + * @return Response + * @throws InvalidArgumentException if the file is not found + */ + public static function getMockFile($path) + { + if (!file_exists($path)) { + throw new InvalidArgumentException('Unable to open mock file: ' . $path); + } + + return Response::fromMessage(file_get_contents($path)); + } + + /** + * Set whether or not to consume the entity body of a request when a mock + * response is used + * + * @param bool $readBodies Set to true to read and consume entity bodies + * + * @return self + */ + public function readBodies($readBodies) + { + $this->readBodies = $readBodies; + + return $this; + } + + /** + * Returns the number of remaining mock responses + * + * @return int + */ + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|Response $response Response object or path to response file + * + * @return MockPlugin + * @throws InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (!($response instanceof Response)) { + if (!is_string($response)) { + throw new InvalidArgumentException('Invalid response'); + } + $response = self::getMockFile($response); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Add an exception to the end of the queue + * + * @param CurlException $e Exception to throw when the request is executed + * + * @return MockPlugin + */ + public function addException(CurlException $e) + { + $this->queue[] = $e; + + return $this; + } + + /** + * Clear the queue + * + * @return MockPlugin + */ + public function clearQueue() + { + $this->queue = array(); + + return $this; + } + + /** + * Returns an array of mock responses remaining in the queue + * + * @return array + */ + public function getQueue() + { + return $this->queue; + } + + /** + * Check if this is a temporary plugin + * + * @return bool + */ + public function isTemporary() + { + return $this->temporary; + } + + /** + * Get a response from the front of the list and add it to a request + * + * @param RequestInterface $request Request to mock + * + * @return self + * @throws CurlException When request.send is called and an exception is queued + */ + public function dequeue(RequestInterface $request) + { + $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request)); + + $item = array_shift($this->queue); + if ($item instanceof Response) { + if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) { + $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) { + while ($data = $event['request']->getBody()->read(8096)); + // Remove the listener after one-time use + $event['request']->getEventDispatcher()->removeListener('request.sent', $f); + }); + } + $request->setResponse($item); + } elseif ($item instanceof CurlException) { + // Emulates exceptions encountered while transferring requests + $item->setRequest($request); + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item)); + // Only throw if the exception wasn't handled + if ($state == RequestInterface::STATE_ERROR) { + throw $item; + } + } + + return $this; + } + + /** + * Clear the array of received requests + */ + public function flush() + { + $this->received = array(); + } + + /** + * Get an array of requests that were mocked by this plugin + * + * @return array + */ + public function getReceivedRequests() + { + return $this->received; + } + + /** + * Called when a request is about to be sent + * + * @param Event $event + * @throws \OutOfBoundsException When queue is empty + */ + public function onRequestBeforeSend(Event $event) + { + if (!$this->queue) { + throw new \OutOfBoundsException('Mock queue is empty'); + } + + $request = $event['request']; + $this->received[] = $request; + // Detach the filter from the client so it's a one-time use + if ($this->temporary && count($this->queue) == 1 && $request->getClient()) { + $request->getClient()->getEventDispatcher()->removeSubscriber($this); + } + $this->dequeue($request); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json new file mode 100755 index 000000000..f8201e31f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-mock", + "description": "Guzzle Mock plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["mock", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Mock": "" } + }, + "target-dir": "Guzzle/Plugin/Mock", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php new file mode 100755 index 000000000..95e0c3e4a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php @@ -0,0 +1,306 @@ +config = Collection::fromConfig($config, array( + 'version' => '1.0', + 'request_method' => self::REQUEST_METHOD_HEADER, + 'consumer_key' => 'anonymous', + 'consumer_secret' => 'anonymous', + 'signature_method' => 'HMAC-SHA1', + 'signature_callback' => function($stringToSign, $key) { + return hash_hmac('sha1', $stringToSign, $key, true); + } + ), array( + 'signature_method', 'signature_callback', 'version', + 'consumer_key', 'consumer_secret' + )); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -1000) + ); + } + + /** + * Request before-send event handler + * + * @param Event $event Event received + * @return array + * @throws \InvalidArgumentException + */ + public function onRequestBeforeSend(Event $event) + { + $timestamp = $this->getTimestamp($event); + $request = $event['request']; + $nonce = $this->generateNonce($request); + $authorizationParams = $this->getOauthParams($timestamp, $nonce); + $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce); + + switch ($this->config['request_method']) { + case self::REQUEST_METHOD_HEADER: + $request->setHeader( + 'Authorization', + $this->buildAuthorizationHeader($authorizationParams) + ); + break; + case self::REQUEST_METHOD_QUERY: + foreach ($authorizationParams as $key => $value) { + $request->getQuery()->set($key, $value); + } + break; + default: + throw new \InvalidArgumentException(sprintf( + 'Invalid consumer method "%s"', + $this->config['request_method'] + )); + } + + return $authorizationParams; + } + + /** + * Builds the Authorization header for a request + * + * @param array $authorizationParams Associative array of authorization parameters + * + * @return string + */ + private function buildAuthorizationHeader($authorizationParams) + { + $authorizationString = 'OAuth '; + foreach ($authorizationParams as $key => $val) { + if ($val) { + $authorizationString .= $key . '="' . urlencode($val) . '", '; + } + } + + return substr($authorizationString, 0, -2); + } + + /** + * Calculate signature for request + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getSignature(RequestInterface $request, $timestamp, $nonce) + { + $string = $this->getStringToSign($request, $timestamp, $nonce); + $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']); + + return base64_encode(call_user_func($this->config['signature_callback'], $string, $key)); + } + + /** + * Calculate string to sign + * + * @param RequestInterface $request Request to generate a signature for + * @param int $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getStringToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getParamsToSign($request, $timestamp, $nonce); + + // Convert booleans to strings. + $params = $this->prepareParameters($params); + + // Build signing string from combined params + $parameterString = clone $request->getQuery(); + $parameterString->replace($params); + + $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null); + + return strtoupper($request->getMethod()) . '&' + . rawurlencode($url) . '&' + . rawurlencode((string) $parameterString); + } + + /** + * Get the oauth parameters as named by the oauth spec + * + * @param $timestamp + * @param $nonce + * @return Collection + */ + protected function getOauthParams($timestamp, $nonce) + { + $params = new Collection(array( + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_nonce' => $nonce, + 'oauth_signature_method' => $this->config['signature_method'], + 'oauth_timestamp' => $timestamp, + )); + + // Optional parameters should not be set if they have not been set in the config as + // the parameter may be considered invalid by the Oauth service. + $optionalParams = array( + 'callback' => 'oauth_callback', + 'token' => 'oauth_token', + 'verifier' => 'oauth_verifier', + 'version' => 'oauth_version' + ); + + foreach ($optionalParams as $optionName => $oauthName) { + if (isset($this->config[$optionName]) == true) { + $params[$oauthName] = $this->config[$optionName]; + } + } + + return $params; + } + + /** + * Get all of the parameters required to sign a request including: + * * The oauth params + * * The request GET params + * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded) + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return array + */ + public function getParamsToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getOauthParams($timestamp, $nonce); + + // Add query string parameters + $params->merge($request->getQuery()); + + // Add POST fields to signing string if required + if ($this->shouldPostFieldsBeSigned($request)) + { + $params->merge($request->getPostFields()); + } + + // Sort params + $params = $params->toArray(); + uksort($params, 'strcmp'); + + return $params; + } + + /** + * Decide whether the post fields should be added to the base string that Oauth signs. + * This implementation is correct. Non-conformant APIs may require that this method be + * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type + * is 'application/x-www-form-urlencoded' + * + * @param $request + * @return bool Whether the post fields should be signed or not + */ + public function shouldPostFieldsBeSigned($request) + { + if (!$this->config->get('disable_post_params') && + $request instanceof EntityEnclosingRequestInterface && + false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) + { + return true; + } + + return false; + } + + /** + * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same + * exact timestamp to use separate nonce's. + * + * @param RequestInterface $request Request to generate a nonce for + * + * @return string + */ + public function generateNonce(RequestInterface $request) + { + return sha1(uniqid('', true) . $request->getUrl()); + } + + /** + * Gets timestamp from event or create new timestamp + * + * @param Event $event Event containing contextual information + * + * @return int + */ + public function getTimestamp(Event $event) + { + return $event['timestamp'] ?: time(); + } + + /** + * Convert booleans to strings, removed unset parameters, and sorts the array + * + * @param array $data Data array + * + * @return array + */ + protected function prepareParameters($data) + { + ksort($data); + foreach ($data as $key => &$value) { + switch (gettype($value)) { + case 'NULL': + unset($data[$key]); + break; + case 'array': + $data[$key] = self::prepareParameters($value); + break; + case 'boolean': + $data[$key] = $value ? 'true' : 'false'; + break; + } + } + + return $data; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json new file mode 100755 index 000000000..c9766ba16 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-oauth", + "description": "Guzzle OAuth plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["oauth", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Oauth": "" } + }, + "target-dir": "Guzzle/Plugin/Oauth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json new file mode 100755 index 000000000..2bbe64cc5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json @@ -0,0 +1,44 @@ +{ + "name": "guzzle/plugin", + "description": "Guzzle plugin component containing all Guzzle HTTP plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["http", "client", "plugin", "extension", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "suggest": { + "guzzle/cache": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin": "" } + }, + "target-dir": "Guzzle/Plugin", + "replace": { + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version" + }, + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php new file mode 100755 index 000000000..cd06f5722 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php @@ -0,0 +1,177 @@ + 'JSON_ERROR_NONE - No errors', + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' + ); + + public function load($config, array $options = array()) + { + // Reset the array of loaded files because this is a new config + $this->loadedFiles = array(); + + if (is_string($config)) { + $config = $this->loadFile($config); + } elseif (!is_array($config)) { + throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config)); + } else { + $this->mergeIncludes($config); + } + + return $this->build($config, $options); + } + + /** + * Add an include alias to the loader + * + * @param string $filename Filename to alias (e.g. _foo) + * @param string $alias Actual file to use (e.g. /path/to/foo.json) + * + * @return self + */ + public function addAlias($filename, $alias) + { + $this->aliases[$filename] = $alias; + + return $this; + } + + /** + * Remove an alias from the loader + * + * @param string $alias Alias to remove + * + * @return self + */ + public function removeAlias($alias) + { + unset($this->aliases[$alias]); + + return $this; + } + + /** + * Perform the parsing of a config file and create the end result + * + * @param array $config Configuration data + * @param array $options Options to use when building + * + * @return mixed + */ + protected abstract function build($config, array $options); + + /** + * Load a configuration file (can load JSON or PHP files that return an array when included) + * + * @param string $filename File to load + * + * @return array + * @throws InvalidArgumentException + * @throws RuntimeException when the JSON cannot be parsed + */ + protected function loadFile($filename) + { + if (isset($this->aliases[$filename])) { + $filename = $this->aliases[$filename]; + } + + switch (pathinfo($filename, PATHINFO_EXTENSION)) { + case 'js': + case 'json': + $level = error_reporting(0); + $json = file_get_contents($filename); + error_reporting($level); + + if ($json === false) { + $err = error_get_last(); + throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']); + } + + $config = json_decode($json, true); + // Throw an exception if there was an error loading the file + if ($error = json_last_error()) { + $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error'; + throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}"); + } + break; + case 'php': + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + $config = require $filename; + if (!is_array($config)) { + throw new InvalidArgumentException('PHP files must return an array of configuration data'); + } + break; + default: + throw new InvalidArgumentException('Unknown file extension: ' . $filename); + } + + // Keep track of this file being loaded to prevent infinite recursion + $this->loadedFiles[$filename] = true; + + // Merge include files into the configuration array + $this->mergeIncludes($config, dirname($filename)); + + return $config; + } + + /** + * Merges in all include files + * + * @param array $config Config data that contains includes + * @param string $basePath Base path to use when a relative path is encountered + * + * @return array Returns the merged and included data + */ + protected function mergeIncludes(&$config, $basePath = null) + { + if (!empty($config['includes'])) { + foreach ($config['includes'] as &$path) { + // Account for relative paths + if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) { + $path = "{$basePath}/{$path}"; + } + // Don't load the same files more than once + if (!isset($this->loadedFiles[$path])) { + $this->loadedFiles[$path] = true; + $config = $this->mergeData($this->loadFile($path), $config); + } + } + } + } + + /** + * Default implementation for merging two arrays of data (uses array_merge_recursive) + * + * @param array $a Original data + * @param array $b Data to merge into the original and overwrite existing values + * + * @return array + */ + protected function mergeData(array $a, array $b) + { + return array_merge_recursive($a, $b); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php new file mode 100755 index 000000000..38150db4b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php @@ -0,0 +1,189 @@ +load($config, $globalParameters); + } + + /** + * @param array $serviceBuilderConfig Service configuration settings: + * - name: Name of the service + * - class: Client class to instantiate using a factory method + * - params: array of key value pair configuration settings for the builder + */ + public function __construct(array $serviceBuilderConfig = array()) + { + $this->builderConfig = $serviceBuilderConfig; + } + + public static function getAllEvents() + { + return array('service_builder.create_client'); + } + + public function unserialize($serialized) + { + $this->builderConfig = json_decode($serialized, true); + } + + public function serialize() + { + return json_encode($this->builderConfig); + } + + /** + * Attach a plugin to every client created by the builder + * + * @param EventSubscriberInterface $plugin Plugin to attach to each client + * + * @return self + */ + public function addGlobalPlugin(EventSubscriberInterface $plugin) + { + $this->plugins[] = $plugin; + + return $this; + } + + /** + * Get data from the service builder without triggering the building of a service + * + * @param string $name Name of the service to retrieve + * + * @return array|null + */ + public function getData($name) + { + return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null; + } + + public function get($name, $throwAway = false) + { + if (!isset($this->builderConfig[$name])) { + + // Check to see if arbitrary data is being referenced + if (isset($this->clients[$name])) { + return $this->clients[$name]; + } + + // Check aliases and return a match if found + foreach ($this->builderConfig as $actualName => $config) { + if (isset($config['alias']) && $config['alias'] == $name) { + return $this->get($actualName, $throwAway); + } + } + throw new ServiceNotFoundException('No service is registered as ' . $name); + } + + if (!$throwAway && isset($this->clients[$name])) { + return $this->clients[$name]; + } + + $builder =& $this->builderConfig[$name]; + + // Convert references to the actual client + foreach ($builder['params'] as &$v) { + if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') { + $v = $this->get(trim($v, '{} ')); + } + } + + // Get the configured parameters and merge in any parameters provided for throw-away clients + $config = $builder['params']; + if (is_array($throwAway)) { + $config = $throwAway + $config; + } + + $client = $builder['class']::factory($config); + + if (!$throwAway) { + $this->clients[$name] = $client; + } + + if ($client instanceof ClientInterface) { + foreach ($this->plugins as $plugin) { + $client->addSubscriber($plugin); + } + // Dispatch an event letting listeners know a client was created + $this->dispatch('service_builder.create_client', array('client' => $client)); + } + + return $client; + } + + public function set($key, $service) + { + if (is_array($service) && isset($service['class']) && isset($service['params'])) { + $this->builderConfig[$key] = $service; + } else { + $this->clients[$key] = $service; + } + + return $this; + } + + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + public function offsetUnset($offset) + { + unset($this->builderConfig[$offset]); + unset($this->clients[$offset]); + } + + public function offsetExists($offset) + { + return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]); + } + + public function offsetGet($offset) + { + return $this->get($offset); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php new file mode 100755 index 000000000..4fc310a47 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php @@ -0,0 +1,40 @@ + &$service) { + + $service['params'] = isset($service['params']) ? $service['params'] : array(); + + // Check if this client builder extends another client + if (!empty($service['extends'])) { + + // Make sure that the service it's extending has been defined + if (!isset($services[$service['extends']])) { + throw new ServiceNotFoundException( + "{$name} is trying to extend a non-existent service: {$service['extends']}" + ); + } + + $extended = &$services[$service['extends']]; + + // Use the correct class attribute + if (empty($service['class'])) { + $service['class'] = isset($extended['class']) ? $extended['class'] : ''; + } + if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) { + $service['params'] = $service['params'] + $extendsParams; + } + } + + // Overwrite default values with global parameter values + if (!empty($options)) { + $service['params'] = $options + $service['params']; + } + + $service['class'] = isset($service['class']) ? $service['class'] : ''; + } + + return new $class($services); + } + + protected function mergeData(array $a, array $b) + { + $result = $b + $a; + + // Merge services using a recursive union of arrays + if (isset($a['services']) && $b['services']) { + + // Get a union of the services of the two arrays + $result['services'] = $b['services'] + $a['services']; + + // Merge each service in using a union of the two arrays + foreach ($result['services'] as $name => &$service) { + + // By default, services completely override a previously defined service unless it extends itself + if (isset($a['services'][$name]['extends']) + && isset($b['services'][$name]['extends']) + && $b['services'][$name]['extends'] == $name + ) { + $service += $a['services'][$name]; + // Use the `extends` attribute of the parent + $service['extends'] = $a['services'][$name]['extends']; + // Merge parameters using a union if both have parameters + if (isset($a['services'][$name]['params'])) { + $service['params'] += $a['services'][$name]['params']; + } + } + } + } + + return $result; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php new file mode 100755 index 000000000..26f8360cc --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php @@ -0,0 +1,46 @@ +loader = $loader; + $this->cache = $cache; + } + + public function load($config, array $options = array()) + { + if (!is_string($config)) { + $key = false; + } else { + $key = 'loader_' . crc32($config); + if ($result = $this->cache->fetch($key)) { + return $result; + } + } + + $result = $this->loader->load($config, $options); + if ($key) { + $this->cache->save($key, $result); + } + + return $result; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php new file mode 100755 index 000000000..3e5f8e53d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php @@ -0,0 +1,297 @@ +getCommand($method, isset($args[0]) ? $args[0] : array())->getResult(); + } + + public function getCommand($name, array $args = array()) + { + // Add global client options to the command + if ($options = $this->getConfig(self::COMMAND_PARAMS)) { + $args += $options; + } + + if (!($command = $this->getCommandFactory()->factory($name, $args))) { + throw new InvalidArgumentException("Command was not found matching {$name}"); + } + + $command->setClient($this); + $this->dispatch('client.command.create', array('client' => $this, 'command' => $command)); + + return $command; + } + + /** + * Set the command factory used to create commands by name + * + * @param CommandFactoryInterface $factory Command factory + * + * @return self + */ + public function setCommandFactory(CommandFactoryInterface $factory) + { + $this->commandFactory = $factory; + + return $this; + } + + /** + * Set the resource iterator factory associated with the client + * + * @param ResourceIteratorFactoryInterface $factory Resource iterator factory + * + * @return self + */ + public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory) + { + $this->resourceIteratorFactory = $factory; + + return $this; + } + + public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array()) + { + if (!($command instanceof CommandInterface)) { + $command = $this->getCommand($command, $commandOptions ?: array()); + } + + return $this->getResourceIteratorFactory()->build($command, $iteratorOptions); + } + + public function execute($command) + { + if ($command instanceof CommandInterface) { + $this->send($this->prepareCommand($command)); + $this->dispatch('command.after_send', array('command' => $command)); + return $command->getResult(); + } elseif (is_array($command) || $command instanceof \Traversable) { + return $this->executeMultiple($command); + } else { + throw new InvalidArgumentException('Command must be a command or array of commands'); + } + } + + public function setDescription(ServiceDescriptionInterface $service) + { + $this->serviceDescription = $service; + + if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) { + $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service)); + } + + // If a baseUrl was set on the description, then update the client + if ($baseUrl = $service->getBaseUrl()) { + $this->setBaseUrl($baseUrl); + } + + return $this; + } + + public function getDescription() + { + return $this->serviceDescription; + } + + /** + * Set the inflector used with the client + * + * @param InflectorInterface $inflector Inflection object + * + * @return self + */ + public function setInflector(InflectorInterface $inflector) + { + $this->inflector = $inflector; + + return $this; + } + + /** + * Get the inflector used with the client + * + * @return self + */ + public function getInflector() + { + if (!$this->inflector) { + $this->inflector = Inflector::getDefault(); + } + + return $this->inflector; + } + + /** + * Prepare a command for sending and get the RequestInterface object created by the command + * + * @param CommandInterface $command Command to prepare + * + * @return RequestInterface + */ + protected function prepareCommand(CommandInterface $command) + { + // Set the client and prepare the command + $request = $command->setClient($this)->prepare(); + // Set the state to new if the command was previously executed + $request->setState(RequestInterface::STATE_NEW); + $this->dispatch('command.before_send', array('command' => $command)); + + return $request; + } + + /** + * Execute multiple commands in parallel + * + * @param array|Traversable $commands Array of CommandInterface objects to execute + * + * @return array Returns an array of the executed commands + * @throws Exception\CommandTransferException + */ + protected function executeMultiple($commands) + { + $requests = array(); + $commandRequests = new \SplObjectStorage(); + + foreach ($commands as $command) { + $request = $this->prepareCommand($command); + $commandRequests[$request] = $command; + $requests[] = $request; + } + + try { + $this->send($requests); + foreach ($commands as $command) { + $this->dispatch('command.after_send', array('command' => $command)); + } + return $commands; + } catch (MultiTransferException $failureException) { + // Throw a CommandTransferException using the successful and failed commands + $e = CommandTransferException::fromMultiTransferException($failureException); + + // Remove failed requests from the successful requests array and add to the failures array + foreach ($failureException->getFailedRequests() as $request) { + if (isset($commandRequests[$request])) { + $e->addFailedCommand($commandRequests[$request]); + unset($commandRequests[$request]); + } + } + + // Always emit the command after_send events for successful commands + foreach ($commandRequests as $success) { + $e->addSuccessfulCommand($commandRequests[$success]); + $this->dispatch('command.after_send', array('command' => $commandRequests[$success])); + } + + throw $e; + } + } + + protected function getResourceIteratorFactory() + { + if (!$this->resourceIteratorFactory) { + // Build the default resource iterator factory if one is not set + $clientClass = get_class($this); + $prefix = substr($clientClass, 0, strrpos($clientClass, '\\')); + $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array( + "{$prefix}\\Iterator", + "{$prefix}\\Model" + )); + } + + return $this->resourceIteratorFactory; + } + + /** + * Get the command factory associated with the client + * + * @return CommandFactoryInterface + */ + protected function getCommandFactory() + { + if (!$this->commandFactory) { + $this->commandFactory = CompositeFactory::getDefaultChain($this); + } + + return $this->commandFactory; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function enableMagicMethods($isEnabled) + { + Version::warn(__METHOD__ . ' is deprecated'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php new file mode 100755 index 000000000..814154f00 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php @@ -0,0 +1,68 @@ +operation = $operation ?: $this->createOperation(); + foreach ($this->operation->getParams() as $name => $arg) { + $currentValue = $this[$name]; + $configValue = $arg->getValue($currentValue); + // If default or static values are set, then this should always be updated on the config object + if ($currentValue !== $configValue) { + $this[$name] = $configValue; + } + } + + $headers = $this[self::HEADERS_OPTION]; + if (!$headers instanceof Collection) { + $this[self::HEADERS_OPTION] = new Collection((array) $headers); + } + + // You can set a command.on_complete option in your parameters to set an onComplete callback + if ($onComplete = $this['command.on_complete']) { + unset($this['command.on_complete']); + $this->setOnComplete($onComplete); + } + + // Set the hidden additional parameters + if (!$this[self::HIDDEN_PARAMS]) { + $this[self::HIDDEN_PARAMS] = array( + self::HEADERS_OPTION, + self::RESPONSE_PROCESSING, + self::HIDDEN_PARAMS, + self::REQUEST_OPTIONS + ); + } + + $this->init(); + } + + /** + * Custom clone behavior + */ + public function __clone() + { + $this->request = null; + $this->result = null; + } + + /** + * Execute the command in the same manner as calling a function + * + * @return mixed Returns the result of {@see AbstractCommand::execute} + */ + public function __invoke() + { + return $this->execute(); + } + + public function getName() + { + return $this->operation->getName(); + } + + /** + * Get the API command information about the command + * + * @return OperationInterface + */ + public function getOperation() + { + return $this->operation; + } + + public function setOnComplete($callable) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException('The onComplete function must be callable'); + } + + $this->onComplete = $callable; + + return $this; + } + + public function execute() + { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be executed.'); + } + + return $this->client->execute($this); + } + + public function getClient() + { + return $this->client; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getRequest() + { + if (!$this->request) { + throw new CommandException('The command must be prepared before retrieving the request'); + } + + return $this->request; + } + + public function getResponse() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + return $this->request->getResponse(); + } + + public function getResult() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + if (null === $this->result) { + $this->process(); + // Call the onComplete method if one is set + if ($this->onComplete) { + call_user_func($this->onComplete, $this); + } + } + + return $this->result; + } + + public function setResult($result) + { + $this->result = $result; + + return $this; + } + + public function isPrepared() + { + return $this->request !== null; + } + + public function isExecuted() + { + return $this->request !== null && $this->request->getState() == 'complete'; + } + + public function prepare() + { + if (!$this->isPrepared()) { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be prepared.'); + } + + // If no response processing value was specified, then attempt to use the highest level of processing + if (!isset($this[self::RESPONSE_PROCESSING])) { + $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL; + } + + // Notify subscribers of the client that the command is being prepared + $this->client->dispatch('command.before_prepare', array('command' => $this)); + + // Fail on missing required arguments, and change parameters via filters + $this->validate(); + // Delegate to the subclass that implements the build method + $this->build(); + + // Add custom request headers set on the command + if ($headers = $this[self::HEADERS_OPTION]) { + foreach ($headers as $key => $value) { + $this->request->setHeader($key, $value); + } + } + + // Add any curl options to the request + if ($options = $this[Client::CURL_OPTIONS]) { + $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options)); + } + + // Set a custom response body + if ($responseBody = $this[self::RESPONSE_BODY]) { + $this->request->setResponseBody($responseBody); + } + + $this->client->dispatch('command.after_prepare', array('command' => $this)); + } + + return $this->request; + } + + /** + * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is + * set, then the command will validate using the default {@see SchemaValidator}. + * + * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema + * + * @return self + */ + public function setValidator(ValidatorInterface $validator) + { + $this->validator = $validator; + + return $this; + } + + public function getRequestHeaders() + { + return $this[self::HEADERS_OPTION]; + } + + /** + * Initialize the command (hook that can be implemented in subclasses) + */ + protected function init() {} + + /** + * Create the request object that will carry out the command + */ + abstract protected function build(); + + /** + * Hook used to create an operation for concrete commands that are not associated with a service description + * + * @return OperationInterface + */ + protected function createOperation() + { + return new Operation(array('name' => get_class($this))); + } + + /** + * Create the result of the command after the request has been completed. + * Override this method in subclasses to customize this behavior + */ + protected function process() + { + $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW + ? DefaultResponseParser::getInstance()->parse($this) + : $this->request->getResponse(); + } + + /** + * Validate and prepare the command based on the schema and rules defined by the command's Operation object + * + * @throws ValidationException when validation errors occur + */ + protected function validate() + { + // Do not perform request validation/transformation if it is disable + if ($this[self::DISABLE_VALIDATION]) { + return; + } + + $errors = array(); + $validator = $this->getValidator(); + foreach ($this->operation->getParams() as $name => $schema) { + $value = $this[$name]; + if (!$validator->validate($schema, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + // Update the config value if it changed and no validation errors were encountered + $this->data[$name] = $value; + } + } + + // Validate additional parameters + $hidden = $this[self::HIDDEN_PARAMS]; + + if ($properties = $this->operation->getAdditionalParameters()) { + foreach ($this->toArray() as $name => $value) { + // It's only additional if it isn't defined in the schema + if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) { + // Always set the name so that error messages are useful + $properties->setName($name); + if (!$validator->validate($properties, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + $this->data[$name] = $value; + } + } + } + } + + if (!empty($errors)) { + $e = new ValidationException('Validation errors: ' . implode("\n", $errors)); + $e->setErrors($errors); + throw $e; + } + } + + /** + * Get the validator used to prepare and validate properties. If no validator has been set on the command, then + * the default {@see SchemaValidator} will be used. + * + * @return ValidatorInterface + */ + protected function getValidator() + { + if (!$this->validator) { + $this->validator = SchemaValidator::getInstance(); + } + + return $this->validator; + } + + /** + * Get array of any validation errors + * If no validator has been set then return false + */ + public function getValidationErrors() + { + if (!$this->validator) { + return false; + } + + return $this->validator->getErrors(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php new file mode 100755 index 000000000..cb6ac40ce --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php @@ -0,0 +1,41 @@ +request = $closure($this, $this->operation); + + if (!$this->request || !$this->request instanceof RequestInterface) { + throw new UnexpectedValueException('Closure command did not return a RequestInterface object'); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php new file mode 100755 index 000000000..fbb61d2ff --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php @@ -0,0 +1,128 @@ +stopPropagation(); + } + + /** + * Get the created object + * + * @return mixed + */ + public function getResult() + { + return $this['result']; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php new file mode 100755 index 000000000..2dc4acd37 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php @@ -0,0 +1,169 @@ +factory = $factory; + } + + /** + * Add a location visitor to the serializer + * + * @param string $location Location to associate with the visitor + * @param RequestVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, RequestVisitorInterface $visitor) + { + $this->factory->addRequestVisitor($location, $visitor); + + return $this; + } + + public function prepare(CommandInterface $command) + { + $request = $this->createRequest($command); + // Keep an array of visitors found in the operation + $foundVisitors = array(); + $operation = $command->getOperation(); + + // Add arguments to the request using the location attribute + foreach ($operation->getParams() as $name => $arg) { + /** @var $arg \Guzzle\Service\Description\Parameter */ + $location = $arg->getLocation(); + // Skip 'uri' locations because they've already been processed + if ($location && $location != 'uri') { + // Instantiate visitors as they are detected in the properties + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getRequestVisitor($location); + } + // Ensure that a value has been set for this parameter + $value = $command[$name]; + if ($value !== null) { + // Apply the parameter value with the location visitor + $foundVisitors[$location]->visit($command, $request, $arg, $value); + } + } + } + + // Serialize additional parameters + if ($additional = $operation->getAdditionalParameters()) { + if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) { + $foundVisitors[$additional->getLocation()] = $visitor; + } + } + + // Call the after method on each visitor found in the operation + foreach ($foundVisitors as $visitor) { + $visitor->after($command, $request); + } + + return $request; + } + + /** + * Serialize additional parameters + * + * @param OperationInterface $operation Operation that owns the command + * @param CommandInterface $command Command to prepare + * @param RequestInterface $request Request to serialize + * @param Parameter $additional Additional parameters + * + * @return null|RequestVisitorInterface + */ + protected function prepareAdditionalParameters( + OperationInterface $operation, + CommandInterface $command, + RequestInterface $request, + Parameter $additional + ) { + if (!($location = $additional->getLocation())) { + return; + } + + $visitor = $this->factory->getRequestVisitor($location); + $hidden = $command[$command::HIDDEN_PARAMS]; + + foreach ($command->toArray() as $key => $value) { + // Ignore values that are null or built-in command options + if ($value !== null + && !in_array($key, $hidden) + && !$operation->hasParam($key) + ) { + $additional->setName($key); + $visitor->visit($command, $request, $additional, $value); + } + } + + return $visitor; + } + + /** + * Create a request for the command and operation + * + * @param CommandInterface $command Command to create a request for + * + * @return RequestInterface + */ + protected function createRequest(CommandInterface $command) + { + $operation = $command->getOperation(); + $client = $command->getClient(); + $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array(); + + // If the command does not specify a template, then assume the base URL of the client + if (!($uri = $operation->getUri())) { + return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options); + } + + // Get the path values and use the client config settings + $variables = array(); + foreach ($operation->getParams() as $name => $arg) { + if ($arg->getLocation() == 'uri') { + if (isset($command[$name])) { + $variables[$name] = $arg->filter($command[$name]); + if (!is_array($variables[$name])) { + $variables[$name] = (string) $variables[$name]; + } + } + } + } + + return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php new file mode 100755 index 000000000..4fe380376 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php @@ -0,0 +1,55 @@ +getRequest()->getResponse(); + + // Account for hard coded content-type values specified in service descriptions + if ($contentType = $command['command.expects']) { + $response->setHeader('Content-Type', $contentType); + } else { + $contentType = (string) $response->getHeader('Content-Type'); + } + + return $this->handleParsing($command, $response, $contentType); + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $result = $response; + if ($result->getBody()) { + if (stripos($contentType, 'json') !== false) { + $result = $result->json(); + } elseif (stripos($contentType, 'xml') !== false) { + $result = $result->xml(); + } + } + + return $result; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php new file mode 100755 index 000000000..1c5ce0741 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php @@ -0,0 +1,39 @@ +client = $client; + $this->aliases = $aliases; + } + + public function factory($name, array $args = array()) + { + if (isset($this->aliases[$name])) { + try { + return $this->client->getCommand($this->aliases[$name], $args); + } catch (InvalidArgumentException $e) { + return null; + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php new file mode 100755 index 000000000..8c46983d6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php @@ -0,0 +1,154 @@ +getDescription()) { + $factories[] = new ServiceDescriptionFactory($description); + } + $factories[] = new ConcreteClassFactory($client); + + return new self($factories); + } + + /** + * @param array $factories Array of command factories + */ + public function __construct(array $factories = array()) + { + $this->factories = $factories; + } + + /** + * Add a command factory to the chain + * + * @param FactoryInterface $factory Factory to add + * @param string|FactoryInterface $before Insert the new command factory before a command factory class or object + * matching a class name. + * @return CompositeFactory + */ + public function add(FactoryInterface $factory, $before = null) + { + $pos = null; + + if ($before) { + foreach ($this->factories as $i => $f) { + if ($before instanceof FactoryInterface) { + if ($f === $before) { + $pos = $i; + break; + } + } elseif (is_string($before)) { + if ($f instanceof $before) { + $pos = $i; + break; + } + } + } + } + + if ($pos === null) { + $this->factories[] = $factory; + } else { + array_splice($this->factories, $i, 0, array($factory)); + } + + return $this; + } + + /** + * Check if the chain contains a specific command factory + * + * @param FactoryInterface|string $factory Factory to check + * + * @return bool + */ + public function has($factory) + { + return (bool) $this->find($factory); + } + + /** + * Remove a specific command factory from the chain + * + * @param string|FactoryInterface $factory Factory to remove by name or instance + * + * @return CompositeFactory + */ + public function remove($factory = null) + { + if (!($factory instanceof FactoryInterface)) { + $factory = $this->find($factory); + } + + $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) { + return $f !== $factory; + })); + + return $this; + } + + /** + * Get a command factory by class name + * + * @param string|FactoryInterface $factory Command factory class or instance + * + * @return null|FactoryInterface + */ + public function find($factory) + { + foreach ($this->factories as $f) { + if ($factory === $f || (is_string($factory) && $f instanceof $factory)) { + return $f; + } + } + } + + /** + * Create a command using the associated command factories + * + * @param string $name Name of the command + * @param array $args Command arguments + * + * @return CommandInterface + */ + public function factory($name, array $args = array()) + { + foreach ($this->factories as $factory) { + $command = $factory->factory($name, $args); + if ($command) { + return $command; + } + } + } + + public function count() + { + return count($this->factories); + } + + public function getIterator() + { + return new \ArrayIterator($this->factories); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php new file mode 100755 index 000000000..0e93deaa0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php @@ -0,0 +1,47 @@ +client = $client; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + public function factory($name, array $args = array()) + { + // Determine the class to instantiate based on the namespace of the current client and the default directory + $prefix = $this->client->getConfig('command.prefix'); + if (!$prefix) { + // The prefix can be specified in a factory method and is cached + $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\'; + $this->client->getConfig()->set('command.prefix', $prefix); + } + + $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name)))); + + // Create the concrete command if it exists + if (class_exists($class)) { + return new $class($args); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php new file mode 100755 index 000000000..35c299d9d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php @@ -0,0 +1,21 @@ +map = $map; + } + + public function factory($name, array $args = array()) + { + if (isset($this->map[$name])) { + $class = $this->map[$name]; + + return new $class($args); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php new file mode 100755 index 000000000..b943a5b50 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php @@ -0,0 +1,71 @@ +setServiceDescription($description); + $this->inflector = $inflector; + } + + /** + * Change the service description used with the factory + * + * @param ServiceDescriptionInterface $description Service description to use + * + * @return FactoryInterface + */ + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the service description + * + * @return ServiceDescriptionInterface + */ + public function getServiceDescription() + { + return $this->description; + } + + public function factory($name, array $args = array()) + { + $command = $this->description->getOperation($name); + + // If a command wasn't found, then try to uppercase the first letter and try again + if (!$command) { + $command = $this->description->getOperation(ucfirst($name)); + // If an inflector was passed, then attempt to get the command using snake_case inflection + if (!$command && $this->inflector) { + $command = $this->description->getOperation($this->inflector->snake($name)); + } + } + + if ($command) { + $class = $command->getClass(); + return new $class($args, $command, $this->description); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php new file mode 100755 index 000000000..adcfca1ba --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php @@ -0,0 +1,69 @@ +resolveRecursively($value, $param) + : $param->filter($value); + } + + /** + * Map nested parameters into the location_key based parameters + * + * @param array $value Value to map + * @param Parameter $param Parameter that holds information about the current key + * + * @return array Returns the mapped array + */ + protected function resolveRecursively(array $value, Parameter $param) + { + foreach ($value as $name => &$v) { + switch ($param->getType()) { + case 'object': + if ($subParam = $param->getProperty($name)) { + $key = $subParam->getWireName(); + $value[$key] = $this->prepareValue($v, $subParam); + if ($name != $key) { + unset($value[$name]); + } + } elseif ($param->getAdditionalProperties() instanceof Parameter) { + $v = $this->prepareValue($v, $param->getAdditionalProperties()); + } + break; + case 'array': + if ($items = $param->getItems()) { + $v = $this->prepareValue($v, $items); + } + break; + } + } + + return $param->filter($value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php new file mode 100755 index 000000000..168d7806f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php @@ -0,0 +1,58 @@ +filter($value); + $entityBody = EntityBody::factory($value); + $request->setBody($entityBody); + $this->addExpectHeader($request, $entityBody, $param->getData('expect_header')); + // Add the Content-Encoding header if one is set on the EntityBody + if ($encoding = $entityBody->getContentEncoding()) { + $request->setHeader('Content-Encoding', $encoding); + } + } + + /** + * Add the appropriate expect header to a request + * + * @param EntityEnclosingRequestInterface $request Request to update + * @param EntityBodyInterface $body Entity body of the request + * @param string|int $expect Expect header setting + */ + protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect) + { + // Allow the `expect` data parameter to be set to remove the Expect header from the request + if ($expect === false) { + $request->removeHeader('Expect'); + } elseif ($expect !== true) { + // Default to using a MB as the point in which to start using the expect header + $expect = $expect ?: 1048576; + // If the expect_header value is numeric then only add if the size is greater than the cutoff + if (is_numeric($expect) && $body->getSize()) { + if ($body->getSize() < $expect) { + $request->removeHeader('Expect'); + } else { + $request->setHeader('Expect', '100-Continue'); + } + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php new file mode 100755 index 000000000..2a537542c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php @@ -0,0 +1,44 @@ +filter($value); + if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->addPrefixedHeaders($request, $param, $value); + } else { + $request->setHeader($param->getWireName(), $value); + } + } + + /** + * Add a prefixed array of headers to the request + * + * @param RequestInterface $request Request to update + * @param Parameter $param Parameter object + * @param array $value Header array to add + * + * @throws InvalidArgumentException + */ + protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value) + { + if (!is_array($value)) { + throw new InvalidArgumentException('An array of mapped headers expected, but received a single value'); + } + $prefix = $param->getSentAs(); + foreach ($value as $headerName => $headerValue) { + $request->setHeader($prefix . $headerName, $headerValue); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php new file mode 100755 index 000000000..757e1c520 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php @@ -0,0 +1,63 @@ +data = new \SplObjectStorage(); + } + + /** + * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a + * Content-Type header unless you specify one here. + * + * @param string $header Header to set when JSON is added (e.g. application/json) + * + * @return self + */ + public function setContentTypeHeader($header = 'application/json') + { + $this->jsonContentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + if (isset($this->data[$command])) { + $json = $this->data[$command]; + } else { + $json = array(); + } + $json[$param->getWireName()] = $this->prepareValue($value, $param); + $this->data[$command] = $json; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + if (isset($this->data[$command])) { + // Don't overwrite the Content-Type if one is set + if ($this->jsonContentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->jsonContentType); + } + + $request->setBody(json_encode($this->data[$command])); + unset($this->data[$command]); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php new file mode 100755 index 000000000..975850b74 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php @@ -0,0 +1,18 @@ +setPostField($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php new file mode 100755 index 000000000..0853ebe62 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php @@ -0,0 +1,24 @@ +filter($value); + if ($value instanceof PostFileInterface) { + $request->addPostFile($value); + } else { + $request->addPostFile($param->getWireName(), $value); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php new file mode 100755 index 000000000..315877aa0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php @@ -0,0 +1,18 @@ +getQuery()->set($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php new file mode 100755 index 000000000..14e0b2d2b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php @@ -0,0 +1,31 @@ +setResponseBody($value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php new file mode 100755 index 000000000..5b7148787 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php @@ -0,0 +1,252 @@ +data = new \SplObjectStorage(); + } + + /** + * Change the content-type header that is added when XML is found + * + * @param string $header Header to set when XML is found + * + * @return self + */ + public function setContentTypeHeader($header) + { + $this->contentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $xml = isset($this->data[$command]) + ? $this->data[$command] + : $this->createRootElement($param->getParent()); + $this->addXml($xml, $param, $value); + + $this->data[$command] = $xml; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + $xml = null; + + // If data was found that needs to be serialized, then do so + if (isset($this->data[$command])) { + $xml = $this->finishDocument($this->data[$command]); + unset($this->data[$command]); + } else { + // Check if XML should always be sent for the command + $operation = $command->getOperation(); + if ($operation->getData('xmlAllowEmpty')) { + $xmlWriter = $this->createRootElement($operation); + $xml = $this->finishDocument($xmlWriter); + } + } + + if ($xml) { + // Don't overwrite the Content-Type if one is set + if ($this->contentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->contentType); + } + $request->setBody($xml); + } + } + + /** + * Create the root XML element to use with a request + * + * @param Operation $operation Operation object + * + * @return \XMLWriter + */ + protected function createRootElement(Operation $operation) + { + static $defaultRoot = array('name' => 'Request'); + // If no root element was specified, then just wrap the XML in 'Request' + $root = $operation->getData('xmlRoot') ?: $defaultRoot; + // Allow the XML declaration to be customized with xmlEncoding + $encoding = $operation->getData('xmlEncoding'); + + $xmlWriter = $this->startDocument($encoding); + + $xmlWriter->startElement($root['name']); + // Create the wrapping element with no namespaces if no namespaces were present + if (!empty($root['namespaces'])) { + // Create the wrapping element with an array of one or more namespaces + foreach ((array) $root['namespaces'] as $prefix => $uri) { + $nsLabel = 'xmlns'; + if (!is_numeric($prefix)) { + $nsLabel .= ':'.$prefix; + } + $xmlWriter->writeAttribute($nsLabel, $uri); + } + } + return $xmlWriter; + } + + /** + * Recursively build the XML body + * + * @param \XMLWriter $xmlWriter XML to modify + * @param Parameter $param API Parameter + * @param mixed $value Value to add + */ + protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value) + { + if ($value === null) { + return; + } + + $value = $param->filter($value); + $type = $param->getType(); + $name = $param->getWireName(); + $prefix = null; + $namespace = $param->getData('xmlNamespace'); + if (false !== strpos($name, ':')) { + list($prefix, $name) = explode(':', $name, 2); + } + + if ($type == 'object' || $type == 'array') { + if (!$param->getData('xmlFlattened')) { + $xmlWriter->startElementNS(null, $name, $namespace); + } + if ($param->getType() == 'array') { + $this->addXmlArray($xmlWriter, $param, $value); + } elseif ($param->getType() == 'object') { + $this->addXmlObject($xmlWriter, $param, $value); + } + if (!$param->getData('xmlFlattened')) { + $xmlWriter->endElement(); + } + return; + } + if ($param->getData('xmlAttribute')) { + $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value); + } else { + $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value); + } + } + + /** + * Write an attribute with namespace if used + * + * @param \XMLWriter $xmlWriter XMLWriter instance + * @param string $prefix Namespace prefix if any + * @param string $name Attribute name + * @param string $namespace The uri of the namespace + * @param string $value The attribute content + */ + protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value) + { + if (empty($namespace)) { + $xmlWriter->writeAttribute($name, $value); + } else { + $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value); + } + } + + /** + * Write an element with namespace if used + * + * @param \XMLWriter $xmlWriter XML writer resource + * @param string $prefix Namespace prefix if any + * @param string $name Element name + * @param string $namespace The uri of the namespace + * @param string $value The element content + */ + protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value) + { + $xmlWriter->startElementNS($prefix, $name, $namespace); + if (strpbrk($value, '<>&')) { + $xmlWriter->writeCData($value); + } else { + $xmlWriter->writeRaw($value); + } + $xmlWriter->endElement(); + } + + /** + * Create a new xml writer and start a document + * + * @param string $encoding document encoding + * + * @return \XMLWriter the writer resource + */ + protected function startDocument($encoding) + { + $xmlWriter = new \XMLWriter(); + $xmlWriter->openMemory(); + $xmlWriter->startDocument('1.0', $encoding); + + return $xmlWriter; + } + + /** + * End the document and return the output + * + * @param \XMLWriter $xmlWriter + * + * @return \string the writer resource + */ + protected function finishDocument($xmlWriter) + { + $xmlWriter->endDocument(); + + return $xmlWriter->outputMemory(); + } + + /** + * Add an array to the XML + */ + protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + if ($items = $param->getItems()) { + foreach ($value as $v) { + $this->addXml($xmlWriter, $items, $v); + } + } + } + + /** + * Add an object to the XML + */ + protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + $noAttributes = array(); + // add values which have attributes + foreach ($value as $name => $v) { + if ($property = $param->getProperty($name)) { + if ($property->getData('xmlAttribute')) { + $this->addXml($xmlWriter, $property, $v); + } else { + $noAttributes[] = array('value' => $v, 'property' => $property); + } + } + } + // now add values with no attributes + foreach ($noAttributes as $element) { + $this->addXml($xmlWriter, $element['property'], $element['value']); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php new file mode 100755 index 000000000..d87eeb945 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php @@ -0,0 +1,26 @@ +getName()] = $param->filter($response->getBody()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php new file mode 100755 index 000000000..0f8737cbd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php @@ -0,0 +1,50 @@ +getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->processPrefixedHeaders($response, $param, $value); + } else { + $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName())); + } + } + + /** + * Process a prefixed header array + * + * @param Response $response Response that contains the headers + * @param Parameter $param Parameter object + * @param array $value Value response array to modify + */ + protected function processPrefixedHeaders(Response $response, Parameter $param, &$value) + { + // Grab prefixed headers that should be placed into an array with the prefix stripped + if ($prefix = $param->getSentAs()) { + $container = $param->getName(); + $len = strlen($prefix); + // Find all matching headers and place them into the containing element + foreach ($response->getHeaders()->toArray() as $key => $header) { + if (stripos($key, $prefix) === 0) { + // Account for multi-value headers + $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header; + } + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php new file mode 100755 index 000000000..a609ebd8c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php @@ -0,0 +1,93 @@ +getResponse()->json(); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $name = $param->getName(); + $key = $param->getWireName(); + if (isset($value[$key])) { + $this->recursiveProcess($param, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + if ($value === null) { + return; + } + + if (is_array($value)) { + $type = $param->getType(); + if ($type == 'array') { + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } elseif ($type == 'object' && !isset($value[0])) { + // On the above line, we ensure that the array is associative and not numerically indexed + $knownProperties = array(); + if ($properties = $param->getProperties()) { + foreach ($properties as $property) { + $name = $property->getName(); + $key = $property->getWireName(); + $knownProperties[$name] = 1; + if (isset($value[$key])) { + $this->recursiveProcess($property, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } elseif (($additional = $param->getAdditionalProperties()) !== true) { + // Validate and filter additional properties + foreach ($value as &$v) { + $this->recursiveProcess($additional, $v); + } + } + } + } + + $value = $param->filter($value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php new file mode 100755 index 000000000..1b10ebce7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php @@ -0,0 +1,23 @@ +getName()] = $response->getReasonPhrase(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php new file mode 100755 index 000000000..033f40c3f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php @@ -0,0 +1,46 @@ +getName()] = $response->getStatusCode(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php new file mode 100755 index 000000000..bb7124be7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php @@ -0,0 +1,151 @@ +getResponse()->xml()), true); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $sentAs = $param->getWireName(); + $name = $param->getName(); + if (isset($value[$sentAs])) { + $this->recursiveProcess($param, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being processed + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + $type = $param->getType(); + + if (!is_array($value)) { + if ($type == 'array') { + // Cast to an array if the value was a string, but should be an array + $this->recursiveProcess($param->getItems(), $value); + $value = array($value); + } + } elseif ($type == 'object') { + $this->processObject($param, $value); + } elseif ($type == 'array') { + $this->processArray($param, $value); + } elseif ($type == 'string' && gettype($value) == 'array') { + $value = ''; + } + + if ($value !== null) { + $value = $param->filter($value); + } + } + + /** + * Process an array + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processArray(Parameter $param, &$value) + { + // Convert the node if it was meant to be an array + if (!isset($value[0])) { + // Collections fo nodes are sometimes wrapped in an additional array. For example: + // 12 should become: + // array('Items' => array(array('a' => 1), array('a' => 2)) + // Some nodes are not wrapped. For example: 12 + // should become array('Foo' => array(array('a' => 1), array('a' => 2)) + if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) { + // Account for the case of a collection wrapping wrapped nodes: Items => Item[] + $value = $value[$param->getItems()->getWireName()]; + // If the wrapped node only had one value, then make it an array of nodes + if (!isset($value[0]) || !is_array($value)) { + $value = array($value); + } + } elseif (!empty($value)) { + // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the + // value is set and not empty + $value = array($value); + } + } + + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } + + /** + * Process an object + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processObject(Parameter $param, &$value) + { + // Ensure that the array is associative and not numerically indexed + if (!isset($value[0]) && ($properties = $param->getProperties())) { + $knownProperties = array(); + foreach ($properties as $property) { + $name = $property->getName(); + $sentAs = $property->getWireName(); + $knownProperties[$name] = 1; + if ($property->getData('xmlAttribute')) { + $this->processXmlAttribute($property, $value); + } elseif (isset($value[$sentAs])) { + $this->recursiveProcess($property, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } + } + } + + /** + * Process an XML attribute property + * + * @param Parameter $property Property to process + * @param array $value Value to process and update + */ + protected function processXmlAttribute(Parameter $property, array &$value) + { + $sentAs = $property->getWireName(); + if (isset($value['@attributes'][$sentAs])) { + $value[$property->getName()] = $value['@attributes'][$sentAs]; + unset($value['@attributes'][$sentAs]); + if (empty($value['@attributes'])) { + unset($value['@attributes']); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php new file mode 100755 index 000000000..74cb62813 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php @@ -0,0 +1,138 @@ + 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor', + 'request.header' => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor', + 'request.json' => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', + 'request.postField' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor', + 'request.postFile' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor', + 'request.query' => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor', + 'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.responseBody' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.xml' => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor', + 'response.body' => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor', + 'response.header' => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor', + 'response.json' => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', + 'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor', + 'response.statusCode' => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor', + 'response.xml' => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor' + ); + + /** @var array Array of mappings of location names to classes */ + protected $mappings; + + /** @var array Cache of instantiated visitors */ + protected $cache = array(); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to + * use the default values. + */ + public function __construct(array $mappings = null) + { + $this->mappings = $mappings === null ? self::$defaultMappings : $mappings; + } + + /** + * Get an instance of a request visitor by location name + * + * @param string $visitor Visitor name + * + * @return RequestVisitorInterface + */ + public function getRequestVisitor($visitor) + { + return $this->getKey('request.' . $visitor); + } + + /** + * Get an instance of a response visitor by location name + * + * @param string $visitor Visitor name + * + * @return ResponseVisitorInterface + */ + public function getResponseVisitor($visitor) + { + return $this->getKey('response.' . $visitor); + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param RequestVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addRequestVisitor($name, RequestVisitorInterface $visitor) + { + $this->cache['request.' . $name] = $visitor; + + return $this; + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param ResponseVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addResponseVisitor($name, ResponseVisitorInterface $visitor) + { + $this->cache['response.' . $name] = $visitor; + + return $this; + } + + /** + * Get a visitor by key value name + * + * @param string $key Key name to retrieve + * + * @return mixed + * @throws InvalidArgumentException + */ + private function getKey($key) + { + if (!isset($this->cache[$key])) { + if (!isset($this->mappings[$key])) { + list($type, $name) = explode('.', $key); + throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}"); + } + $this->cache[$key] = new $this->mappings[$key]; + } + + return $this->cache[$key]; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php new file mode 100755 index 000000000..0748b5af0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php @@ -0,0 +1,89 @@ +responseParser = $parser; + + return $this; + } + + /** + * Set the request serializer used with the command + * + * @param RequestSerializerInterface $serializer Request serializer + * + * @return self + */ + public function setRequestSerializer(RequestSerializerInterface $serializer) + { + $this->requestSerializer = $serializer; + + return $this; + } + + /** + * Get the request serializer used with the command + * + * @return RequestSerializerInterface + */ + public function getRequestSerializer() + { + if (!$this->requestSerializer) { + // Use the default request serializer if none was found + $this->requestSerializer = DefaultRequestSerializer::getInstance(); + } + + return $this->requestSerializer; + } + + /** + * Get the response parser used for the operation + * + * @return ResponseParserInterface + */ + public function getResponseParser() + { + if (!$this->responseParser) { + // Use the default response parser if none was found + $this->responseParser = OperationResponseParser::getInstance(); + } + + return $this->responseParser; + } + + protected function build() + { + // Prepare and serialize the request + $this->request = $this->getRequestSerializer()->prepare($this); + } + + protected function process() + { + // Do not process the response if 'command.response_processing' is set to 'raw' + $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW + ? $this->request->getResponse() + : $this->getResponseParser()->parse($this); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php new file mode 100755 index 000000000..ca00bc062 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php @@ -0,0 +1,195 @@ +factory = $factory; + $this->schemaInModels = $schemaInModels; + } + + /** + * Add a location visitor to the command + * + * @param string $location Location to associate with the visitor + * @param ResponseVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, ResponseVisitorInterface $visitor) + { + $this->factory->addResponseVisitor($location, $visitor); + + return $this; + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $operation = $command->getOperation(); + $type = $operation->getResponseType(); + $model = null; + + if ($type == OperationInterface::TYPE_MODEL) { + $model = $operation->getServiceDescription()->getModel($operation->getResponseClass()); + } elseif ($type == OperationInterface::TYPE_CLASS) { + return $this->parseClass($command); + } + + if (!$model) { + // Return basic processing if the responseType is not model or the model cannot be found + return parent::handleParsing($command, $response, $contentType); + } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) { + // Returns a model with no visiting if the command response processing is not model + return new Model(parent::handleParsing($command, $response, $contentType)); + } else { + // Only inject the schema into the model if "schemaInModel" is true + return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null); + } + } + + /** + * Parse a class object + * + * @param CommandInterface $command Command to parse into an object + * + * @return mixed + * @throws ResponseClassException + */ + protected function parseClass(CommandInterface $command) + { + // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result + $event = new CreateResponseClassEvent(array('command' => $command)); + $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event); + if ($result = $event->getResult()) { + return $result; + } + + $className = $command->getOperation()->getResponseClass(); + if (!method_exists($className, 'fromCommand')) { + throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method"); + } + + return $className::fromCommand($command); + } + + /** + * Perform transformations on the result array + * + * @param Parameter $model Model that defines the structure + * @param CommandInterface $command Command that performed the operation + * @param Response $response Response received + * + * @return array Returns the array of result data + */ + protected function visitResult(Parameter $model, CommandInterface $command, Response $response) + { + $foundVisitors = $result = $knownProps = array(); + $props = $model->getProperties(); + + foreach ($props as $schema) { + if ($location = $schema->getLocation()) { + // Trigger the before method on the first found visitor of this type + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + } + } + + // Visit additional properties when it is an actual schema + if (($additional = $model->getAdditionalProperties()) instanceof Parameter) { + $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors); + } + + // Apply the parameter value with the location visitor + foreach ($props as $schema) { + $knownProps[$schema->getName()] = 1; + if ($location = $schema->getLocation()) { + $foundVisitors[$location]->visit($command, $response, $schema, $result); + } + } + + // Remove any unknown and potentially unsafe top-level properties + if ($additional === false) { + $result = array_intersect_key($result, $knownProps); + } + + // Call the after() method of each found visitor + foreach ($foundVisitors as $visitor) { + $visitor->after($command); + } + + return $result; + } + + protected function visitAdditionalProperties( + Parameter $model, + CommandInterface $command, + Response $response, + Parameter $additional, + &$result, + array &$foundVisitors + ) { + // Only visit when a location is specified + if ($location = $additional->getLocation()) { + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + // Only traverse if an array was parsed from the before() visitors + if (is_array($result)) { + // Find each additional property + foreach (array_keys($result) as $key) { + // Check if the model actually knows this property. If so, then it is not additional + if (!$model->getProperty($key)) { + // Set the name to the key so that we can parse it with each visitor + $additional->setName($key); + $foundVisitors[$location]->visit($command, $response, $additional, $result); + } + } + // Reset the additionalProperties name to null + $additional->setName(null); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php new file mode 100755 index 000000000..60b9334d4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php @@ -0,0 +1,21 @@ + true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true, + 'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true, + 'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true, + 'errorResponses' => true + ); + + /** @var array Parameters */ + protected $parameters = array(); + + /** @var Parameter Additional parameters schema */ + protected $additionalParameters; + + /** @var string Name of the command */ + protected $name; + + /** @var string HTTP method */ + protected $httpMethod; + + /** @var string This is a short summary of what the operation does */ + protected $summary; + + /** @var string A longer text field to explain the behavior of the operation. */ + protected $notes; + + /** @var string Reference URL providing more information about the operation */ + protected $documentationUrl; + + /** @var string HTTP URI of the command */ + protected $uri; + + /** @var string Class of the command object */ + protected $class; + + /** @var string This is what is returned from the method */ + protected $responseClass; + + /** @var string Type information about the response */ + protected $responseType; + + /** @var string Information about the response returned by the operation */ + protected $responseNotes; + + /** @var bool Whether or not the command is deprecated */ + protected $deprecated; + + /** @var array Array of errors that could occur when running the command */ + protected $errorResponses; + + /** @var ServiceDescriptionInterface */ + protected $description; + + /** @var array Extra operation information */ + protected $data; + + /** + * Builds an Operation object using an array of configuration data: + * - name: (string) Name of the command + * - httpMethod: (string) HTTP method of the operation + * - uri: (string) URI template that can create a relative or absolute URL + * - class: (string) Concrete class that implements this command + * - parameters: (array) Associative array of parameters for the command. {@see Parameter} for information. + * - summary: (string) This is a short summary of what the operation does + * - notes: (string) A longer text field to explain the behavior of the operation. + * - documentationUrl: (string) Reference URL providing more information about the operation + * - responseClass: (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant + * class name, or model. + * - responseNotes: (string) Information about the response returned by the operation + * - responseType: (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this + * value will be automatically inferred based on whether or not there is a model matching the + * name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default. + * - deprecated: (bool) Set to true if this is a deprecated command + * - errorResponses: (array) Errors that could occur when executing the command. Array of hashes, each with a + * 'code' (the HTTP response code), 'reason' (response reason phrase or description of the + * error), and 'class' (a custom exception class that would be thrown if the error is + * encountered). + * - data: (array) Any extra data that might be used to help build or serialize the operation + * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is + * not in the schema + * + * @param array $config Array of configuration data + * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found + */ + public function __construct(array $config = array(), ServiceDescriptionInterface $description = null) + { + $this->description = $description; + + // Get the intersection of the available properties and properties set on the operation + foreach (array_intersect_key($config, self::$properties) as $key => $value) { + $this->{$key} = $value; + } + + $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS; + $this->deprecated = (bool) $this->deprecated; + $this->errorResponses = $this->errorResponses ?: array(); + $this->data = $this->data ?: array(); + + if (!$this->responseClass) { + $this->responseClass = 'array'; + $this->responseType = 'primitive'; + } elseif ($this->responseType) { + // Set the response type to perform validation + $this->setResponseType($this->responseType); + } else { + // A response class was set and no response type was set, so guess what the type is + $this->inferResponseType(); + } + + // Parameters need special handling when adding + if ($this->parameters) { + foreach ($this->parameters as $name => $param) { + if ($param instanceof Parameter) { + $param->setName($name)->setParent($this); + } elseif (is_array($param)) { + $param['name'] = $name; + $this->addParam(new Parameter($param, $this->description)); + } + } + } + + if ($this->additionalParameters) { + if ($this->additionalParameters instanceof Parameter) { + $this->additionalParameters->setParent($this); + } elseif (is_array($this->additionalParameters)) { + $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description)); + } + } + } + + public function toArray() + { + $result = array(); + // Grab valid properties and filter out values that weren't set + foreach (array_keys(self::$properties) as $check) { + if ($value = $this->{$check}) { + $result[$check] = $value; + } + } + // Remove the name property + unset($result['name']); + // Parameters need to be converted to arrays + $result['parameters'] = array(); + foreach ($this->parameters as $key => $param) { + $result['parameters'][$key] = $param->toArray(); + } + // Additional parameters need to be cast to an array + if ($this->additionalParameters instanceof Parameter) { + $result['additionalParameters'] = $this->additionalParameters->toArray(); + } + + return $result; + } + + public function getServiceDescription() + { + return $this->description; + } + + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + public function getParams() + { + return $this->parameters; + } + + public function getParamNames() + { + return array_keys($this->parameters); + } + + public function hasParam($name) + { + return isset($this->parameters[$name]); + } + + public function getParam($param) + { + return isset($this->parameters[$param]) ? $this->parameters[$param] : null; + } + + /** + * Add a parameter to the command + * + * @param Parameter $param Parameter to add + * + * @return self + */ + public function addParam(Parameter $param) + { + $this->parameters[$param->getName()] = $param; + $param->setParent($this); + + return $this; + } + + /** + * Remove a parameter from the command + * + * @param string $name Name of the parameter to remove + * + * @return self + */ + public function removeParam($name) + { + unset($this->parameters[$name]); + + return $this; + } + + public function getHttpMethod() + { + return $this->httpMethod; + } + + /** + * Set the HTTP method of the command + * + * @param string $httpMethod Method to set + * + * @return self + */ + public function setHttpMethod($httpMethod) + { + $this->httpMethod = $httpMethod; + + return $this; + } + + public function getClass() + { + return $this->class; + } + + /** + * Set the concrete class of the command + * + * @param string $className Concrete class name + * + * @return self + */ + public function setClass($className) + { + $this->class = $className; + + return $this; + } + + public function getName() + { + return $this->name; + } + + /** + * Set the name of the command + * + * @param string $name Name of the command + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getSummary() + { + return $this->summary; + } + + /** + * Set a short summary of what the operation does + * + * @param string $summary Short summary of the operation + * + * @return self + */ + public function setSummary($summary) + { + $this->summary = $summary; + + return $this; + } + + public function getNotes() + { + return $this->notes; + } + + /** + * Set a longer text field to explain the behavior of the operation. + * + * @param string $notes Notes on the operation + * + * @return self + */ + public function setNotes($notes) + { + $this->notes = $notes; + + return $this; + } + + public function getDocumentationUrl() + { + return $this->documentationUrl; + } + + /** + * Set the URL pointing to additional documentation on the command + * + * @param string $docUrl Documentation URL + * + * @return self + */ + public function setDocumentationUrl($docUrl) + { + $this->documentationUrl = $docUrl; + + return $this; + } + + public function getResponseClass() + { + return $this->responseClass; + } + + /** + * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array', + * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID). + * + * @param string $responseClass Type of response + * + * @return self + */ + public function setResponseClass($responseClass) + { + $this->responseClass = $responseClass; + $this->inferResponseType(); + + return $this; + } + + public function getResponseType() + { + return $this->responseType; + } + + /** + * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation' + * + * @param string $responseType Response type information + * + * @return self + * @throws InvalidArgumentException + */ + public function setResponseType($responseType) + { + static $types = array( + self::TYPE_PRIMITIVE => true, + self::TYPE_CLASS => true, + self::TYPE_MODEL => true, + self::TYPE_DOCUMENTATION => true + ); + if (!isset($types[$responseType])) { + throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types))); + } + + $this->responseType = $responseType; + + return $this; + } + + public function getResponseNotes() + { + return $this->responseNotes; + } + + /** + * Set notes about the response of the operation + * + * @param string $notes Response notes + * + * @return self + */ + public function setResponseNotes($notes) + { + $this->responseNotes = $notes; + + return $this; + } + + public function getDeprecated() + { + return $this->deprecated; + } + + /** + * Set whether or not the command is deprecated + * + * @param bool $isDeprecated Set to true to mark as deprecated + * + * @return self + */ + public function setDeprecated($isDeprecated) + { + $this->deprecated = $isDeprecated; + + return $this; + } + + public function getUri() + { + return $this->uri; + } + + /** + * Set the URI template of the command + * + * @param string $uri URI template to set + * + * @return self + */ + public function setUri($uri) + { + $this->uri = $uri; + + return $this; + } + + public function getErrorResponses() + { + return $this->errorResponses; + } + + /** + * Add an error to the command + * + * @param string $code HTTP response code + * @param string $reason HTTP response reason phrase or information about the error + * @param string $class Exception class associated with the error + * + * @return self + */ + public function addErrorResponse($code, $reason, $class) + { + $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class); + + return $this; + } + + /** + * Set all of the error responses of the operation + * + * @param array $errorResponses Hash of error name to a hash containing a code, reason, class + * + * @return self + */ + public function setErrorResponses(array $errorResponses) + { + $this->errorResponses = $errorResponses; + + return $this; + } + + public function getData($name) + { + return isset($this->data[$name]) ? $this->data[$name] : null; + } + + /** + * Set a particular data point on the operation + * + * @param string $name Name of the data value + * @param mixed $value Value to set + * + * @return self + */ + public function setData($name, $value) + { + $this->data[$name] = $value; + + return $this; + } + + /** + * Get the additionalParameters of the operation + * + * @return Parameter|null + */ + public function getAdditionalParameters() + { + return $this->additionalParameters; + } + + /** + * Set the additionalParameters of the operation + * + * @param Parameter|null $parameter Parameter to set + * + * @return self + */ + public function setAdditionalParameters($parameter) + { + if ($this->additionalParameters = $parameter) { + $this->additionalParameters->setParent($this); + } + + return $this; + } + + /** + * Infer the response type from the responseClass value + */ + protected function inferResponseType() + { + static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1); + if (isset($primitives[$this->responseClass])) { + $this->responseType = self::TYPE_PRIMITIVE; + } elseif ($this->description && $this->description->hasModel($this->responseClass)) { + $this->responseType = self::TYPE_MODEL; + } else { + $this->responseType = self::TYPE_CLASS; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php new file mode 100755 index 000000000..4de41bd67 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php @@ -0,0 +1,159 @@ +getModel($data['$ref'])) { + $data = $model->toArray() + $data; + } + } elseif (isset($data['extends'])) { + // If this parameter extends from another parameter then start with the actual data + // union in the parent's data (e.g. actual supersedes parent) + if ($extends = $description->getModel($data['extends'])) { + $data += $extends->toArray(); + } + } + } + + // Pull configuration data into the parameter + foreach ($data as $key => $value) { + $this->{$key} = $value; + } + + $this->serviceDescription = $description; + $this->required = (bool) $this->required; + $this->data = (array) $this->data; + + if ($this->filters) { + $this->setFilters((array) $this->filters); + } + + if ($this->type == 'object' && $this->additionalProperties === null) { + $this->additionalProperties = true; + } + } + + /** + * Convert the object to an array + * + * @return array + */ + public function toArray() + { + static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs', + 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum', + 'filters'); + + $result = array(); + + // Anything that is in the `Items` attribute of an array *must* include it's name if available + if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) { + $result['name'] = $this->name; + } + + foreach ($checks as $c) { + if ($value = $this->{$c}) { + $result[$c] = $value; + } + } + + if ($this->default !== null) { + $result['default'] = $this->default; + } + + if ($this->items !== null) { + $result['items'] = $this->getItems()->toArray(); + } + + if ($this->additionalProperties !== null) { + $result['additionalProperties'] = $this->getAdditionalProperties(); + if ($result['additionalProperties'] instanceof self) { + $result['additionalProperties'] = $result['additionalProperties']->toArray(); + } + } + + if ($this->type == 'object' && $this->properties) { + $result['properties'] = array(); + foreach ($this->getProperties() as $name => $property) { + $result['properties'][$name] = $property->toArray(); + } + } + + return $result; + } + + /** + * Get the default or static value of the command based on a value + * + * @param string $value Value that is currently set + * + * @return mixed Returns the value, a static value if one is present, or a default value + */ + public function getValue($value) + { + if ($this->static || ($this->default !== null && $value === null)) { + return $this->default; + } + + return $value; + } + + /** + * Run a value through the filters OR format attribute associated with the parameter + * + * @param mixed $value Value to filter + * + * @return mixed Returns the filtered value + */ + public function filter($value) + { + // Formats are applied exclusively and supersed filters + if ($this->format) { + return SchemaFormatter::format($this->format, $value); + } + + // Convert Boolean values + if ($this->type == 'boolean' && !is_bool($value)) { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); + } + + // Apply filters to the value + if ($this->filters) { + foreach ($this->filters as $filter) { + if (is_array($filter)) { + // Convert complex filters that hold value place holders + foreach ($filter['args'] as &$data) { + if ($data == '@value') { + $data = $value; + } elseif ($data == '@api') { + $data = $this; + } + } + $value = call_user_func_array($filter['method'], $filter['args']); + } else { + $value = call_user_func($filter, $value); + } + } + } + + return $value; + } + + /** + * Get the name of the parameter + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the key of the parameter, where sentAs will supersede name if it is set + * + * @return string + */ + public function getWireName() + { + return $this->sentAs ?: $this->name; + } + + /** + * Set the name of the parameter + * + * @param string $name Name to set + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get the type(s) of the parameter + * + * @return string|array + */ + public function getType() + { + return $this->type; + } + + /** + * Set the type(s) of the parameter + * + * @param string|array $type Type of parameter or array of simple types used in a union + * + * @return self + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Get if the parameter is required + * + * @return bool + */ + public function getRequired() + { + return $this->required; + } + + /** + * Set if the parameter is required + * + * @param bool $isRequired Whether or not the parameter is required + * + * @return self + */ + public function setRequired($isRequired) + { + $this->required = (bool) $isRequired; + + return $this; + } + + /** + * Get the default value of the parameter + * + * @return string|null + */ + public function getDefault() + { + return $this->default; + } + + /** + * Set the default value of the parameter + * + * @param string|null $default Default value to set + * + * @return self + */ + public function setDefault($default) + { + $this->default = $default; + + return $this; + } + + /** + * Get the description of the parameter + * + * @return string|null + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set the description of the parameter + * + * @param string $description Description + * + * @return self + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Get the minimum acceptable value for an integer + * + * @return int|null + */ + public function getMinimum() + { + return $this->minimum; + } + + /** + * Set the minimum acceptable value for an integer + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinimum($min) + { + $this->minimum = $min; + + return $this; + } + + /** + * Get the maximum acceptable value for an integer + * + * @return int|null + */ + public function getMaximum() + { + return $this->maximum; + } + + /** + * Set the maximum acceptable value for an integer + * + * @param int $max Maximum + * + * @return self + */ + public function setMaximum($max) + { + $this->maximum = $max; + + return $this; + } + + /** + * Get the minimum allowed length of a string value + * + * @return int + */ + public function getMinLength() + { + return $this->minLength; + } + + /** + * Set the minimum allowed length of a string value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinLength($min) + { + $this->minLength = $min; + + return $this; + } + + /** + * Get the maximum allowed length of a string value + * + * @return int|null + */ + public function getMaxLength() + { + return $this->maxLength; + } + + /** + * Set the maximum allowed length of a string value + * + * @param int $max Maximum length + * + * @return self + */ + public function setMaxLength($max) + { + $this->maxLength = $max; + + return $this; + } + + /** + * Get the maximum allowed number of items in an array value + * + * @return int|null + */ + public function getMaxItems() + { + return $this->maxItems; + } + + /** + * Set the maximum allowed number of items in an array value + * + * @param int $max Maximum + * + * @return self + */ + public function setMaxItems($max) + { + $this->maxItems = $max; + + return $this; + } + + /** + * Get the minimum allowed number of items in an array value + * + * @return int + */ + public function getMinItems() + { + return $this->minItems; + } + + /** + * Set the minimum allowed number of items in an array value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinItems($min) + { + $this->minItems = $min; + + return $this; + } + + /** + * Get the location of the parameter + * + * @return string|null + */ + public function getLocation() + { + return $this->location; + } + + /** + * Set the location of the parameter + * + * @param string|null $location Location of the parameter + * + * @return self + */ + public function setLocation($location) + { + $this->location = $location; + + return $this; + } + + /** + * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being + * applied to a location. + * + * @return string|null + */ + public function getSentAs() + { + return $this->sentAs; + } + + /** + * Set the sentAs attribute + * + * @param string|null $name Name of the value as it is sent over the wire + * + * @return self + */ + public function setSentAs($name) + { + $this->sentAs = $name; + + return $this; + } + + /** + * Retrieve a known property from the parameter by name or a data property by name. When not specific name value + * is specified, all data properties will be returned. + * + * @param string|null $name Specify a particular property name to retrieve + * + * @return array|mixed|null + */ + public function getData($name = null) + { + if (!$name) { + return $this->data; + } + + if (isset($this->data[$name])) { + return $this->data[$name]; + } elseif (isset($this->{$name})) { + return $this->{$name}; + } + + return null; + } + + /** + * Set the extra data properties of the parameter or set a specific extra property + * + * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set + * @param mixed|null $data When setting a specific extra property, specify the data to set for it + * + * @return self + */ + public function setData($nameOrData, $data = null) + { + if (is_array($nameOrData)) { + $this->data = $nameOrData; + } else { + $this->data[$nameOrData] = $data; + } + + return $this; + } + + /** + * Get whether or not the default value can be changed + * + * @return mixed|null + */ + public function getStatic() + { + return $this->static; + } + + /** + * Set to true if the default value cannot be changed + * + * @param bool $static True or false + * + * @return self + */ + public function setStatic($static) + { + $this->static = (bool) $static; + + return $this; + } + + /** + * Get an array of filters used by the parameter + * + * @return array + */ + public function getFilters() + { + return $this->filters ?: array(); + } + + /** + * Set the array of filters used by the parameter + * + * @param array $filters Array of functions to use as filters + * + * @return self + */ + public function setFilters(array $filters) + { + $this->filters = array(); + foreach ($filters as $filter) { + $this->addFilter($filter); + } + + return $this; + } + + /** + * Add a filter to the parameter + * + * @param string|array $filter Method to filter the value through + * + * @return self + * @throws InvalidArgumentException + */ + public function addFilter($filter) + { + if (is_array($filter)) { + if (!isset($filter['method'])) { + throw new InvalidArgumentException('A [method] value must be specified for each complex filter'); + } + } + + if (!$this->filters) { + $this->filters = array($filter); + } else { + $this->filters[] = $filter; + } + + return $this; + } + + /** + * Get the parent object (an {@see OperationInterface} or {@see Parameter} + * + * @return OperationInterface|Parameter|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * Set the parent object of the parameter + * + * @param OperationInterface|Parameter|null $parent Parent container of the parameter + * + * @return self + */ + public function setParent($parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Get the properties of the parameter + * + * @return array + */ + public function getProperties() + { + if (!$this->propertiesCache) { + $this->propertiesCache = array(); + foreach (array_keys($this->properties) as $name) { + $this->propertiesCache[$name] = $this->getProperty($name); + } + } + + return $this->propertiesCache; + } + + /** + * Get a specific property from the parameter + * + * @param string $name Name of the property to retrieve + * + * @return null|Parameter + */ + public function getProperty($name) + { + if (!isset($this->properties[$name])) { + return null; + } + + if (!($this->properties[$name] instanceof self)) { + $this->properties[$name]['name'] = $name; + $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription); + $this->properties[$name]->setParent($this); + } + + return $this->properties[$name]; + } + + /** + * Remove a property from the parameter + * + * @param string $name Name of the property to remove + * + * @return self + */ + public function removeProperty($name) + { + unset($this->properties[$name]); + $this->propertiesCache = null; + + return $this; + } + + /** + * Add a property to the parameter + * + * @param Parameter $property Properties to set + * + * @return self + */ + public function addProperty(Parameter $property) + { + $this->properties[$property->getName()] = $property; + $property->setParent($this); + $this->propertiesCache = null; + + return $this; + } + + /** + * Get the additionalProperties value of the parameter + * + * @return bool|Parameter|null + */ + public function getAdditionalProperties() + { + if (is_array($this->additionalProperties)) { + $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription); + $this->additionalProperties->setParent($this); + } + + return $this->additionalProperties; + } + + /** + * Set the additionalProperties value of the parameter + * + * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow + * + * @return self + */ + public function setAdditionalProperties($additional) + { + $this->additionalProperties = $additional; + + return $this; + } + + /** + * Set the items data of the parameter + * + * @param Parameter|null $items Items to set + * + * @return self + */ + public function setItems(Parameter $items = null) + { + if ($this->items = $items) { + $this->items->setParent($this); + } + + return $this; + } + + /** + * Get the item data of the parameter + * + * @return Parameter|null + */ + public function getItems() + { + if (is_array($this->items)) { + $this->items = new static($this->items, $this->serviceDescription); + $this->items->setParent($this); + } + + return $this->items; + } + + /** + * Get the class that the parameter must implement + * + * @return null|string + */ + public function getInstanceOf() + { + return $this->instanceOf; + } + + /** + * Set the class that the parameter must be an instance of + * + * @param string|null $instanceOf Class or interface name + * + * @return self + */ + public function setInstanceOf($instanceOf) + { + $this->instanceOf = $instanceOf; + + return $this; + } + + /** + * Get the enum of strings that are valid for the parameter + * + * @return array|null + */ + public function getEnum() + { + return $this->enum; + } + + /** + * Set the enum of strings that are valid for the parameter + * + * @param array|null $enum Array of strings or null + * + * @return self + */ + public function setEnum(array $enum = null) + { + $this->enum = $enum; + + return $this; + } + + /** + * Get the regex pattern that must match a value when the value is a string + * + * @return string + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Set the regex pattern that must match a value when the value is a string + * + * @param string $pattern Regex pattern + * + * @return self + */ + public function setPattern($pattern) + { + $this->pattern = $pattern; + + return $this; + } + + /** + * Get the format attribute of the schema + * + * @return string + */ + public function getFormat() + { + return $this->format; + } + + /** + * Set the format attribute of the schema + * + * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http) + * + * @return self + */ + public function setFormat($format) + { + $this->format = $format; + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php new file mode 100755 index 000000000..7f47fc9d7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php @@ -0,0 +1,156 @@ +setTimezone(self::getUtcTimeZone())->format($format); + } + + throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php new file mode 100755 index 000000000..b045422d4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php @@ -0,0 +1,291 @@ +castIntegerToStringType = $castIntegerToStringType; + } + + public function validate(Parameter $param, &$value) + { + $this->errors = array(); + $this->recursiveProcess($param, $value); + + if (empty($this->errors)) { + return true; + } else { + sort($this->errors); + return false; + } + } + + /** + * Get the errors encountered while validating + * + * @return array + */ + public function getErrors() + { + return $this->errors ?: array(); + } + + /** + * Recursively validate a parameter + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and validate. The value may change during this validate. + * @param string $path Current validation path (used for error reporting) + * @param int $depth Current depth in the validation validate + * + * @return bool Returns true if valid, or false if invalid + */ + protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0) + { + // Update the value by adding default or static values + $value = $param->getValue($value); + + $required = $param->getRequired(); + // if the value is null and the parameter is not required or is static, then skip any further recursion + if ((null === $value && !$required) || $param->getStatic()) { + return true; + } + + $type = $param->getType(); + // Attempt to limit the number of times is_array is called by tracking if the value is an array + $valueIsArray = is_array($value); + // If a name is set then update the path so that validation messages are more helpful + if ($name = $param->getName()) { + $path .= "[{$name}]"; + } + + if ($type == 'object') { + + // Objects are either associative arrays, ToArrayInterface, or some other object + if ($param->getInstanceOf()) { + $instance = $param->getInstanceOf(); + if (!($value instanceof $instance)) { + $this->errors[] = "{$path} must be an instance of {$instance}"; + return false; + } + } + + // Determine whether or not this "value" has properties and should be traversed + $traverse = $temporaryValue = false; + + // Convert the value to an array + if (!$valueIsArray && $value instanceof ToArrayInterface) { + $value = $value->toArray(); + } + + if ($valueIsArray) { + // Ensure that the array is associative and not numerically indexed + if (isset($value[0])) { + $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array."; + return false; + } + $traverse = true; + } elseif ($value === null) { + // Attempt to let the contents be built up by default values if possible + $value = array(); + $temporaryValue = $valueIsArray = $traverse = true; + } + + if ($traverse) { + + if ($properties = $param->getProperties()) { + // if properties were found, the validate each property of the value + foreach ($properties as $property) { + $name = $property->getName(); + if (isset($value[$name])) { + $this->recursiveProcess($property, $value[$name], $path, $depth + 1); + } else { + $current = null; + $this->recursiveProcess($property, $current, $path, $depth + 1); + // Only set the value if it was populated with something + if (null !== $current) { + $value[$name] = $current; + } + } + } + } + + $additional = $param->getAdditionalProperties(); + if ($additional !== true) { + // If additional properties were found, then validate each against the additionalProperties attr. + $keys = array_keys($value); + // Determine the keys that were specified that were not listed in the properties of the schema + $diff = array_diff($keys, array_keys($properties)); + if (!empty($diff)) { + // Determine which keys are not in the properties + if ($additional instanceOf Parameter) { + foreach ($diff as $key) { + $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth); + } + } else { + // if additionalProperties is set to false and there are additionalProperties in the values, then fail + foreach ($diff as $prop) { + $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop); + } + } + } + } + + // A temporary value will be used to traverse elements that have no corresponding input value. + // This allows nested required parameters with default values to bubble up into the input. + // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value. + if ($temporaryValue && empty($value)) { + $value = null; + $valueIsArray = false; + } + } + + } elseif ($type == 'array' && $valueIsArray && $param->getItems()) { + foreach ($value as $i => &$item) { + // Validate each item in an array against the items attribute of the schema + $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1); + } + } + + // If the value is required and the type is not null, then there is an error if the value is not set + if ($required && $value === null && $type != 'null') { + $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required'); + if ($param->getDescription()) { + $message .= ': ' . $param->getDescription(); + } + $this->errors[] = $message; + return false; + } + + // Validate that the type is correct. If the type is string but an integer was passed, the class can be + // instructed to cast the integer to a string to pass validation. This is the default behavior. + if ($type && (!$type = $this->determineType($type, $value))) { + if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) { + $value = (string) $value; + } else { + $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType()); + } + } + + // Perform type specific validation for strings, arrays, and integers + if ($type == 'string') { + + // Strings can have enums which are a list of predefined values + if (($enum = $param->getEnum()) && !in_array($value, $enum)) { + $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) { + return '"' . addslashes($s) . '"'; + }, $enum)); + } + // Strings can have a regex pattern that the value must match + if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) { + $this->errors[] = "{$path} must match the following regular expression: {$pattern}"; + } + + $strLen = null; + if ($min = $param->getMinLength()) { + $strLen = strlen($value); + if ($strLen < $min) { + $this->errors[] = "{$path} length must be greater than or equal to {$min}"; + } + } + if ($max = $param->getMaxLength()) { + if (($strLen ?: strlen($value)) > $max) { + $this->errors[] = "{$path} length must be less than or equal to {$max}"; + } + } + + } elseif ($type == 'array') { + + $size = null; + if ($min = $param->getMinItems()) { + $size = count($value); + if ($size < $min) { + $this->errors[] = "{$path} must contain {$min} or more elements"; + } + } + if ($max = $param->getMaxItems()) { + if (($size ?: count($value)) > $max) { + $this->errors[] = "{$path} must contain {$max} or fewer elements"; + } + } + + } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') { + if (($min = $param->getMinimum()) && $value < $min) { + $this->errors[] = "{$path} must be greater than or equal to {$min}"; + } + if (($max = $param->getMaximum()) && $value > $max) { + $this->errors[] = "{$path} must be less than or equal to {$max}"; + } + } + + return empty($this->errors); + } + + /** + * From the allowable types, determine the type that the variable matches + * + * @param string $type Parameter type + * @param mixed $value Value to determine the type + * + * @return string|bool Returns the matching type on + */ + protected function determineType($type, $value) + { + foreach ((array) $type as $t) { + if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) { + return 'string'; + } elseif ($t == 'object' && (is_array($value) || is_object($value))) { + return 'object'; + } elseif ($t == 'array' && is_array($value)) { + return 'array'; + } elseif ($t == 'integer' && is_integer($value)) { + return 'integer'; + } elseif ($t == 'boolean' && is_bool($value)) { + return 'boolean'; + } elseif ($t == 'number' && is_numeric($value)) { + return 'number'; + } elseif ($t == 'numeric' && is_numeric($value)) { + return 'numeric'; + } elseif ($t == 'null' && !$value) { + return 'null'; + } elseif ($t == 'any') { + return 'any'; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php new file mode 100755 index 000000000..286e65eec --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php @@ -0,0 +1,271 @@ +load($config, $options); + } + + /** + * @param array $config Array of configuration data + */ + public function __construct(array $config = array()) + { + $this->fromArray($config); + } + + public function serialize() + { + return json_encode($this->toArray()); + } + + public function unserialize($json) + { + $this->operations = array(); + $this->fromArray(json_decode($json, true)); + } + + public function toArray() + { + $result = array( + 'name' => $this->name, + 'apiVersion' => $this->apiVersion, + 'baseUrl' => $this->baseUrl, + 'description' => $this->description + ) + $this->extraData; + $result['operations'] = array(); + foreach ($this->getOperations() as $name => $operation) { + $result['operations'][$operation->getName() ?: $name] = $operation->toArray(); + } + if (!empty($this->models)) { + $result['models'] = array(); + foreach ($this->models as $id => $model) { + $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model; + } + } + + return array_filter($result); + } + + public function getBaseUrl() + { + return $this->baseUrl; + } + + /** + * Set the baseUrl of the description + * + * @param string $baseUrl Base URL of each operation + * + * @return self + */ + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + + return $this; + } + + public function getOperations() + { + foreach (array_keys($this->operations) as $name) { + $this->getOperation($name); + } + + return $this->operations; + } + + public function hasOperation($name) + { + return isset($this->operations[$name]); + } + + public function getOperation($name) + { + // Lazily retrieve and build operations + if (!isset($this->operations[$name])) { + return null; + } + + if (!($this->operations[$name] instanceof Operation)) { + $this->operations[$name] = new Operation($this->operations[$name], $this); + } + + return $this->operations[$name]; + } + + /** + * Add a operation to the service description + * + * @param OperationInterface $operation Operation to add + * + * @return self + */ + public function addOperation(OperationInterface $operation) + { + $this->operations[$operation->getName()] = $operation->setServiceDescription($this); + + return $this; + } + + public function getModel($id) + { + if (!isset($this->models[$id])) { + return null; + } + + if (!($this->models[$id] instanceof Parameter)) { + $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this); + } + + return $this->models[$id]; + } + + public function getModels() + { + // Ensure all models are converted into parameter objects + foreach (array_keys($this->models) as $id) { + $this->getModel($id); + } + + return $this->models; + } + + public function hasModel($id) + { + return isset($this->models[$id]); + } + + /** + * Add a model to the service description + * + * @param Parameter $model Model to add + * + * @return self + */ + public function addModel(Parameter $model) + { + $this->models[$model->getName()] = $model; + + return $this; + } + + public function getApiVersion() + { + return $this->apiVersion; + } + + public function getName() + { + return $this->name; + } + + public function getDescription() + { + return $this->description; + } + + public function getData($key) + { + return isset($this->extraData[$key]) ? $this->extraData[$key] : null; + } + + public function setData($key, $value) + { + $this->extraData[$key] = $value; + + return $this; + } + + /** + * Initialize the state from an array + * + * @param array $config Configuration data + * @throws InvalidArgumentException + */ + protected function fromArray(array $config) + { + // Keep a list of default keys used in service descriptions that is later used to determine extra data keys + static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description'); + // Pull in the default configuration values + foreach ($defaultKeys as $key) { + if (isset($config[$key])) { + $this->{$key} = $config[$key]; + } + } + + // Account for the Swagger name for Guzzle's baseUrl + if (isset($config['basePath'])) { + $this->baseUrl = $config['basePath']; + } + + // Ensure that the models and operations properties are always arrays + $this->models = (array) $this->models; + $this->operations = (array) $this->operations; + + // We want to add operations differently than adding the other properties + $defaultKeys[] = 'operations'; + + // Create operations for each operation + if (isset($config['operations'])) { + foreach ($config['operations'] as $name => $operation) { + if (!($operation instanceof Operation) && !is_array($operation)) { + throw new InvalidArgumentException('Invalid operation in service description: ' + . gettype($operation)); + } + $this->operations[$name] = $operation; + } + } + + // Get all of the additional properties of the service description and store them in a data array + foreach (array_diff(array_keys($config), $defaultKeys) as $key) { + $this->extraData[$key] = $config[$key]; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php new file mode 100755 index 000000000..5983e586b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php @@ -0,0 +1,106 @@ + $op) { + $name = $op['name'] = isset($op['name']) ? $op['name'] : $name; + // Extend other operations + if (!empty($op['extends'])) { + $this->resolveExtension($name, $op, $operations); + } + $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array(); + $operations[$name] = $op; + } + } + + return new ServiceDescription(array( + 'apiVersion' => isset($config['apiVersion']) ? $config['apiVersion'] : null, + 'baseUrl' => isset($config['baseUrl']) ? $config['baseUrl'] : null, + 'description' => isset($config['description']) ? $config['description'] : null, + 'operations' => $operations, + 'models' => isset($config['models']) ? $config['models'] : null + ) + $config); + } + + /** + * @param string $name Name of the operation + * @param array $op Operation value array + * @param array $operations Currently loaded operations + * @throws DescriptionBuilderException when extending a non-existent operation + */ + protected function resolveExtension($name, array &$op, array &$operations) + { + $resolved = array(); + $original = empty($op['parameters']) ? false: $op['parameters']; + $hasClass = !empty($op['class']); + foreach ((array) $op['extends'] as $extendedCommand) { + if (empty($operations[$extendedCommand])) { + throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}"); + } + $toArray = $operations[$extendedCommand]; + $resolved = empty($resolved) + ? $toArray['parameters'] + : array_merge($resolved, $toArray['parameters']); + + $op = $op + $toArray; + if (!$hasClass && isset($toArray['class'])) { + $op['class'] = $toArray['class']; + } + } + $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php new file mode 100755 index 000000000..94ca77da4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php @@ -0,0 +1,28 @@ +getMessage(), $e->getCode(), $e->getPrevious()); + $ce->setSuccessfulRequests($e->getSuccessfulRequests()); + + $alreadyAddedExceptions = array(); + foreach ($e->getFailedRequests() as $request) { + if ($re = $e->getExceptionForFailedRequest($request)) { + $alreadyAddedExceptions[] = $re; + $ce->addFailedRequestWithException($request, $re); + } else { + $ce->addFailedRequest($request); + } + } + + // Add any exceptions that did not map to a request + if (count($alreadyAddedExceptions) < count($e)) { + foreach ($e as $ex) { + if (!in_array($ex, $alreadyAddedExceptions)) { + $ce->add($ex); + } + } + } + + return $ce; + } + + /** + * Get all of the commands in the transfer + * + * @return array + */ + public function getAllCommands() + { + return array_merge($this->successfulCommands, $this->failedCommands); + } + + /** + * Add to the array of successful commands + * + * @param CommandInterface $command Successful command + * + * @return self + */ + public function addSuccessfulCommand(CommandInterface $command) + { + $this->successfulCommands[] = $command; + + return $this; + } + + /** + * Add to the array of failed commands + * + * @param CommandInterface $command Failed command + * + * @return self + */ + public function addFailedCommand(CommandInterface $command) + { + $this->failedCommands[] = $command; + + return $this; + } + + /** + * Get an array of successful commands + * + * @return array + */ + public function getSuccessfulCommands() + { + return $this->successfulCommands; + } + + /** + * Get an array of failed commands + * + * @return array + */ + public function getFailedCommands() + { + return $this->failedCommands; + } + + /** + * Get the Exception that caused the given $command to fail + * + * @param CommandInterface $command Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedCommand(CommandInterface $command) + { + return $this->getExceptionForFailedRequest($command->getRequest()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php new file mode 100755 index 000000000..1407e5687 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php @@ -0,0 +1,7 @@ +invalidCommands = $commands; + parent::__construct( + 'Encountered commands in a batch transfer that use inconsistent clients. The batching ' . + 'strategy you use with a command transfer must divide command batches by client.' + ); + } + + /** + * Get the invalid commands + * + * @return array + */ + public function getCommands() + { + return $this->invalidCommands; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php new file mode 100755 index 000000000..d59ff2185 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php @@ -0,0 +1,9 @@ +errors = $errors; + } + + /** + * Get any validation errors + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php new file mode 100755 index 000000000..21140e772 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php @@ -0,0 +1,37 @@ +canBuild($command)) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + $className = $this->getClassName($command); + + return new $className($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return (bool) $this->getClassName($command); + } + + /** + * Get the name of the class to instantiate for the command + * + * @param CommandInterface $command Command that is associated with the iterator + * + * @return string + */ + abstract protected function getClassName(CommandInterface $command); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php new file mode 100755 index 000000000..2efc133c6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php @@ -0,0 +1,67 @@ +factories = $factories; + } + + public function build(CommandInterface $command, array $options = array()) + { + if (!($factory = $this->getFactory($command))) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + return $factory->build($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return $this->getFactory($command) !== false; + } + + /** + * Add a factory to the composite factory + * + * @param ResourceIteratorFactoryInterface $factory Factory to add + * + * @return self + */ + public function addFactory(ResourceIteratorFactoryInterface $factory) + { + $this->factories[] = $factory; + + return $this; + } + + /** + * Get the factory that matches the command object + * + * @param CommandInterface $command Command retrieving the iterator for + * + * @return ResourceIteratorFactoryInterface|bool + */ + protected function getFactory(CommandInterface $command) + { + foreach ($this->factories as $factory) { + if ($factory->canBuild($command)) { + return $factory; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php new file mode 100755 index 000000000..c71ca9d85 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php @@ -0,0 +1,34 @@ +map = $map; + } + + public function getClassName(CommandInterface $command) + { + $className = $command->getName(); + + if (isset($this->map[$className])) { + return $this->map[$className]; + } elseif (isset($this->map['*'])) { + // If a wildcard was added, then always use that + return $this->map['*']; + } + + return null; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php new file mode 100755 index 000000000..2322434a5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php @@ -0,0 +1,64 @@ +data = $data; + $this->structure = $structure; + } + + /** + * Get the structure of the model + * + * @return Parameter + */ + public function getStructure() + { + return $this->structure ?: new Parameter(); + } + + /** + * Provides debug information about the model object + * + * @return string + */ + public function __toString() + { + $output = 'Debug output of '; + if ($this->structure) { + $output .= $this->structure->getName() . ' '; + } + $output .= 'model'; + $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n"; + $output .= "Model data\n-----------\n\n"; + $output .= "This data can be retrieved from the model object using the get() method of the model " + . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n"; + $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1); + $output .= implode("\n", $lines); + + if ($this->structure) { + $output .= "\n\nModel structure\n---------------\n\n"; + $output .= "The following JSON document defines how the model was parsed from an HTTP response into the " + . "associative array structure you see above.\n\n"; + $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n"; + } + + return $output . "\n"; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php new file mode 100755 index 000000000..e14152432 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php @@ -0,0 +1,254 @@ +originalCommand = $command; + + // Parse options from the array of options + $this->data = $data; + $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0; + $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false; + } + + /** + * Get all of the resources as an array (Warning: this could issue a large number of requests) + * + * @return array + */ + public function toArray() + { + return iterator_to_array($this, false); + } + + public function setLimit($limit) + { + $this->limit = $limit; + $this->resetState(); + + return $this; + } + + public function setPageSize($pageSize) + { + $this->pageSize = $pageSize; + $this->resetState(); + + return $this; + } + + /** + * Get an option from the iterator + * + * @param string $key Key of the option to retrieve + * + * @return mixed|null Returns NULL if not set or the value if set + */ + public function get($key) + { + return array_key_exists($key, $this->data) ? $this->data[$key] : null; + } + + /** + * Set an option on the iterator + * + * @param string $key Key of the option to set + * @param mixed $value Value to set for the option + * + * @return ResourceIterator + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + public function current() + { + return $this->resources ? current($this->resources) : false; + } + + public function key() + { + return max(0, $this->iteratedCount - 1); + } + + public function count() + { + return $this->retrievedCount; + } + + /** + * Get the total number of requests sent + * + * @return int + */ + public function getRequestCount() + { + return $this->requestCount; + } + + /** + * Rewind the Iterator to the first element and send the original command + */ + public function rewind() + { + // Use the original command + $this->command = clone $this->originalCommand; + $this->resetState(); + $this->next(); + } + + public function valid() + { + return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken) + && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + public function next() + { + $this->iteratedCount++; + + // Check if a new set of resources needs to be retrieved + $sendRequest = false; + if (!$this->resources) { + $sendRequest = true; + } else { + // iterate over the internal array + $current = next($this->resources); + $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + if ($sendRequest) { + + $this->dispatch('resource_iterator.before_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + + // Get a new command object from the original command + $this->command = clone $this->originalCommand; + // Send a request and retrieve the newly loaded resources + $this->resources = $this->sendRequest(); + $this->requestCount++; + + // If no resources were found, then the last request was not needed + // and iteration must stop + if (empty($this->resources)) { + $this->invalid = true; + } else { + // Add to the number of retrieved resources + $this->retrievedCount += count($this->resources); + // Ensure that we rewind to the beginning of the array + reset($this->resources); + } + + $this->dispatch('resource_iterator.after_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + } + } + + /** + * Retrieve the NextToken that can be used in other iterators. + * + * @return string Returns a NextToken + */ + public function getNextToken() + { + return $this->nextToken; + } + + /** + * Returns the value that should be specified for the page size for a request that will maintain any hard limits, + * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit + * + * @return int Returns the page size of the next request. + */ + protected function calculatePageSize() + { + if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) { + return 1 + ($this->limit - $this->iteratedCount); + } + + return (int) $this->pageSize; + } + + /** + * Reset the internal state of the iterator without triggering a rewind() + */ + protected function resetState() + { + $this->iteratedCount = 0; + $this->retrievedCount = 0; + $this->nextToken = false; + $this->resources = null; + $this->invalid = false; + } + + /** + * Send a request to retrieve the next page of results. Hook for subclasses to implement. + * + * @return array Returns the newly loaded resources + */ + abstract protected function sendRequest(); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php new file mode 100755 index 000000000..6aa36153f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php @@ -0,0 +1,111 @@ +iterator = $iterator; + $this->callback = $callback; + Version::warn(__CLASS__ . ' is deprecated'); + } + + /** + * Apply the callback to the contents of the resource iterator + * + * @param int $perBatch The number of records to group per batch transfer + * + * @return int Returns the number of iterated resources + */ + public function apply($perBatch = 50) + { + $this->iterated = $this->batches = $batches = 0; + $that = $this; + $it = $this->iterator; + $callback = $this->callback; + + $batch = BatchBuilder::factory() + ->createBatchesWith(new BatchSizeDivisor($perBatch)) + ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) { + $batches++; + $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch)); + call_user_func_array($callback, array($it, $batch)); + $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch)); + })) + ->autoFlushAt($perBatch) + ->build(); + + $this->dispatch('iterator_batch.created_batch', array('batch' => $batch)); + + foreach ($this->iterator as $resource) { + $this->iterated++; + $batch->add($resource); + } + + $batch->flush(); + $this->batches = $batches; + + return $this->iterated; + } + + /** + * Get the total number of batches sent + * + * @return int + */ + public function getBatchCount() + { + return $this->batches; + } + + /** + * Get the total number of iterated resources + * + * @return int + */ + public function getIteratedCount() + { + return $this->iterated; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php new file mode 100755 index 000000000..2fd998071 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php @@ -0,0 +1,60 @@ + AbcFoo). + */ +class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory +{ + /** @var array List of namespaces used to look for classes */ + protected $namespaces; + + /** @var InflectorInterface Inflector used to determine class names */ + protected $inflector; + + /** + * @param string|array $namespaces List of namespaces for iterator objects + * @param InflectorInterface $inflector Inflector used to resolve class names + */ + public function __construct($namespaces = array(), InflectorInterface $inflector = null) + { + $this->namespaces = (array) $namespaces; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + /** + * Registers a namespace to check for Iterators + * + * @param string $namespace Namespace which contains Iterator classes + * + * @return self + */ + public function registerNamespace($namespace) + { + array_unshift($this->namespaces, $namespace); + + return $this; + } + + protected function getClassName(CommandInterface $command) + { + $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator'; + + // Determine the name of the class to load + foreach ($this->namespaces as $namespace) { + $potentialClassName = $namespace . '\\' . $iteratorName; + if (class_exists($potentialClassName)) { + return $potentialClassName; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php new file mode 100755 index 000000000..8b4e8dbe0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php @@ -0,0 +1,30 @@ +=5.3.2", + "guzzle/cache": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Service": "" } + }, + "target-dir": "Guzzle/Service", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php new file mode 100755 index 000000000..d115fd890 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php @@ -0,0 +1,284 @@ +contextOptions = stream_context_get_options($context); + $this->context = $context; + } elseif (is_array($context) || !$context) { + $this->contextOptions = $context; + $this->createContext($params); + } elseif ($context) { + throw new InvalidArgumentException('$context must be an array or resource'); + } + + // Dispatch the before send event + $request->dispatch('request.before_send', array( + 'request' => $request, + 'context' => $this->context, + 'context_options' => $this->contextOptions + )); + + $this->setUrl($request); + $this->addDefaultContextOptions($request); + $this->addSslOptions($request); + $this->addBodyOptions($request); + $this->addProxyOptions($request); + + // Create the file handle but silence errors + return $this->createStream($params) + ->setCustomData('request', $request) + ->setCustomData('response_headers', $this->getLastResponseHeaders()); + } + + /** + * Set an option on the context and the internal options array + * + * @param string $wrapper Stream wrapper name of http + * @param string $name Context name + * @param mixed $value Context value + * @param bool $overwrite Set to true to overwrite an existing value + */ + protected function setContextValue($wrapper, $name, $value, $overwrite = false) + { + if (!isset($this->contextOptions[$wrapper])) { + $this->contextOptions[$wrapper] = array($name => $value); + } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) { + return; + } + $this->contextOptions[$wrapper][$name] = $value; + stream_context_set_option($this->context, $wrapper, $name, $value); + } + + /** + * Create a stream context + * + * @param array $params Parameter array + */ + protected function createContext(array $params) + { + $options = $this->contextOptions; + $this->context = $this->createResource(function () use ($params, $options) { + return stream_context_create($options, $params); + }); + } + + /** + * Get the last response headers received by the HTTP request + * + * @return array + */ + public function getLastResponseHeaders() + { + return $this->lastResponseHeaders; + } + + /** + * Adds the default context options to the stream context options + * + * @param RequestInterface $request Request + */ + protected function addDefaultContextOptions(RequestInterface $request) + { + $this->setContextValue('http', 'method', $request->getMethod()); + $headers = $request->getHeaderLines(); + + // "Connection: close" is required to get streams to work in HTTP 1.1 + if (!$request->hasHeader('Connection')) { + $headers[] = 'Connection: close'; + } + + $this->setContextValue('http', 'header', $headers); + $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion()); + $this->setContextValue('http', 'ignore_errors', true); + } + + /** + * Set the URL to use with the factory + * + * @param RequestInterface $request Request that owns the URL + */ + protected function setUrl(RequestInterface $request) + { + $this->url = $request->getUrl(true); + + // Check for basic Auth username + if ($request->getUsername()) { + $this->url->setUsername($request->getUsername()); + } + + // Check for basic Auth password + if ($request->getPassword()) { + $this->url->setPassword($request->getPassword()); + } + } + + /** + * Add SSL options to the stream context + * + * @param RequestInterface $request Request + */ + protected function addSslOptions(RequestInterface $request) + { + if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) { + $this->setContextValue('ssl', 'verify_peer', true, true); + if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) { + $this->setContextValue('ssl', 'cafile', $cafile, true); + } + } else { + $this->setContextValue('ssl', 'verify_peer', false, true); + } + } + + /** + * Add body (content) specific options to the context options + * + * @param RequestInterface $request + */ + protected function addBodyOptions(RequestInterface $request) + { + // Add the content for the request if needed + if (!($request instanceof EntityEnclosingRequestInterface)) { + return; + } + + if (count($request->getPostFields())) { + $this->setContextValue('http', 'content', (string) $request->getPostFields(), true); + } elseif ($request->getBody()) { + $this->setContextValue('http', 'content', (string) $request->getBody(), true); + } + + // Always ensure a content-length header is sent + if (isset($this->contextOptions['http']['content'])) { + $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array(); + $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']); + $this->setContextValue('http', 'header', $headers, true); + } + } + + /** + * Add proxy parameters to the context if needed + * + * @param RequestInterface $request Request + */ + protected function addProxyOptions(RequestInterface $request) + { + if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) { + $this->setContextValue('http', 'proxy', $proxy); + } + } + + /** + * Create the stream for the request with the context options + * + * @param array $params Parameters of the stream + * + * @return StreamInterface + */ + protected function createStream(array $params) + { + $http_response_header = null; + $url = $this->url; + $context = $this->context; + $fp = $this->createResource(function () use ($context, $url, &$http_response_header) { + return fopen((string) $url, 'r', false, $context); + }); + + // Determine the class to instantiate + $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream'; + + /** @var $stream StreamInterface */ + $stream = new $className($fp); + + // Track the response headers of the request + if (isset($http_response_header)) { + $this->lastResponseHeaders = $http_response_header; + $this->processResponseHeaders($stream); + } + + return $stream; + } + + /** + * Process response headers + * + * @param StreamInterface $stream + */ + protected function processResponseHeaders(StreamInterface $stream) + { + // Set the size on the stream if it was returned in the response + foreach ($this->lastResponseHeaders as $header) { + if ((stripos($header, 'Content-Length:')) === 0) { + $stream->setSize(trim(substr($header, 15))); + } + } + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Closure to invoke that must return a valid resource + * + * @return resource + * @throws RuntimeException on error + */ + protected function createResource($callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = array( + 'message' => $msg, + 'file' => $file, + 'line' => $line + ); + return true; + }); + $resource = call_user_func($callback); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource. '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new RuntimeException(trim($message)); + } + + return $resource; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php new file mode 100755 index 000000000..12bed268d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php @@ -0,0 +1,289 @@ + array( + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true + ), + 'write' => array( + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ) + ); + + /** + * @param resource $stream Stream resource to wrap + * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream. + * + * @throws InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $size = null) + { + $this->setStream($stream, $size); + } + + /** + * Closes the stream when the helper is destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { + return ''; + } + + $originalPos = $this->ftell(); + $body = stream_get_contents($this->stream, -1, 0); + $this->seek($originalPos); + + return $body; + } + + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->cache[self::IS_READABLE] = false; + $this->cache[self::IS_WRITABLE] = false; + } + + /** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return bool|string Returns false on failure or a hash string on success + */ + public static function getHash(StreamInterface $stream, $algo, $rawOutput = false) + { + $pos = $stream->ftell(); + if (!$stream->seek(0)) { + return false; + } + + $ctx = hash_init($algo); + while (!$stream->feof()) { + hash_update($ctx, $stream->read(8192)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; + } + + public function getMetaData($key = null) + { + $meta = stream_get_meta_data($this->stream); + + return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null); + } + + public function getStream() + { + return $this->stream; + } + + public function setStream($stream, $size = null) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Stream must be a resource'); + } + + $this->size = $size; + $this->stream = $stream; + $this->rebuildCache(); + + return $this; + } + + public function detachStream() + { + $this->stream = null; + + return $this; + } + + public function getWrapper() + { + return $this->cache[self::WRAPPER_TYPE]; + } + + public function getWrapperData() + { + return $this->getMetaData('wrapper_data') ?: array(); + } + + public function getStreamType() + { + return $this->cache[self::STREAM_TYPE]; + } + + public function getUri() + { + return $this->cache['uri']; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + // If the stream is a file based stream and local, then use fstat + clearstatcache(true, $this->cache['uri']); + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) { + // Only get the size based on the content if the the stream is readable and seekable + $pos = $this->ftell(); + $this->size = strlen((string) $this); + $this->seek($pos); + return $this->size; + } + + return false; + } + + public function isReadable() + { + return $this->cache[self::IS_READABLE]; + } + + public function isRepeatable() + { + return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]; + } + + public function isWritable() + { + return $this->cache[self::IS_WRITABLE]; + } + + public function isConsumed() + { + return feof($this->stream); + } + + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->cache[self::IS_LOCAL]; + } + + public function isSeekable() + { + return $this->cache[self::SEEKABLE]; + } + + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false; + } + + public function read($length) + { + return fread($this->stream, $length); + } + + public function write($string) + { + // We can't know the size after writing anything + $this->size = null; + + return fwrite($this->stream, $string); + } + + public function ftell() + { + return ftell($this->stream); + } + + public function rewind() + { + return $this->seek(0); + } + + public function readLine($maxLength = null) + { + if (!$this->cache[self::IS_READABLE]) { + return false; + } else { + return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream()); + } + } + + public function setCustomData($key, $value) + { + $this->customData[$key] = $value; + + return $this; + } + + public function getCustomData($key) + { + return isset($this->customData[$key]) ? $this->customData[$key] : null; + } + + /** + * Reprocess stream metadata + */ + protected function rebuildCache() + { + $this->cache = stream_get_meta_data($this->stream); + $this->cache[self::IS_LOCAL] = stream_is_local($this->stream); + $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]); + $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php new file mode 100755 index 000000000..6d7dc3761 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php @@ -0,0 +1,218 @@ +=5.3.2", + "guzzle/common": "self.version" + }, + "suggest": { + "guzzle/http": "To convert Guzzle request objects to PHP streams" + }, + "autoload": { + "psr-0": { "Guzzle\\Stream": "" } + }, + "target-dir": "Guzzle/Stream", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php new file mode 100755 index 000000000..951738d2a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php @@ -0,0 +1,33 @@ +getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + + $decoratorA = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($batch)) + ->getMockForAbstractClass(); + + $decoratorB = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($decoratorA)) + ->getMockForAbstractClass(); + + $decoratorA->add('foo'); + $this->assertFalse($decoratorB->isEmpty()); + $this->assertFalse($batch->isEmpty()); + $this->assertEquals(array($decoratorB, $decoratorA), $decoratorB->getDecorators()); + $this->assertEquals(array(), $decoratorB->flush()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php new file mode 100755 index 000000000..4da09d30e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php @@ -0,0 +1,86 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + } + + private function getMockBatchBuilder() + { + return BatchBuilder::factory() + ->transferWith($this->getMockTransfer()) + ->createBatchesWith($this->getMockDivisor()); + } + + public function testFactoryCreatesInstance() + { + $builder = BatchBuilder::factory(); + $this->assertInstanceOf('Guzzle\Batch\BatchBuilder', $builder); + } + + public function testAddsAutoFlush() + { + $batch = $this->getMockBatchBuilder()->autoFlushAt(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\FlushingBatch', $batch); + } + + public function testAddsExceptionBuffering() + { + $batch = $this->getMockBatchBuilder()->bufferExceptions()->build(); + $this->assertInstanceOf('Guzzle\Batch\ExceptionBufferingBatch', $batch); + } + + public function testAddHistory() + { + $batch = $this->getMockBatchBuilder()->keepHistory()->build(); + $this->assertInstanceOf('Guzzle\Batch\HistoryBatch', $batch); + } + + public function testAddsNotify() + { + $batch = $this->getMockBatchBuilder()->notify(function() {})->build(); + $this->assertInstanceOf('Guzzle\Batch\NotifyingBatch', $batch); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testTransferStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->createBatchesWith($this->getMockDivisor())->build(); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testDivisorStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->transferWith($this->getMockTransfer())->build(); + } + + public function testTransfersRequests() + { + $batch = BatchBuilder::factory()->transferRequests(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\BatchRequestTransfer', $this->readAttribute($batch, 'transferStrategy')); + } + + public function testTransfersCommands() + { + $batch = BatchBuilder::factory()->transferCommands(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\BatchCommandTransfer', $this->readAttribute($batch, 'transferStrategy')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php new file mode 100755 index 000000000..753db7dab --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php @@ -0,0 +1,36 @@ +createBatches($queue); + $this->assertEquals(array(array('foo'), array('baz')), $batches); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php new file mode 100755 index 000000000..6ba7ae052 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php @@ -0,0 +1,52 @@ +itemsTransferred = null; + $itemsTransferred =& $this->itemsTransferred; + + $this->transferStrategy = new BatchClosureTransfer(function (array $batch) use (&$itemsTransferred) { + $itemsTransferred = $batch; + return; + }); + } + + public function testTransfersBatch() + { + $batchedItems = array('foo', 'bar', 'baz'); + $this->transferStrategy->transfer($batchedItems); + + $this->assertEquals($batchedItems, $this->itemsTransferred); + } + + public function testTransferBailsOnEmptyBatch() + { + $batchedItems = array(); + $this->transferStrategy->transfer($batchedItems); + + $this->assertNull($this->itemsTransferred); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsCallable() + { + $foo = new BatchClosureTransfer('uh oh!'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php new file mode 100755 index 000000000..a04efabbb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php @@ -0,0 +1,83 @@ + $command) { + if ($i % 2) { + $command->setClient($client1); + } else { + $command->setClient($client2); + } + $queue[] = $command; + } + + $batch = new BatchCommandTransfer(2); + $this->assertEquals(array( + array($commands[0], $commands[2]), + array($commands[4]), + array($commands[1], $commands[3]) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreCommands() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchCommandTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = $this->getMockBuilder('Guzzle\Service\Client') + ->setMethods(array('send')) + ->getMock(); + $client->expects($this->once()) + ->method('send'); + $command = new Mc(); + $command->setClient($client); + $batch = new BatchCommandTransfer(2); + $batch->transfer(array($command)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchCommandTransfer(2); + $batch->transfer(array()); + } + + /** + * @expectedException Guzzle\Service\Exception\InconsistentClientTransferException + */ + public function testEnsuresAllCommandsUseTheSameClient() + { + $batch = new BatchCommandTransfer(2); + $client1 = new Client('http://www.example.com'); + $client2 = new Client('http://www.example.com'); + $command1 = new Mc(); + $command1->setClient($client1); + $command2 = new Mc(); + $command2->setClient($client2); + $batch->transfer(array($command1, $command2)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php new file mode 100755 index 000000000..dec7bd55e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php @@ -0,0 +1,80 @@ +setCurlMulti(new CurlMulti()); + + $client2 = new Client('http://www.example.com'); + $client2->setCurlMulti(new CurlMulti()); + + $request1 = $client1->get(); + $request2 = $client2->get(); + $request3 = $client1->get(); + $request4 = $client2->get(); + $request5 = $client1->get(); + + $queue = new \SplQueue(); + $queue[] = $request1; + $queue[] = $request2; + $queue[] = $request3; + $queue[] = $request4; + $queue[] = $request5; + + $batch = new BatchRequestTransfer(2); + $this->assertEquals(array( + array($request1, $request3), + array($request3), + array($request2, $request4) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreRequests() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchRequestTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = new Client('http://127.0.0.1:123'); + $request = $client->get(); + // For some reason... PHP unit clones the request, which emits a request.clone event. This causes the + // 'sorted' property of the event dispatcher to contain an array in the cloned request that is not present in + // the original. + $request->dispatch('request.clone'); + + $multi = $this->getMock('Guzzle\Http\Curl\CurlMultiInterface'); + $client->setCurlMulti($multi); + $multi->expects($this->once()) + ->method('add') + ->with($request); + $multi->expects($this->once()) + ->method('send'); + + $batch = new BatchRequestTransfer(2); + $batch->transfer(array($request)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchRequestTransfer(2); + $batch->transfer(array()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php new file mode 100755 index 000000000..5542228fd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php @@ -0,0 +1,24 @@ +assertEquals(3, $d->getSize()); + $d->setSize(2); + $batches = $d->createBatches($queue); + $this->assertEquals(array(array('foo', 'baz'), array('bar')), $batches); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php new file mode 100755 index 000000000..296f57aef --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php @@ -0,0 +1,91 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + } + + public function testAddsItemsToQueue() + { + $batch = new Batch($this->getMockTransfer(), $this->getMockDivisor()); + $this->assertSame($batch, $batch->add('foo')); + $this->assertEquals(1, count($batch)); + } + + public function testFlushReturnsItems() + { + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer'); + + $divisor = $this->getMockDivisor(); + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnValue(array(array('foo', 'baz'), array('bar')))); + + $batch = new Batch($transfer, $divisor); + + $batch->add('foo')->add('baz')->add('bar'); + $items = $batch->flush(); + + $this->assertEquals(array('foo', 'baz', 'bar'), $items); + } + + public function testThrowsExceptionContainingTheFailedBatch() + { + $called = 0; + $originalException = new \Exception('Foo!'); + + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer') + ->will($this->returnCallback(function () use (&$called, $originalException) { + if (++$called == 2) { + throw $originalException; + } + })); + + $divisor = $this->getMockDivisor(); + $batch = new Batch($transfer, $divisor); + + // PHPunit clones objects before passing them to a callback. + // Horrible hack to get around this! + $queue = $this->readAttribute($batch, 'queue'); + + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnCallback(function ($batch) use ($queue) { + foreach ($queue as $item) { + $items[] = $item; + } + return array_chunk($items, 2); + })); + + $batch->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertFalse($batch->isEmpty()); + + try { + $items = $batch->flush(); + $this->fail('Expected exception'); + } catch (BatchTransferException $e) { + $this->assertEquals($originalException, $e->getPrevious()); + $this->assertEquals(array('bar', 'bee'), array_values($e->getBatch())); + $this->assertEquals(1, count($batch)); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php new file mode 100755 index 000000000..fd810b11f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php @@ -0,0 +1,45 @@ +getMockBuilder('Guzzle\Batch\BatchTransferInterface') + ->setMethods(array('transfer')) + ->getMock(); + + $d = new BatchSizeDivisor(1); + $batch = new Batch($t, $d); + + $called = 0; + $t->expects($this->exactly(3)) + ->method('transfer') + ->will($this->returnCallback(function ($batch) use (&$called) { + if (++$called === 2) { + throw new \Exception('Foo'); + } + })); + + $decorator = new ExceptionBufferingBatch($batch); + $decorator->add('foo')->add('baz')->add('bar'); + $result = $decorator->flush(); + + $e = $decorator->getExceptions(); + $this->assertEquals(1, count($e)); + $this->assertEquals(array('baz'), $e[0]->getBatch()); + + $decorator->clearExceptions(); + $this->assertEquals(0, count($decorator->getExceptions())); + + $this->assertEquals(array('foo', 'bar'), $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php new file mode 100755 index 000000000..9b37a485f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php @@ -0,0 +1,40 @@ +getMock('Guzzle\Batch\BatchTransferInterface', array('transfer')); + $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface', array('createBatches')); + + $batch = new Batch($t, $d); + $queue = $this->readAttribute($batch, 'queue'); + + $d->expects($this->exactly(2)) + ->method('createBatches') + ->will($this->returnCallback(function () use ($queue) { + $items = array(); + foreach ($queue as $item) { + $items[] = $item; + } + return array($items); + })); + + $t->expects($this->exactly(2)) + ->method('transfer'); + + $flush = new FlushingBatch($batch, 3); + $this->assertEquals(3, $flush->getThreshold()); + $flush->setThreshold(2); + $flush->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertEquals(1, count($flush)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php new file mode 100755 index 000000000..60d6f951a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php @@ -0,0 +1,26 @@ +getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + + $history = new HistoryBatch($batch); + $history->add('foo')->add('baz'); + $this->assertEquals(array('foo', 'baz'), $history->getHistory()); + $history->clearHistory(); + $this->assertEquals(array(), $history->getHistory()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php new file mode 100755 index 000000000..69a89007a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Batch\Batch', array('flush'), array( + $this->getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + )); + + $batch->expects($this->once()) + ->method('flush') + ->will($this->returnValue(array('foo', 'baz'))); + + $data = array(); + $decorator = new NotifyingBatch($batch, function ($batch) use (&$data) { + $data[] = $batch; + }); + + $decorator->add('foo')->add('baz'); + $decorator->flush(); + $this->assertEquals(array(array('foo', 'baz')), $data); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsValid() + { + $batch = new Batch( + $this->getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + $decorator = new NotifyingBatch($batch, 'foo'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php new file mode 100755 index 000000000..c4140a91d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php @@ -0,0 +1,64 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresConfigIsObject() + { + CacheAdapterFactory::fromCache(array()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresKnownType() + { + CacheAdapterFactory::fromCache(new \stdClass()); + } + + public function cacheProvider() + { + return array( + array(new DoctrineCacheAdapter(new ArrayCache()), 'Guzzle\Cache\DoctrineCacheAdapter'), + array(new ArrayCache(), 'Guzzle\Cache\DoctrineCacheAdapter'), + array(StorageFactory::factory(array('adapter' => 'memory')), 'Guzzle\Cache\Zf2CacheAdapter'), + ); + } + + /** + * @dataProvider cacheProvider + */ + public function testCreatesNullCacheAdapterByDefault($cache, $type) + { + $adapter = CacheAdapterFactory::fromCache($cache); + $this->assertInstanceOf($type, $adapter); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php new file mode 100755 index 000000000..3e30dddc5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php @@ -0,0 +1,68 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + public function testGetCacheObject() + { + $this->assertEquals($this->cache, $this->adapter->getCacheObject()); + } + + public function testSave() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + } + + public function testFetch() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testContains() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->contains('test')); + } + + public function testDelete() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->delete('test')); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php new file mode 100755 index 000000000..12de65b5a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php @@ -0,0 +1,94 @@ +callables = array( + 'contains' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data); + }, + 'delete' => function($id, $options = array()) use ($that) { + unset($that->data[$id]); + return true; + }, + 'fetch' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data) ? $that->data[$id] : null; + }, + 'save' => function($id, $data, $lifeTime, $options = array()) use ($that) { + $that->data[$id] = $data; + return true; + } + ); + + $this->adapter = new ClosureCacheAdapter($this->callables); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->cache = null; + $this->callables = null; + parent::tearDown(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testEnsuresCallablesArePresent() + { + $callables = $this->callables; + unset($callables['delete']); + $cache = new ClosureCacheAdapter($callables); + } + + public function testAllCallablesMustBePresent() + { + $cache = new ClosureCacheAdapter($this->callables); + } + + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php new file mode 100755 index 000000000..e05df3f78 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php @@ -0,0 +1,20 @@ +assertEquals(false, $c->contains('foo')); + $this->assertEquals(true, $c->delete('foo')); + $this->assertEquals(false, $c->fetch('foo')); + $this->assertEquals(true, $c->save('foo', 'bar')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php new file mode 100755 index 000000000..9077c12d3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php @@ -0,0 +1,58 @@ +cache = StorageFactory::factory(array( + 'adapter' => 'memory' + )); + $this->adapter = new Zf2CacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php new file mode 100755 index 000000000..19d12e6f6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php @@ -0,0 +1,63 @@ +assertEquals(array(), AbstractHasDispatcher::getAllEvents()); + } + + public function testAllowsDispatcherToBeInjected() + { + $d = new EventDispatcher(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertSame($mock, $mock->setEventDispatcher($d)); + $this->assertSame($d, $mock->getEventDispatcher()); + } + + public function testCreatesDefaultEventDispatcherIfNeeded() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventDispatcher', $mock->getEventDispatcher()); + } + + public function testHelperDispatchesEvents() + { + $data = array(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $mock->getEventDispatcher()->addListener('test', function(Event $e) use (&$data) { + $data = $e->getIterator()->getArrayCopy(); + }); + $mock->dispatch('test', array( + 'param' => 'abc' + )); + $this->assertEquals(array( + 'param' => 'abc', + ), $data); + } + + public function testHelperAttachesSubscribers() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $subscriber = $this->getMockForAbstractClass('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') + ->setMethods(array('addSubscriber')) + ->getMock(); + + $dispatcher->expects($this->once()) + ->method('addSubscriber'); + + $mock->setEventDispatcher($dispatcher); + $mock->addSubscriber($subscriber); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php new file mode 100755 index 000000000..0648a02b8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php @@ -0,0 +1,529 @@ +coll = new Collection(); + } + + public function testConstructorCanBeCalledWithNoParams() + { + $this->coll = new Collection(); + $p = $this->coll->getAll(); + $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); + } + + public function testConstructorCanBeCalledWithParams() + { + $testData = array( + 'test' => 'value', + 'test_2' => 'value2' + ); + $this->coll = new Collection($testData); + $this->assertEquals($this->coll->getAll(), $testData, '-> getAll() must return the data passed in the constructor'); + $this->assertEquals($this->coll->getAll(), $this->coll->toArray()); + } + + public function testImplementsIteratorAggregate() + { + $this->coll->set('key', 'value'); + $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); + $this->assertEquals(1, count($this->coll)); + $total = 0; + foreach ($this->coll as $key => $value) { + $this->assertEquals('key', $key); + $this->assertEquals('value', $value); + $total++; + } + $this->assertEquals(1, $total); + } + + public function testCanAddValuesToExistingKeysByUsingArray() + { + $this->coll->add('test', 'value1'); + $this->assertEquals($this->coll->getAll(), array('test' => 'value1')); + $this->coll->add('test', 'value2'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2'))); + $this->coll->add('test', 'value3'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2', 'value3'))); + } + + public function testHandlesMergingInDisparateDataSources() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + $this->coll->merge($params); + $this->assertEquals($this->coll->getAll(), $params); + + // Pass the same object to itself + $this->assertEquals($this->coll->merge($this->coll), $this->coll); + } + + public function testCanClearAllDataOrSpecificKeys() + { + $this->coll->merge(array( + 'test' => 'value1', + 'test2' => 'value2' + )); + + // Clear a specific parameter by name + $this->coll->remove('test'); + + $this->assertEquals($this->coll->getAll(), array( + 'test2' => 'value2' + )); + + // Clear all parameters + $this->coll->clear(); + + $this->assertEquals($this->coll->getAll(), array()); + } + + public function testGetsValuesByKey() + { + $this->assertNull($this->coll->get('test')); + $this->coll->add('test', 'value'); + $this->assertEquals('value', $this->coll->get('test')); + $this->coll->set('test2', 'v2'); + $this->coll->set('test3', 'v3'); + $this->assertEquals(array( + 'test' => 'value', + 'test2' => 'v2' + ), $this->coll->getAll(array('test', 'test2'))); + } + + public function testProvidesKeys() + { + $this->assertEquals(array(), $this->coll->getKeys()); + $this->coll->merge(array( + 'test1' => 'value1', + 'test2' => 'value2' + )); + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + // Returns the cached array previously returned + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + $this->coll->remove('test1'); + $this->assertEquals(array('test2'), $this->coll->getKeys()); + $this->coll->add('test3', 'value3'); + $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); + } + + public function testChecksIfHasKey() + { + $this->assertFalse($this->coll->hasKey('test')); + $this->coll->add('test', 'value'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->coll->add('test2', 'value2'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->assertEquals(true, $this->coll->hasKey('test2')); + $this->assertFalse($this->coll->hasKey('testing')); + $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); + } + + public function testChecksIfHasValue() + { + $this->assertFalse($this->coll->hasValue('value')); + $this->coll->add('test', 'value'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->coll->add('test2', 'value2'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->assertEquals('test2', $this->coll->hasValue('value2')); + $this->assertFalse($this->coll->hasValue('val')); + } + + public function testCanGetAllValuesByArray() + { + $this->coll->add('foo', 'bar'); + $this->coll->add('tEsT', 'value'); + $this->coll->add('tesTing', 'v2'); + $this->coll->add('key', 'v3'); + $this->assertNull($this->coll->get('test')); + $this->assertEquals(array( + 'foo' => 'bar', + 'tEsT' => 'value', + 'tesTing' => 'v2' + ), $this->coll->getAll(array( + 'foo', 'tesTing', 'tEsT' + ))); + } + + public function testImplementsCount() + { + $data = new Collection(); + $this->assertEquals(0, $data->count()); + $data->add('key', 'value'); + $this->assertEquals(1, count($data)); + $data->add('key', 'value2'); + $this->assertEquals(1, count($data)); + $data->add('key_2', 'value3'); + $this->assertEquals(2, count($data)); + } + + public function testAddParamsByMerging() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + + // Add some parameters + $this->coll->merge($params); + + // Add more parameters by merging them in + $this->coll->merge(array( + 'test' => 'another', + 'different_key' => 'new value' + )); + + $this->assertEquals(array( + 'test' => array('value1', 'another'), + 'test2' => 'value2', + 'test3' => array('value3', 'value4'), + 'different_key' => 'new value' + ), $this->coll->getAll()); + } + + public function testAllowsFunctionalFilter() + { + $this->coll->merge(array( + 'fruit' => 'apple', + 'number' => 'ten', + 'prepositions' => array('about', 'above', 'across', 'after'), + 'same_number' => 'ten' + )); + + $filtered = $this->coll->filter(function($key, $value) { + return $value == 'ten'; + }); + + $this->assertNotEquals($filtered, $this->coll); + + $this->assertEquals(array( + 'number' => 'ten', + 'same_number' => 'ten' + ), $filtered->getAll()); + } + + public function testAllowsFunctionalMapping() + { + $this->coll->merge(array( + 'number_1' => 1, + 'number_2' => 2, + 'number_3' => 3 + )); + + $mapped = $this->coll->map(function($key, $value) { + return $value * $value; + }); + + $this->assertNotEquals($mapped, $this->coll); + + $this->assertEquals(array( + 'number_1' => 1, + 'number_2' => 4, + 'number_3' => 9 + ), $mapped->getAll()); + } + + public function testImplementsArrayAccess() + { + $this->coll->merge(array( + 'k1' => 'v1', + 'k2' => 'v2' + )); + + $this->assertTrue($this->coll->offsetExists('k1')); + $this->assertFalse($this->coll->offsetExists('Krull')); + + $this->coll->offsetSet('k3', 'v3'); + $this->assertEquals('v3', $this->coll->offsetGet('k3')); + $this->assertEquals('v3', $this->coll->get('k3')); + + $this->coll->offsetUnset('k1'); + $this->assertFalse($this->coll->offsetExists('k1')); + } + + public function testUsesStaticWhenCreatingNew() + { + $qs = new QueryString(array( + 'a' => 'b', + 'c' => 'd' + )); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->map(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->map(function($a, $b) {}, array(), false)); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->filter(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->filter(function($a, $b) {}, false)); + } + + public function testCanReplaceAllData() + { + $this->assertSame($this->coll, $this->coll->replace(array( + 'a' => '123' + ))); + + $this->assertEquals(array( + 'a' => '123' + ), $this->coll->getAll()); + } + + public function dataProvider() + { + return array( + array('this_is_a_test', '{a}_is_a_{b}', array( + 'a' => 'this', + 'b' => 'test' + )), + array('this_is_a_test', '{abc}_is_a_{0}', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', '{abc}_is_a_{0}', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', 'this_is_a_test', array( + 'abc' => 'this' + )), + array('{abc}_is_{not_found}a_{0}', '{abc}_is_{not_found}a_{0}', array()) + ); + } + + /** + * @dataProvider dataProvider + */ + public function testInjectsConfigData($output, $input, $config) + { + $collection = new Collection($config); + $this->assertEquals($output, $collection->inject($input)); + } + + public function testCanSearchByKey() + { + $collection = new Collection(array( + 'foo' => 'bar', + 'BaZ' => 'pho' + )); + + $this->assertEquals('foo', $collection->keySearch('FOO')); + $this->assertEquals('BaZ', $collection->keySearch('baz')); + $this->assertEquals(false, $collection->keySearch('Bar')); + } + + public function testPreparesFromConfig() + { + $c = Collection::fromConfig(array( + 'a' => '123', + 'base_url' => 'http://www.test.com/' + ), array( + 'a' => 'xyz', + 'b' => 'lol' + ), array('a')); + + $this->assertInstanceOf('Guzzle\Common\Collection', $c); + $this->assertEquals(array( + 'a' => '123', + 'b' => 'lol', + 'base_url' => 'http://www.test.com/' + ), $c->getAll()); + + try { + $c = Collection::fromConfig(array(), array(), array('a')); + $this->fail('Exception not throw when missing config'); + } catch (InvalidArgumentException $e) { + } + } + + function falseyDataProvider() + { + return array( + array(false, false), + array(null, null), + array('', ''), + array(array(), array()), + array(0, 0), + ); + } + + /** + * @dataProvider falseyDataProvider + */ + public function testReturnsCorrectData($a, $b) + { + $c = new Collection(array('value' => $a)); + $this->assertSame($b, $c->get('value')); + } + + public function testRetrievesNestedKeysUsingPath() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar' + ) + ) + ); + $collection = new Collection($data); + $this->assertEquals('bar', $collection->getPath('foo')); + $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); + $this->assertNull($collection->getPath('wewewf')); + $this->assertNull($collection->getPath('baz/mesa/jar/jar')); + } + + public function testFalseyKeysStillDescend() + { + $collection = new Collection(array( + '0' => array( + 'a' => 'jar' + ), + 1 => 'other' + )); + $this->assertEquals('jar', $collection->getPath('0/a')); + $this->assertEquals('other', $collection->getPath('1')); + } + + public function getPathProvider() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c') + ), + 'bar' => array( + 'baz' => 'bam', + 'array' => array('d', 'e', 'f') + ) + ), + 'bam' => array( + array('foo' => 1), + array('foo' => 2), + array('array' => array('h', 'i')) + ) + ); + $c = new Collection($data); + + return array( + // Simple path selectors + array($c, 'foo', 'bar'), + array($c, 'baz', $data['baz']), + array($c, 'bam', $data['bam']), + array($c, 'baz/mesa', $data['baz']['mesa']), + array($c, 'baz/mesa/jar', 'jar'), + // Merge everything two levels under baz + array($c, 'baz/*', array( + 'jar' => 'jar', + 'array' => array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array']), + 'baz' => 'bam' + )), + // Does not barf on missing keys + array($c, 'fefwfw', null), + // Does not barf when a wildcard does not resolve correctly + array($c, '*/*/*/*/*/wefwfe', array()), + // Allows custom separator + array($c, '*|mesa', $data['baz']['mesa'], '|'), + // Merge all 'array' keys two levels under baz (the trailing * does not hurt the results) + array($c, 'baz/*/array/*', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), + // Merge all 'array' keys two levels under baz + array($c, 'baz/*/array', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), + array($c, 'baz/mesa/array', $data['baz']['mesa']['array']), + // Having a trailing * does not hurt the results + array($c, 'baz/mesa/array/*', $data['baz']['mesa']['array']), + // Merge of anything one level deep + array($c, '*', array_merge(array('bar'), $data['baz'], $data['bam'])), + // Funky merge of anything two levels deep + array($c, '*/*', array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'), + 'baz' => 'bam', + 'foo' => array(1, 2) + )), + // Funky merge of all 'array' keys that are two levels deep + array($c, '*/*/array', array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i')) + ); + } + + /** + * @dataProvider getPathProvider + */ + public function testGetPath(Collection $c, $path, $expected, $separator = '/') + { + $this->assertEquals($expected, $c->getPath($path, $separator)); + } + + public function testOverridesSettings() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $c->overwriteWith(array('foo' => 10, 'bar' => 300)); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testOverwriteWithCollection() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testOverwriteWithTraversable() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b->getIterator()); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testCanSetNestedPathValueThatDoesNotExist() + { + $c = new Collection(array()); + $c->setPath('foo/bar/baz/123', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); + } + + public function testCanSetNestedPathValueThatExists() + { + $c = new Collection(array('foo' => array('bar' => 'test'))); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testVerifiesNestedPathIsValidAtExactLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testVerifiesThatNestedPathIsValidAtAnyLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar/baz', 'test'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php new file mode 100755 index 000000000..5484e1446 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php @@ -0,0 +1,62 @@ + '123', + 'other' => '456', + 'event' => 'test.notify' + )); + } + + public function testAllowsParameterInjection() + { + $event = new Event(array( + 'test' => '123' + )); + $this->assertEquals('123', $event['test']); + } + + public function testImplementsArrayAccess() + { + $event = $this->getEvent(); + $this->assertEquals('123', $event['test']); + $this->assertNull($event['foobar']); + + $this->assertTrue($event->offsetExists('test')); + $this->assertFalse($event->offsetExists('foobar')); + + unset($event['test']); + $this->assertFalse($event->offsetExists('test')); + + $event['test'] = 'new'; + $this->assertEquals('new', $event['test']); + } + + public function testImplementsIteratorAggregate() + { + $event = $this->getEvent(); + $this->assertInstanceOf('ArrayIterator', $event->getIterator()); + } + + public function testConvertsToArray() + { + $this->assertEquals(array( + 'test' => '123', + 'other' => '456', + 'event' => 'test.notify' + ), $this->getEvent()->toArray()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php new file mode 100755 index 000000000..c72a2a637 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php @@ -0,0 +1,21 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + $transferException = new BatchTransferException(array('foo'), array(1, 2), $e, $t, $d); + $this->assertEquals(array('foo'), $transferException->getBatch()); + $this->assertSame($t, $transferException->getTransferStrategy()); + $this->assertSame($d, $transferException->getDivisorStrategy()); + $this->assertSame($e, $transferException->getPrevious()); + $this->assertEquals(array(1, 2), $transferException->getTransferredItems()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php new file mode 100755 index 000000000..2aecf2a06 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php @@ -0,0 +1,66 @@ +getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $e->getMessage()); + $this->assertContains(" Test\n\n #0 ./", $e->getMessage()); + $this->assertSame($exceptions[0], $e->getFirst()); + } + + public function testCanSetExceptions() + { + $ex = new \Exception('foo'); + $e = new ExceptionCollection(); + $e->setExceptions(array($ex)); + $this->assertSame($ex, $e->getFirst()); + } + + public function testActsAsArray() + { + $e = new ExceptionCollection(); + $exceptions = $this->getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertEquals(2, count($e)); + $this->assertEquals($exceptions, $e->getIterator()->getArrayCopy()); + } + + public function testCanAddSelf() + { + $e1 = new ExceptionCollection(); + $e1->add(new \Exception("Test")); + $e2 = new ExceptionCollection('Meta description!'); + $e2->add(new \Exception("Test 2")); + $e3 = new ExceptionCollection(); + $e3->add(new \Exception('Baz')); + $e2->add($e3); + $e1->add($e2); + $message = $e1->getMessage(); + $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n Test\n\n #0 ", $message); + $this->assertContains("\n\n(Guzzle\\Common\\Exception\\ExceptionCollection) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n\n Meta description!\n\n", $message); + $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n Test 2\n\n #0 ", $message); + $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains(" Baz\n\n #0", $message); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php new file mode 100755 index 000000000..c3a81d1e4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php @@ -0,0 +1,27 @@ +isRunning()) { + self::$server->flush(); + } else { + self::$server->start(); + } + } + + return self::$server; + } + + /** + * Set the service builder to use for tests + * + * @param ServiceBuilderInterface $builder Service builder + */ + public static function setServiceBuilder(ServiceBuilderInterface $builder) + { + self::$serviceBuilder = $builder; + } + + /** + * Get a service builder object that can be used throughout the service tests + * + * @return ServiceBuilder + */ + public static function getServiceBuilder() + { + if (!self::$serviceBuilder) { + throw new RuntimeException('No service builder has been set via setServiceBuilder()'); + } + + return self::$serviceBuilder; + } + + /** + * Check if an event dispatcher has a subscriber + * + * @param HasDispatcherInterface $dispatcher + * @param EventSubscriberInterface $subscriber + * + * @return bool + */ + protected function hasSubscriber(HasDispatcherInterface $dispatcher, EventSubscriberInterface $subscriber) + { + $class = get_class($subscriber); + $all = array_keys(call_user_func(array($class, 'getSubscribedEvents'))); + + foreach ($all as $i => $event) { + foreach ($dispatcher->getEventDispatcher()->getListeners($event) as $e) { + if ($e[0] === $subscriber) { + unset($all[$i]); + break; + } + } + } + + return count($all) == 0; + } + + /** + * Get a wildcard observer for an event dispatcher + * + * @param HasDispatcherInterface $hasDispatcher + * + * @return MockObserver + */ + public function getWildcardObserver(HasDispatcherInterface $hasDispatcher) + { + $class = get_class($hasDispatcher); + $o = new MockObserver(); + $events = call_user_func(array($class, 'getAllEvents')); + foreach ($events as $event) { + $hasDispatcher->getEventDispatcher()->addListener($event, array($o, 'update')); + } + + return $o; + } + + /** + * Set the mock response base path + * + * @param string $path Path to mock response folder + * + * @return GuzzleTestCase + */ + public static function setMockBasePath($path) + { + self::$mockBasePath = $path; + } + + /** + * Mark a request as being mocked + * + * @param RequestInterface $request + * + * @return self + */ + public function addMockedRequest(RequestInterface $request) + { + $this->requests[] = $request; + + return $this; + } + + /** + * Get all of the mocked requests + * + * @return array + */ + public function getMockedRequests() + { + return $this->requests; + } + + /** + * Get a mock response for a client by mock file name + * + * @param string $path Relative path to the mock response file + * + * @return Response + */ + public function getMockResponse($path) + { + return $path instanceof Response + ? $path + : MockPlugin::getMockFile(self::$mockBasePath . DIRECTORY_SEPARATOR . $path); + } + + /** + * Set a mock response from a mock file on the next client request. + * + * This method assumes that mock response files are located under the + * Command/Mock/ directory of the Service being tested + * (e.g. Unfuddle/Command/Mock/). A mock response is added to the next + * request sent by the client. + * + * @param Client $client Client object to modify + * @param string $paths Path to files within the Mock folder of the service + * + * @return MockPlugin returns the created mock plugin + */ + public function setMockResponse(Client $client, $paths) + { + $this->requests = array(); + $that = $this; + $mock = new MockPlugin(null, true); + $client->getEventDispatcher()->removeSubscriber($mock); + $mock->getEventDispatcher()->addListener('mock.request', function(Event $event) use ($that) { + $that->addMockedRequest($event['request']); + }); + + if ($paths instanceof Response) { + // A single response instance has been specified, create an array with that instance + // as the only element for the following loop to work as expected + $paths = array($paths); + } + + foreach ((array) $paths as $path) { + $mock->addResponse($this->getMockResponse($path)); + } + + $client->getEventDispatcher()->addSubscriber($mock); + + return $mock; + } + + /** + * Compare HTTP headers and use special markup to filter values + * A header prefixed with '!' means it must not exist + * A header prefixed with '_' means it must be ignored + * A header value of '*' means anything after the * will be ignored + * + * @param array $filteredHeaders Array of special headers + * @param array $actualHeaders Array of headers to check against + * + * @return array|bool Returns an array of the differences or FALSE if none + */ + public function compareHeaders($filteredHeaders, $actualHeaders) + { + $comparison = new HeaderComparison(); + + return $comparison->compare($filteredHeaders, $actualHeaders); + } + + /** + * Case insensitive assertContains + * + * @param string $needle Search string + * @param string $haystack Search this + * @param string $message Optional failure message + */ + public function assertContainsIns($needle, $haystack, $message = null) + { + $this->assertContains(strtolower($needle), strtolower($haystack), $message); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php new file mode 100755 index 000000000..20feaa875 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php @@ -0,0 +1,34 @@ +getMockForAbstractClass('Guzzle\Http\AbstractEntityBodyDecorator', array($e)); + + $this->assertSame($e->getStream(), $mock->getStream()); + $this->assertSame($e->getContentLength(), $mock->getContentLength()); + $this->assertSame($e->getSize(), $mock->getSize()); + $this->assertSame($e->getContentMd5(), $mock->getContentMd5()); + $this->assertSame($e->getContentType(), $mock->getContentType()); + $this->assertSame($e->__toString(), $mock->__toString()); + $this->assertSame($e->getUri(), $mock->getUri()); + $this->assertSame($e->getStreamType(), $mock->getStreamType()); + $this->assertSame($e->getWrapper(), $mock->getWrapper()); + $this->assertSame($e->getWrapperData(), $mock->getWrapperData()); + $this->assertSame($e->isReadable(), $mock->isReadable()); + $this->assertSame($e->isWritable(), $mock->isWritable()); + $this->assertSame($e->isConsumed(), $mock->isConsumed()); + $this->assertSame($e->isLocal(), $mock->isLocal()); + $this->assertSame($e->isSeekable(), $mock->isSeekable()); + $this->assertSame($e->getContentEncoding(), $mock->getContentEncoding()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php new file mode 100755 index 000000000..e6e6cdbf5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php @@ -0,0 +1,249 @@ +decorated = EntityBody::factory('testing'); + $this->body = new CachingEntityBody($this->decorated); + } + + public function testUsesRemoteSizeIfPossible() + { + $body = EntityBody::factory('test'); + $caching = new CachingEntityBody($body); + $this->assertEquals(4, $caching->getSize()); + $this->assertEquals(4, $caching->getContentLength()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage does not support custom stream rewind + */ + public function testDoesNotAllowRewindFunction() + { + $this->body->setRewindFunction(true); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Cannot seek to byte 10 + */ + public function testCannotSeekPastWhatHasBeenRead() + { + $this->body->seek(10); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage supports only SEEK_SET and SEEK_CUR + */ + public function testCannotUseSeekEnd() + { + $this->body->seek(2, SEEK_END); + } + + public function testChangingUnderlyingStreamUpdatesSizeAndStream() + { + $size = filesize(__FILE__); + $s = fopen(__FILE__, 'r'); + $this->body->setStream($s, $size); + $this->assertEquals($size, $this->body->getSize()); + $this->assertEquals($size, $this->decorated->getSize()); + $this->assertSame($s, $this->body->getStream()); + $this->assertSame($s, $this->decorated->getStream()); + } + + public function testRewindUsesSeek() + { + $a = EntityBody::factory('foo'); + $d = $this->getMockBuilder('Guzzle\Http\CachingEntityBody') + ->setMethods(array('seek')) + ->setConstructorArgs(array($a)) + ->getMock(); + $d->expects($this->once()) + ->method('seek') + ->with(0) + ->will($this->returnValue(true)); + $d->rewind(); + } + + public function testCanSeekToReadBytes() + { + $this->assertEquals('te', $this->body->read(2)); + $this->body->seek(0); + $this->assertEquals('test', $this->body->read(4)); + $this->assertEquals(4, $this->body->ftell()); + $this->body->seek(2); + $this->assertEquals(2, $this->body->ftell()); + $this->body->seek(2, SEEK_CUR); + $this->assertEquals(4, $this->body->ftell()); + $this->assertEquals('ing', $this->body->read(3)); + } + + public function testWritesToBufferStream() + { + $this->body->read(2); + $this->body->write('hi'); + $this->body->rewind(); + $this->assertEquals('tehiing', (string) $this->body); + } + + public function testReadLinesFromBothStreams() + { + $this->body->seek($this->body->ftell()); + $this->body->write("test\n123\nhello\n1234567890\n"); + $this->body->rewind(); + $this->assertEquals("test\n", $this->body->readLine(7)); + $this->assertEquals("123\n", $this->body->readLine(7)); + $this->assertEquals("hello\n", $this->body->readLine(7)); + $this->assertEquals("123456", $this->body->readLine(7)); + $this->assertEquals("7890\n", $this->body->readLine(7)); + // We overwrote the decorated stream, so no more data + $this->assertEquals('', $this->body->readLine(7)); + } + + public function testSkipsOverwrittenBytes() + { + $decorated = EntityBody::factory( + implode("\n", array_map(function ($n) { + return str_pad($n, 4, '0', STR_PAD_LEFT); + }, range(0, 25))) + ); + + $body = new CachingEntityBody($decorated); + + $this->assertEquals("0000\n", $body->readLine()); + $this->assertEquals("0001\n", $body->readLine()); + // Write over part of the body yet to be read, so skip some bytes + $this->assertEquals(5, $body->write("TEST\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + // Read, which skips bytes, then reads + $this->assertEquals("0003\n", $body->readLine()); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("0004\n", $body->readLine()); + $this->assertEquals("0005\n", $body->readLine()); + + // Overwrite part of the cached body (so don't skip any bytes) + $body->seek(5); + $this->assertEquals(5, $body->write("ABCD\n")); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("TEST\n", $body->readLine()); + $this->assertEquals("0003\n", $body->readLine()); + $this->assertEquals("0004\n", $body->readLine()); + $this->assertEquals("0005\n", $body->readLine()); + $this->assertEquals("0006\n", $body->readLine()); + $this->assertEquals(5, $body->write("1234\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + + // Seek to 0 and ensure the overwritten bit is replaced + $body->rewind(); + $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); + + // Ensure that casting it to a string does not include the bit that was overwritten + $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); + } + + public function testWrapsContentType() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getContentType')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + $a->expects($this->once()) + ->method('getContentType') + ->will($this->returnValue('foo')); + $d = new CachingEntityBody($a); + $this->assertEquals('foo', $d->getContentType()); + } + + public function testWrapsContentEncoding() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getContentEncoding')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + $a->expects($this->once()) + ->method('getContentEncoding') + ->will($this->returnValue('foo')); + $d = new CachingEntityBody($a); + $this->assertEquals('foo', $d->getContentEncoding()); + } + + public function testWrapsMetadata() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getMetadata', 'getWrapper', 'getWrapperData', 'getStreamType', 'getUri')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + + $a->expects($this->once()) + ->method('getMetadata') + ->will($this->returnValue(array())); + // Called twice for getWrapper and getWrapperData + $a->expects($this->exactly(1)) + ->method('getWrapper') + ->will($this->returnValue('wrapper')); + $a->expects($this->once()) + ->method('getWrapperData') + ->will($this->returnValue(array())); + $a->expects($this->once()) + ->method('getStreamType') + ->will($this->returnValue('baz')); + $a->expects($this->once()) + ->method('getUri') + ->will($this->returnValue('path/to/foo')); + + $d = new CachingEntityBody($a); + $this->assertEquals(array(), $d->getMetaData()); + $this->assertEquals('wrapper', $d->getWrapper()); + $this->assertEquals(array(), $d->getWrapperData()); + $this->assertEquals('baz', $d->getStreamType()); + $this->assertEquals('path/to/foo', $d->getUri()); + } + + public function testWrapsCustomData() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getCustomData', 'setCustomData')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + + $a->expects($this->exactly(1)) + ->method('getCustomData') + ->with('foo') + ->will($this->returnValue('bar')); + + $a->expects($this->exactly(1)) + ->method('setCustomData') + ->with('foo', 'bar') + ->will($this->returnSelf()); + + $d = new CachingEntityBody($a); + $this->assertSame($d, $d->setCustomData('foo', 'bar')); + $this->assertEquals('bar', $d->getCustomData('foo')); + } + + public function testClosesBothStreams() + { + $s = fopen('php://temp', 'r'); + $a = EntityBody::factory($s); + $d = new CachingEntityBody($a); + $d->close(); + $this->assertFalse(is_resource($s)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php new file mode 100755 index 000000000..4a91a18f9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php @@ -0,0 +1,601 @@ +assertEquals('http://www.google.com/', $client->getBaseUrl()); + $this->assertSame($client, $client->setConfig(array( + 'test' => '123' + ))); + $this->assertEquals(array('test' => '123'), $client->getConfig()->getAll()); + $this->assertEquals('123', $client->getConfig('test')); + $this->assertSame($client, $client->setBaseUrl('http://www.test.com/{test}')); + $this->assertEquals('http://www.test.com/123', $client->getBaseUrl()); + $this->assertEquals('http://www.test.com/{test}', $client->getBaseUrl(false)); + + try { + $client->setConfig(false); + } catch (\InvalidArgumentException $e) { + } + } + + public function testDescribesEvents() + { + $this->assertEquals(array('client.create_request'), Client::getAllEvents()); + } + + public function testConstructorCanAcceptConfig() + { + $client = new Client('http://www.test.com/', array( + 'data' => '123' + )); + $this->assertEquals('123', $client->getConfig('data')); + } + + public function testCanUseCollectionAsConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(new Collection(array( + 'api' => 'v1', + 'key' => 'value', + 'base_url' => 'http://www.google.com/' + ))); + $this->assertEquals('v1', $client->getConfig('api')); + } + + public function testExpandsUriTemplatesUsingConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(array('api' => 'v1', 'key' => 'value', 'foo' => 'bar')); + $ref = new \ReflectionMethod($client, 'expandTemplate'); + $ref->setAccessible(true); + $this->assertEquals('Testing...api/v1/key/value', $ref->invoke($client, 'Testing...api/{api}/key/{key}')); + } + + public function testClientAttachersObserversToRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $logPlugin = $this->getLogPlugin(); + $client->getEventDispatcher()->addSubscriber($logPlugin); + + // Get a request from the client and ensure the the observer was + // attached to the new request + $request = $client->createRequest(); + $this->assertTrue($this->hasSubscriber($request, $logPlugin)); + } + + public function testClientReturnsValidBaseUrls() + { + $client = new Client('http://www.{foo}.{data}/', array( + 'data' => '123', + 'foo' => 'bar' + )); + $this->assertEquals('http://www.bar.123/', $client->getBaseUrl()); + $client->setBaseUrl('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $client->getBaseUrl()); + } + + public function testClientAddsCurlOptionsToRequests() + { + $client = new Client('http://www.test.com/', array( + 'api' => 'v1', + // Adds the option using the curl values + 'curl.options' => array( + 'CURLOPT_HTTPAUTH' => 'CURLAUTH_DIGEST', + 'abc' => 'foo', + 'blacklist' => 'abc', + 'debug' => true + ) + )); + + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertEquals(CURLAUTH_DIGEST, $options->get(CURLOPT_HTTPAUTH)); + $this->assertEquals('foo', $options->get('abc')); + $this->assertEquals('abc', $options->get('blacklist')); + } + + public function testClientAllowsFineGrainedSslControlButIsSecureByDefault() + { + $client = new Client('https://www.secure.com/'); + + // secure by default + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertTrue($options->get(CURLOPT_SSL_VERIFYPEER)); + + // set a capath if you prefer + $client = new Client('https://www.secure.com/'); + $client->setSslVerification(__DIR__); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertSame(__DIR__, $options->get(CURLOPT_CAPATH)); + } + + public function testConfigSettingsControlSslConfiguration() + { + // Use the default ca certs on the system + $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => 'system')); + $this->assertNull($client->getConfig('curl.options')); + // Can set the cacert value as well + $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => false)); + $options = $client->getConfig('curl.options'); + $this->assertArrayNotHasKey(CURLOPT_CAINFO, $options); + $this->assertSame(false, $options[CURLOPT_SSL_VERIFYPEER]); + $this->assertSame(0, $options[CURLOPT_SSL_VERIFYHOST]); + } + + public function testClientAllowsUnsafeOperationIfRequested() + { + // be really unsafe if you insist + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + + $client->setSslVerification(false); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertFalse($options->get(CURLOPT_SSL_VERIFYPEER)); + $this->assertNull($options->get(CURLOPT_CAINFO)); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testThrowsExceptionForInvalidCertificate() + { + $client = new Client('https://www.secure.com/'); + $client->setSslVerification('/path/to/missing/file'); + } + + public function testClientAllowsSettingSpecificSslCaInfo() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + + $client->setSslVerification(__FILE__); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertSame(__FILE__, $options->get(CURLOPT_CAINFO)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testClientPreventsInadvertentInsecureVerifyHostSetting() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + $client->setSslVerification(__FILE__, true, true); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testClientPreventsInvalidVerifyPeerSetting() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + $client->setSslVerification(__FILE__, 'yes'); + } + + public function testClientAddsParamsToRequests() + { + Version::$emitWarnings = false; + $client = new Client('http://www.example.com', array( + 'api' => 'v1', + 'request.params' => array( + 'foo' => 'bar', + 'baz' => 'jar' + ) + )); + $request = $client->createRequest(); + $this->assertEquals('bar', $request->getParams()->get('foo')); + $this->assertEquals('jar', $request->getParams()->get('baz')); + Version::$emitWarnings = true; + } + + public function urlProvider() + { + $u = $this->getServer()->getUrl() . 'base/'; + $u2 = $this->getServer()->getUrl() . 'base?z=1'; + return array( + array($u, '', $u), + array($u, 'relative/path/to/resource', $u . 'relative/path/to/resource'), + array($u, 'relative/path/to/resource?a=b&c=d', $u . 'relative/path/to/resource?a=b&c=d'), + array($u, '/absolute/path/to/resource', $this->getServer()->getUrl() . 'absolute/path/to/resource'), + array($u, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'), + array($u2, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d&z=1'), + array($u2, 'relative/path/to/resource', $this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1'), + array($u2, 'relative/path/to/resource?another=query', $this->getServer()->getUrl() . 'base/relative/path/to/resource?another=query&z=1') + ); + } + + /** + * @dataProvider urlProvider + */ + public function testBuildsRelativeUrls($baseUrl, $url, $result) + { + $client = new Client($baseUrl); + $this->assertEquals($result, $client->get($url)->getUrl()); + } + + public function testAllowsConfigsToBeChangedAndInjectedInBaseUrl() + { + $client = new Client('http://{a}/{b}'); + $this->assertEquals('http:///', $client->getBaseUrl()); + $this->assertEquals('http://{a}/{b}', $client->getBaseUrl(false)); + $client->setConfig(array( + 'a' => 'test.com', + 'b' => 'index.html' + )); + $this->assertEquals('http://test.com/index.html', $client->getBaseUrl()); + } + + public function testCreatesRequestsWithDefaultValues() + { + $client = new Client($this->getServer()->getUrl() . 'base'); + + // Create a GET request + $request = $client->createRequest(); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a DELETE request + $request = $client->createRequest('DELETE'); + $this->assertEquals('DELETE', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a HEAD request with custom headers + $request = $client->createRequest('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + $this->assertEquals('http://www.test.com/', $request->getUrl()); + + // Create a PUT request + $request = $client->createRequest('PUT'); + $this->assertEquals('PUT', $request->getMethod()); + + // Create a PUT request with injected config + $client->getConfig()->set('a', 1)->set('b', 2); + $request = $client->createRequest('PUT', '/path/{a}?q={b}'); + $this->assertEquals($request->getUrl(), $this->getServer()->getUrl() . 'path/1?q=2'); + } + + public function testClientHasHelperMethodsForCreatingRequests() + { + $url = $this->getServer()->getUrl(); + $client = new Client($url . 'base'); + $this->assertEquals('GET', $client->get()->getMethod()); + $this->assertEquals('PUT', $client->put()->getMethod()); + $this->assertEquals('POST', $client->post()->getMethod()); + $this->assertEquals('HEAD', $client->head()->getMethod()); + $this->assertEquals('DELETE', $client->delete()->getMethod()); + $this->assertEquals('OPTIONS', $client->options()->getMethod()); + $this->assertEquals('PATCH', $client->patch()->getMethod()); + $this->assertEquals($url . 'base/abc', $client->get('abc')->getUrl()); + $this->assertEquals($url . 'zxy', $client->put('/zxy')->getUrl()); + $this->assertEquals($url . 'zxy?a=b', $client->post('/zxy?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->head('?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->delete('/base?a=b')->getUrl()); + } + + public function testClientInjectsConfigsIntoUrls() + { + $client = new Client('http://www.test.com/api/v1', array( + 'test' => '123' + )); + $request = $client->get('relative/{test}'); + $this->assertEquals('http://www.test.com/api/v1/relative/123', $request->getUrl()); + } + + public function testAllowsEmptyBaseUrl() + { + $client = new Client(); + $request = $client->get('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $request->setResponse(new Response(200), true); + $request->send(); + } + + public function testAllowsCustomCurlMultiObjects() + { + $mock = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('add', 'send')); + $mock->expects($this->once()) + ->method('add') + ->will($this->returnSelf()); + $mock->expects($this->once()) + ->method('send') + ->will($this->returnSelf()); + + $client = new Client(); + $client->setCurlMulti($mock); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $client->send($request); + } + + public function testClientSendsMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + + $responses = array( + new Response(200), + new Response(201), + new Response(202) + ); + + $mock->addResponse($responses[0]); + $mock->addResponse($responses[1]); + $mock->addResponse($responses[2]); + + $client->getEventDispatcher()->addSubscriber($mock); + + $requests = array( + $client->get(), + $client->head(), + $client->put('/', null, 'test') + ); + + $this->assertEquals(array( + $responses[0], + $responses[1], + $responses[2] + ), $client->send($requests)); + } + + public function testClientSendsSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(200); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $this->assertEquals($response, $client->send($client->get())); + } + + /** + * @expectedException \Guzzle\Http\Exception\BadResponseException + */ + public function testClientThrowsExceptionForSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(404); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send($client->get()); + } + + /** + * @expectedException \Guzzle\Common\Exception\ExceptionCollection + */ + public function testClientThrowsExceptionForMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $mock->addResponse(new Response(200)); + $mock->addResponse(new Response(404)); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send(array($client->get(), $client->head())); + } + + public function testQueryStringsAreNotDoubleEncoded() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + 'data' => array( + 'test' => 'a&b' + ) + )); + + $request = $client->get('{/path*}{?query,data*}'); + $this->assertEquals('http://test.com/foo/bar?query=hi%20there&test=a%26b', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + $this->assertEquals('a&b', $request->getQuery()->get('test')); + } + + public function testQueryStringsAreNotDoubleEncodedUsingAbsolutePaths() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + )); + $request = $client->get('http://test.com{?query}'); + $this->assertEquals('http://test.com?query=hi%20there', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + } + + public function testAllowsUriTemplateInjection() + { + $client = new Client('http://test.com'); + $ref = new \ReflectionMethod($client, 'getUriTemplate'); + $ref->setAccessible(true); + $a = $ref->invoke($client); + $this->assertSame($a, $ref->invoke($client)); + $client->setUriTemplate(new UriTemplate()); + $this->assertNotSame($a, $ref->invoke($client)); + } + + public function testAllowsCustomVariablesWhenExpandingTemplates() + { + $client = new Client('http://test.com', array('test' => 'hi')); + $ref = new \ReflectionMethod($client, 'expandTemplate'); + $ref->setAccessible(true); + $uri = $ref->invoke($client, 'http://{test}{?query*}', array('query' => array('han' => 'solo'))); + $this->assertEquals('http://hi?han=solo', $uri); + } + + public function testUriArrayAllowsCustomTemplateVariables() + { + $client = new Client(); + $vars = array( + 'var' => 'hi' + ); + $this->assertEquals('/hi', (string) $client->createRequest('GET', array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->get(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->put(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->post(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->head(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->options(array('/{var}', $vars))->getUrl()); + } + + public function testAllowsDefaultHeaders() + { + Version::$emitWarnings = false; + $default = array('X-Test' => 'Hi!'); + $other = array('X-Other' => 'Foo'); + + $client = new Client(); + $client->setDefaultHeaders($default); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + $client->setDefaultHeaders(new Collection($default)); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + + $request = $client->createRequest('GET', null, $other); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET', null, new Collection($other)); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET'); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + Version::$emitWarnings = true; + } + + public function testDontReuseCurlMulti() + { + $client1 = new Client(); + $client2 = new Client(); + $this->assertNotSame($client1->getCurlMulti(), $client2->getCurlMulti()); + } + + public function testGetDefaultUserAgent() + { + $client = new Client(); + $agent = $this->readAttribute($client, 'userAgent'); + $version = curl_version(); + $testAgent = sprintf('Guzzle/%s curl/%s PHP/%s', Version::VERSION, $version['version'], PHP_VERSION); + $this->assertEquals($agent, $testAgent); + + $client->setUserAgent('foo'); + $this->assertEquals('foo', $this->readAttribute($client, 'userAgent')); + } + + public function testOverwritesUserAgent() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com', array('User-agent' => 'foo')); + $this->assertEquals('foo', (string) $request->getHeader('User-Agent')); + } + + public function testUsesDefaultUserAgent() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com'); + $this->assertContains('Guzzle/', (string) $request->getHeader('User-Agent')); + } + + public function testCanSetDefaultRequestOptions() + { + $client = new Client(); + $client->getConfig()->set('request.options', array( + 'query' => array('test' => '123', 'other' => 'abc'), + 'headers' => array('Foo' => 'Bar', 'Baz' => 'Bam') + )); + $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test')); + // Explicit options on a request should overrule default options + $this->assertEquals('Test', (string) $request->getHeader('Foo')); + $this->assertEquals('hello', $request->getQuery()->get('test')); + // Default options should still be set + $this->assertEquals('abc', $request->getQuery()->get('other')); + $this->assertEquals('Bam', (string) $request->getHeader('Baz')); + } + + public function testCanSetSetOptionsOnRequests() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test'), null, array( + 'cookies' => array('michael' => 'test') + )); + $this->assertEquals('test', $request->getCookie('michael')); + } + + public function testHasDefaultOptionsHelperMethods() + { + $client = new Client(); + // With path + $client->setDefaultOption('headers/foo', 'bar'); + $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); + // With simple key + $client->setDefaultOption('allow_redirects', false); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + + $this->assertEquals(array( + 'headers' => array('foo' => 'bar'), + 'allow_redirects' => false + ), $client->getConfig('request.options')); + + $request = $client->get('/'); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testHeadCanUseOptions() + { + $client = new Client(); + $head = $client->head('http://www.foo.com', array(), array('query' => array('foo' => 'bar'))); + $this->assertEquals('bar', $head->getQuery()->get('foo')); + } + + public function testCanSetRelativeUrlStartingWithHttp() + { + $client = new Client('http://www.foo.com'); + $this->assertEquals( + 'http://www.foo.com/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php new file mode 100755 index 000000000..5bf28de36 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php @@ -0,0 +1,947 @@ +getEventDispatcher()->addListener('request.sent', function (Event $e) use ($that) { + $that->requestHandle = $e['handle']; + }); + + return $request; + } + + public function setUp() + { + $this->requestHandle = null; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorExpectsCurlResource() + { + $h = new CurlHandle(false, array()); + } + + public function testConstructorExpectsProperOptions() + { + $h = curl_init($this->getServer()->getUrl()); + try { + $ha = new CurlHandle($h, false); + $this->fail('Expected InvalidArgumentException'); + } catch (\InvalidArgumentException $e) { + } + + $ha = new CurlHandle($h, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + + $ha = new CurlHandle($h, new Collection(array( + CURLOPT_URL => $this->getServer()->getUrl() + ))); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + } + + public function testConstructorInitializesObject() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertSame($handle, $h->getHandle()); + $this->assertInstanceOf('Guzzle\\Http\\Url', $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), (string) $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), $h->getOptions()->get(CURLOPT_URL)); + } + + public function testStoresStdErr() + { + $request = RequestFactory::getInstance()->create('GET', 'http://test.com'); + $request->getCurlOptions()->set('debug', true); + $h = CurlHandle::factory($request); + $this->assertEquals($h->getStderr(true), $h->getOptions()->get(CURLOPT_STDERR)); + $this->assertInternalType('resource', $h->getStderr(true)); + $this->assertInternalType('string', $h->getStderr(false)); + $r = $h->getStderr(true); + fwrite($r, 'test'); + $this->assertEquals('test', $h->getStderr(false)); + } + + public function testStoresCurlErrorNumber() + { + $h = new CurlHandle(curl_init('http://test.com'), array(CURLOPT_URL => 'http://test.com')); + $this->assertEquals(CURLE_OK, $h->getErrorNo()); + $h->setErrorNo(CURLE_OPERATION_TIMEOUTED); + $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $h->getErrorNo()); + } + + public function testAccountsForMissingStdErr() + { + $handle = curl_init('http://www.test.com/'); + $h = new CurlHandle($handle, array( + CURLOPT_URL => 'http://www.test.com/' + )); + $this->assertNull($h->getStderr(false)); + } + + public function testDeterminesIfResourceIsAvailable() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array()); + $this->assertTrue($h->isAvailable()); + + // Mess it up by closing the handle + curl_close($handle); + $this->assertFalse($h->isAvailable()); + + // Mess it up by unsetting the handle + $handle = null; + $this->assertFalse($h->isAvailable()); + } + + public function testWrapsErrorsAndInfo() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + $settings = array( + CURLOPT_PORT => 123, + CURLOPT_CONNECTTIMEOUT_MS => 1, + CURLOPT_TIMEOUT_MS => 1 + ); + + $handle = curl_init($this->getServer()->getUrl()); + curl_setopt_array($handle, $settings); + $h = new CurlHandle($handle, $settings); + @curl_exec($handle); + + $errors = array( + "couldn't connect to host", + 'timeout was reached', + 'connection time-out', + 'connect() timed out!', + 'failed connect to 127.0.0.1:123; connection refused', + 'failed to connect to 127.0.0.1 port 123: connection refused' + ); + $this->assertTrue(in_array(strtolower($h->getError()), $errors), $h->getError() . ' was not the error'); + + $this->assertTrue($h->getErrorNo() > 0); + + $this->assertEquals($this->getServer()->getUrl(), $h->getInfo(CURLINFO_EFFECTIVE_URL)); + $this->assertInternalType('array', $h->getInfo()); + + curl_close($handle); + $this->assertEquals(null, $h->getInfo('url')); + } + + public function testGetInfoWithoutDebugMode() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get($this->getServer()->getUrl()); + $response = $request->send(); + + $info = $response->getInfo(); + $this->assertFalse(empty($info)); + $this->assertEquals($this->getServer()->getUrl(), $info['url']); + } + + public function testWrapsCurlOptions() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_AUTOREFERER => true, + CURLOPT_BUFFERSIZE => 1024 + )); + + $this->assertEquals(true, $h->getOptions()->get(CURLOPT_AUTOREFERER)); + $this->assertEquals(1024, $h->getOptions()->get(CURLOPT_BUFFERSIZE)); + } + + /** + * Data provider for factory tests + * + * @return array + */ + public function dataProvider() + { + $testFile = __DIR__ . '/../../../../../phpunit.xml.dist'; + + $postBody = new QueryString(array('file' => '@' . $testFile)); + $qs = new QueryString(array( + 'x' => 'y', + 'z' => 'a' + )); + + $client = new Client(); + $userAgent = $client->getDefaultUserAgent(); + $auth = base64_encode('michael:123'); + $testFileSize = filesize($testFile); + + $tests = array( + // Send a regular GET + array('GET', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + )), + // Test that custom request methods can be used + array('TRACE', 'http://www.google.com/', null, null, array( + CURLOPT_CUSTOMREQUEST => 'TRACE' + )), + // Send a GET using a port + array('GET', 'http://127.0.0.1:8080', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_PORT => 8080, + CURLOPT_HTTPHEADER => array('Accept:', 'Host: 127.0.0.1:8080', 'User-Agent: ' . $userAgent), + )), + // Send a HEAD request + array('HEAD', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + CURLOPT_NOBODY => 1 + )), + // Send a GET using basic auth + array('GET', 'https://michael:123@127.0.0.1/index.html?q=2', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array( + 'Accept:', + 'Host: 127.0.0.1', + 'Authorization: Basic ' . $auth, + 'User-Agent: ' . $userAgent + ), + CURLOPT_PORT => 443 + )), + // Send a GET request with custom headers + array('GET', 'http://127.0.0.1:8124/', array( + 'x-test-data' => 'Guzzle' + ), null, array( + CURLOPT_PORT => 8124, + CURLOPT_HTTPHEADER => array( + 'Accept:', + 'Host: 127.0.0.1:8124', + 'x-test-data: Guzzle', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'x-test-data' => 'Guzzle' + )), + // Send a POST using a query string + array('POST', 'http://127.0.0.1:8124/post.php', null, $qs, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POSTFIELDS => 'x=y&z=a', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', + '!Transfer-Encoding' => null + )), + // Send a PUT using raw data + array('PUT', 'http://127.0.0.1:8124/put.php', null, EntityBody::factory(fopen($testFile, 'r+')), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_READFUNCTION => 'callback', + CURLOPT_INFILESIZE => filesize($testFile), + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + '!Expect' => null, + 'Content-Length' => $testFileSize, + '!Transfer-Encoding' => null + )), + // Send a POST request using an array of fields + array('POST', 'http://127.0.0.1:8124/post.php', null, array( + 'x' => 'y', + 'a' => 'b' + ), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => 'x=y&a=b', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data and a custom content-type + array('POST', 'http://127.0.0.1:8124/post.php', array( + 'Content-Type' => 'application/json' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_UPLOAD => true, + CURLOPT_INFILESIZE => 14, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/json', + 'User-Agent: ' . $userAgent + ), + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + '!Expect' => null, + 'Content-Length' => '14', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data, a custom content-type, and use chunked encoding + array('POST', 'http://127.0.0.1:8124/post.php', array( + 'Content-Type' => 'application/json', + 'Transfer-Encoding' => 'chunked' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_UPLOAD => true, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Transfer-Encoding: chunked', + 'Content-Type: application/json', + 'User-Agent: ' . $userAgent + ), + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + '!Expect' => null, + 'Transfer-Encoding' => 'chunked', + '!Content-Length' => '' + )), + // Send a POST request with no body + array('POST', 'http://127.0.0.1:8124/post.php', null, '', array( + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Transfer-Encoding' => null + )), + // Send a POST request with empty post fields + array('POST', 'http://127.0.0.1:8124/post.php', null, array(), array( + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Transfer-Encoding' => null + )), + // Send a PATCH request + array('PATCH', 'http://127.0.0.1:8124/patch.php', null, 'body', array( + CURLOPT_INFILESIZE => 4, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + )), + // Send a DELETE request with a body + array('DELETE', 'http://127.0.0.1:8124/delete.php', null, 'body', array( + CURLOPT_CUSTOMREQUEST => 'DELETE', + CURLOPT_INFILESIZE => 4, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '4', + '!Expect' => null, + '!Transfer-Encoding' => null + )), + + /** + * Send a request with empty path and a fragment - the fragment must be + * stripped out before sending it to curl + * + * @issue 453 + * @link https://github.com/guzzle/guzzle/issues/453 + */ + array('GET', 'http://www.google.com#head', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + )), + ); + + $postTest = array('POST', 'http://127.0.0.1:8124/post.php', null, $postBody, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => array( + 'file' => '@' . $testFile . ';filename=phpunit.xml.dist;type=application/octet-stream' + ), + CURLOPT_HTTPHEADER => array ( + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: multipart/form-data', + 'Expect: 100-Continue', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '*', + 'Expect' => '100-Continue', + 'Content-Type' => 'multipart/form-data; boundary=*', + '!Transfer-Encoding' => null + )); + + if (version_compare(phpversion(), '5.5.0', '>=')) { + $postTest[4][CURLOPT_POSTFIELDS] = array( + 'file' => new \CurlFile($testFile, 'application/octet-stream', 'phpunit.xml.dist') + ); + } + + $tests[] = $postTest; + + return $tests; + } + + /** + * @dataProvider dataProvider + */ + public function testFactoryCreatesCurlBasedOnRequest($method, $url, $headers, $body, $options, $expectedHeaders = null) + { + $client = new Client(); + $request = $client->createRequest($method, $url, $headers, $body); + $request->getCurlOptions()->set('debug', true); + + $originalRequest = clone $request; + $curlTest = clone $request; + $handle = CurlHandle::factory($curlTest); + + $this->assertInstanceOf('Guzzle\\Http\\Curl\\CurlHandle', $handle); + $o = $handle->getOptions()->getAll(); + + // Headers are case-insensitive + if (isset($o[CURLOPT_HTTPHEADER])) { + $o[CURLOPT_HTTPHEADER] = array_map('strtolower', $o[CURLOPT_HTTPHEADER]); + } + if (isset($options[CURLOPT_HTTPHEADER])) { + $options[CURLOPT_HTTPHEADER] = array_map('strtolower', $options[CURLOPT_HTTPHEADER]); + } + + $check = 0; + foreach ($options as $key => $value) { + $check++; + $this->assertArrayHasKey($key, $o, '-> Check number ' . $check); + if ($key != CURLOPT_HTTPHEADER && $key != CURLOPT_POSTFIELDS && (is_array($o[$key])) || $o[$key] instanceof \Closure) { + $this->assertEquals('callback', $value, '-> Check number ' . $check); + } else { + $this->assertTrue($value == $o[$key], '-> Check number ' . $check . ' - ' . var_export($value, true) . ' != ' . var_export($o[$key], true)); + } + } + + // If we are testing the actual sent headers + if ($expectedHeaders) { + + // Send the request to the test server + $client = new Client($this->getServer()->getUrl()); + $request->setClient($client); + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + // Get the request that was sent and create a request that we expected + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals($method, $requests[0]->getMethod()); + + $test = $this->compareHeaders($expectedHeaders, $requests[0]->getHeaders()); + $this->assertFalse($test, $test . "\nSent: \n" . $request . "\n\n" . $requests[0]); + + // Ensure only one Content-Length header is sent + if ($request->getHeader('Content-Length')) { + $this->assertEquals((string) $request->getHeader('Content-Length'), (string) $requests[0]->getHeader('Content-Length')); + } + } + } + + public function testFactoryUsesSpecifiedProtocol() + { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:8124/'); + $request->setProtocolVersion('1.1'); + $handle = CurlHandle::factory($request); + $options = $handle->getOptions(); + $this->assertEquals(CURL_HTTP_VERSION_1_1, $options[CURLOPT_HTTP_VERSION]); + } + + public function testUploadsPutData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->getCurlOptions()->set('debug', true); + $request->setBody(EntityBody::factory('test'), 'text/plain', false); + $request->getCurlOptions()->set('progress', true); + + $o = $this->getWildcardObserver($request); + $request->send(); + + // Make sure that the events were dispatched + $this->assertTrue($o->has('curl.callback.progress')); + + // Ensure that the request was received exactly as intended + $r = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); + $this->assertEquals(4, (string) $r[0]->getHeader('Content-Length')); + $sent = strtolower($r[0]); + $this->assertContains('put / http/1.1', $sent); + $this->assertContains('host: 127.0.0.1', $sent); + $this->assertContains('user-agent:', $sent); + $this->assertContains('content-type: text/plain', $sent); + } + + public function testUploadsPutDataUsingChunkedEncodingWhenLengthCannotBeDetermined() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->setBody(EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')), 'text/plain'); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[1]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[1]->hasHeader('Content-Length')); + } + + public function testUploadsPutDataUsingChunkedEncodingWhenForced() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', array('Transfer-Encoding' => 'chunked'), 'hi!'); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[0]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[0]->hasHeader('Content-Length')); + $this->assertEquals('hi!', $r[0]->getBody(true)); + } + + public function testSendsPostRequestsWithFields() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFields(array( + 'a' => 'b', + 'c' => 'ay! ~This is a test, isn\'t it?' + )); + $request->send(); + + // Make sure that the request was sent correctly + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('a=b&c=ay%21%20~This%20is%20a%20test%2C%20isn%27t%20it%3F', (string) $r[0]->getBody()); + $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); + $this->assertEquals(56, (string) $r[0]->getHeader('Content-Length')); + $sent = strtolower($r[0]); + $this->assertContains('post / http/1.1', $sent); + $this->assertContains('content-type: application/x-www-form-urlencoded; charset=utf-8', $sent); + } + + public function testSendsPostRequestsWithFiles() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFiles(array( + 'foo' => __FILE__, + )); + $request->addPostFields(array( + 'bar' => 'baz', + 'arr' => array('a' => 1, 'b' => 2), + )); + $this->updateForHandle($request); + $request->send(); + + // Ensure the CURLOPT_POSTFIELDS option was set properly + $options = $this->requestHandle->getOptions()->getAll(); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=CurlHandleTest.php;type=text/x-', $options[CURLOPT_POSTFIELDS]['foo']); + } else{ + $this->assertInstanceOf('CURLFile', $options[CURLOPT_POSTFIELDS]['foo']); + } + $this->assertEquals('baz', $options[CURLOPT_POSTFIELDS]['bar']); + $this->assertEquals('1', $options[CURLOPT_POSTFIELDS]['arr[a]']); + $this->assertEquals('2', $options[CURLOPT_POSTFIELDS]['arr[b]']); + // Ensure that a Content-Length header was sent by cURL + $this->assertTrue($request->hasHeader('Content-Length')); + } + + public function testCurlConfigurationOptionsAreSet() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->setClient(new Client('http://www.example.com')); + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 99); + $request->getCurlOptions()->set('curl.fake_opt', 99); + $request->getCurlOptions()->set(CURLOPT_PORT, 8181); + $handle = CurlHandle::factory($request); + $this->assertEquals(99, $handle->getOptions()->get(CURLOPT_CONNECTTIMEOUT)); + $this->assertEquals(8181, $handle->getOptions()->get(CURLOPT_PORT)); + $this->assertNull($handle->getOptions()->get('curl.fake_opt')); + $this->assertNull($handle->getOptions()->get('fake_opt')); + } + + public function testEnsuresRequestsHaveResponsesWhenUpdatingFromTransfer() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $handle = CurlHandle::factory($request); + $handle->updateRequestFromTransfer($request); + } + + public function testCanSendBodyAsString() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'foo'); + $request->getCurlOptions()->set('body_as_string', true); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('PUT /', $requests[0]); + $this->assertContains("\nfoo", $requests[0]); + $this->assertContains('content-length: 3', $requests[0]); + $this->assertNotContains('content-type', $requests[0]); + } + + public function testCanSendPostBodyAsString() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/', null, 'foo'); + $request->getCurlOptions()->set('body_as_string', true); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('POST /', $requests[0]); + $this->assertContains("\nfoo", $requests[0]); + $this->assertContains('content-length: 3', $requests[0]); + $this->assertNotContains('content-type', $requests[0]); + } + + public function testAllowsWireTransferInfoToBeEnabled() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $handle = CurlHandle::factory($request); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_STDERR)); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_VERBOSE)); + } + + public function testAddsCustomCurlOptions() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, 200); + $handle = CurlHandle::factory($request); + $this->assertEquals(200, $handle->getOptions()->get(CURLOPT_TIMEOUT)); + } + + public function testSendsPostUploadsWithContentDispositionHeaders() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $fileToUpload = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json'; + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post(); + $request->addPostFile('foo', $fileToUpload, 'application/json'); + $request->addPostFile('foo', __FILE__); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $body = (string) $requests[0]->getBody(); + + $this->assertContains('Content-Disposition: form-data; name="foo[0]"; filename="', $body); + $this->assertContains('Content-Type: application/json', $body); + $this->assertContains('Content-Type: text/x-', $body); + $this->assertContains('Content-Disposition: form-data; name="foo[1]"; filename="', $body); + } + + public function requestMethodProvider() + { + return array(array('POST'), array('PUT'), array('PATCH')); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSendsRequestsWithNoBodyUsingContentLengthZero($method) + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $client->createRequest($method)->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($requests[0]->hasHeader('Transfer-Encoding')); + $this->assertTrue($requests[0]->hasHeader('Content-Length')); + $this->assertEquals('0', (string) $requests[0]->getHeader('Content-Length')); + } + + /** + * @dataProvider provideCurlConfig + */ + public function testParseCurlConfigConvertsStringKeysToConstantKeys($options, $expected) + { + $actual = CurlHandle::parseCurlConfig($options); + $this->assertEquals($expected, $actual); + } + + /** + * Data provider for curl configurations + * + * @return array + */ + public function provideCurlConfig() + { + return array( + // Conversion of option name to constant value + array( + array( + 'CURLOPT_PORT' => 10, + 'CURLOPT_TIMEOUT' => 99 + ), + array( + CURLOPT_PORT => 10, + CURLOPT_TIMEOUT => 99 + ) + ), + // Keeps non constant options + array( + array('debug' => true), + array('debug' => true) + ), + // Conversion of constant names to constant values + array( + array('debug' => 'CURLPROXY_HTTP'), + array('debug' => CURLPROXY_HTTP) + ) + ); + } + + public function testSeeksToBeginningOfStreamWhenSending() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'test'); + $request->send(); + $request->send(); + + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($received)); + $this->assertEquals('test', (string) $received[0]->getBody()); + $this->assertEquals('test', (string) $received[1]->getBody()); + } + + public function testAllowsCurloptEncodingToBeSet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', null); + $request->getCurlOptions()->set(CURLOPT_ENCODING, ''); + $this->updateForHandle($request); + $request->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertSame('', $options[CURLOPT_ENCODING]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('accept: */*', $received[0]); + $this->assertContainsIns('accept-encoding: ', $received[0]); + } + + public function testSendsExpectHeaderWhenSizeIsGreaterThanCutoff() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'test'); + // Start sending the expect header to 2 bytes + $this->updateForHandle($request); + $request->setExpectHeaderCutoff(2)->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertContains('Expect: 100-Continue', $options[CURLOPT_HTTPHEADER]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('expect: 100-continue', $received[0]); + } + + public function testSetsCurloptEncodingWhenAcceptEncodingHeaderIsSet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', array( + 'Accept' => 'application/json', + 'Accept-Encoding' => 'gzip, deflate', + )); + $this->updateForHandle($request); + $request->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertSame('gzip, deflate', $options[CURLOPT_ENCODING]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('accept: application/json', $received[0]); + $this->assertContainsIns('accept-encoding: gzip, deflate', $received[0]); + } + + public function testSendsPostFieldsForNonPostRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client(); + $request = $client->put($this->getServer()->getUrl(), null, array( + 'foo' => 'baz', + 'baz' => 'bar' + )); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PUT', $requests[0]->getMethod()); + $this->assertEquals( + 'application/x-www-form-urlencoded; charset=utf-8', + (string) $requests[0]->getHeader('Content-Type') + ); + $this->assertEquals(15, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals('foo=baz&baz=bar', (string) $requests[0]->getBody()); + } + + public function testSendsPostFilesForNonPostRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client(); + $request = $client->put($this->getServer()->getUrl(), null, array( + 'foo' => '@' . __FILE__ + )); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PUT', $requests[0]->getMethod()); + $this->assertContains('multipart/form-data', (string) $requests[0]->getHeader('Content-Type')); + $this->assertContains('testSendsPostFilesForNonPostRequests', (string) $requests[0]->getBody()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php new file mode 100755 index 000000000..e04141c5f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php @@ -0,0 +1,110 @@ +multi = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + } + + public function tearDown() + { + unset($this->multi); + } + + public function testConstructorSetsMaxHandles() + { + $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + $this->assertEquals(self::MAX_HANDLES, $this->readAttribute($m, 'maxHandles')); + } + + public function testConstructorSetsSelectTimeout() + { + $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + $this->assertEquals(self::SELECT_TIMEOUT, $this->readAttribute($m, 'selectTimeout')); + } + + public function testAddingRequestsAddsToQueue() + { + $r = new Request('GET', 'http://www.foo.com'); + $this->assertSame($this->multi, $this->multi->add($r)); + $this->assertEquals(1, count($this->multi)); + $this->assertEquals(array($r), $this->multi->all()); + + $this->assertTrue($this->multi->remove($r)); + $this->assertFalse($this->multi->remove($r)); + $this->assertEquals(0, count($this->multi)); + } + + public function testResetClearsState() + { + $r = new Request('GET', 'http://www.foo.com'); + $this->multi->add($r); + $this->multi->reset(); + $this->assertEquals(0, count($this->multi)); + } + + public function testSendWillSendQueuedRequestsFirst() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $events = array(); + $client->getCurlMulti()->getEventDispatcher()->addListener( + CurlMultiProxy::ADD_REQUEST, + function ($e) use (&$events) { + $events[] = $e; + } + ); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.complete', function () use ($client) { + $client->get('/foo')->send(); + }); + $request->send(); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($received)); + $this->assertEquals($this->getServer()->getUrl(), $received[0]->getUrl()); + $this->assertEquals($this->getServer()->getUrl() . 'foo', $received[1]->getUrl()); + $this->assertEquals(2, count($events)); + } + + public function testTrimsDownMaxHandleCount() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $client->setCurlMulti(new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT)); + $request = $client->get(); + $request->send(); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $handles = $this->readAttribute($client->getCurlMulti(), 'handles'); + $this->assertEquals(2, count($handles)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php new file mode 100755 index 000000000..1272281f9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php @@ -0,0 +1,455 @@ +multi = new MockMulti(); + } + + public function tearDown() + { + unset($this->multi); + } + + public function testConstructorCreateMultiHandle() + { + $this->assertInternalType('resource', $this->multi->getHandle()); + $this->assertEquals('curl_multi', get_resource_type($this->multi->getHandle())); + } + + public function testDestructorClosesMultiHandle() + { + $handle = $this->multi->getHandle(); + $this->multi->__destruct(); + $this->assertFalse(is_resource($handle)); + } + + public function testRequestsCanBeAddedAndCounted() + { + $multi = new CurlMulti(); + $request1 = new Request('GET', 'http://www.google.com/'); + $multi->add($request1); + $this->assertEquals(array($request1), $multi->all()); + $request2 = new Request('POST', 'http://www.google.com/'); + $multi->add($request2); + $this->assertEquals(array($request1, $request2), $multi->all()); + $this->assertEquals(2, count($multi)); + } + + public function testRequestsCanBeRemoved() + { + $request1 = new Request('GET', 'http://www.google.com/'); + $this->multi->add($request1); + $request2 = new Request('PUT', 'http://www.google.com/'); + $this->multi->add($request2); + $this->assertEquals(array($request1, $request2), $this->multi->all()); + $this->assertTrue($this->multi->remove($request1)); + $this->assertFalse($this->multi->remove($request1)); + $this->assertEquals(array($request2), $this->multi->all()); + } + + public function testsResetRemovesRequestsAndResetsState() + { + $this->multi->add(new Request('GET', 'http://www.google.com/')); + $this->multi->reset(); + $this->assertEquals(array(), $this->multi->all()); + } + + public function testSendsRequestsThroughCurl() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n" . + "data" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $request2 = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->send(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + + $this->assertTrue($response1->getBody(true) == 'data' || $response2->getBody(true) == 'data'); + $this->assertTrue($response1->getBody(true) == '' || $response2->getBody(true) == ''); + $this->assertTrue($response1->getStatusCode() == '204' || $response2->getStatusCode() == '204'); + $this->assertNotEquals((string) $response1, (string) $response2); + } + + public function testSendsThroughCurlAndAggregatesRequestExceptions() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n" . + "data", + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n", + "HTTP/1.1 404 Not Found\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $request2 = new Request('HEAD', $this->getServer()->getUrl()); + $request3 = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->add($request3); + + try { + $this->multi->send(); + $this->fail('MultiTransferException not thrown when aggregating request exceptions'); + } catch (MultiTransferException $e) { + + $this->assertTrue($e->containsRequest($request1)); + $this->assertTrue($e->containsRequest($request2)); + $this->assertTrue($e->containsRequest($request3)); + $this->assertInstanceOf('ArrayIterator', $e->getIterator()); + $this->assertEquals(1, count($e)); + $exceptions = $e->getIterator(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + $response3 = $request3->getResponse(); + + $this->assertNotEquals((string) $response1, (string) $response2); + $this->assertNotEquals((string) $response3, (string) $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response3); + + $failed = $exceptions[0]->getResponse(); + $this->assertEquals(404, $failed->getStatusCode()); + $this->assertEquals(1, count($e)); + + // Test the IteratorAggregate functionality + foreach ($e as $except) { + $this->assertEquals($failed, $except->getResponse()); + } + + $this->assertEquals(1, count($e->getFailedRequests())); + $this->assertEquals(2, count($e->getSuccessfulRequests())); + $this->assertEquals(3, count($e->getAllRequests())); + } + } + + public function testCurlErrorsAreCaught() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + try { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $request->setClient(new Client()); + $request->getCurlOptions()->set(CURLOPT_FRESH_CONNECT, true); + $request->getCurlOptions()->set(CURLOPT_FORBID_REUSE, true); + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, 5); + $request->send(); + $this->fail('CurlException not thrown'); + } catch (CurlException $e) { + $m = $e->getMessage(); + $this->assertContains('[curl] ', $m); + $this->assertContains('[url] http://127.0.0.1:9876/', $m); + $this->assertInternalType('array', $e->getCurlInfo()); + } + } + + public function testRemovesQueuedRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $r = new Response(200); + $request->setClient(new Client()); + $request->setResponse($r, true); + $this->multi->add($request); + $this->multi->send(); + $this->assertSame($r, $request->getResponse()); + } + + public function testRemovesQueuedRequestsAddedInTransit() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.receive.status_line', function (Event $event) use ($client) { + // Create a request using a queued response + $request = $client->get()->setResponse(new Response(200), true); + $request->send(); + }); + $r->send(); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + public function testCatchesExceptionsBeforeSendingSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $multi = new CurlMulti(); + $client->setCurlMulti($multi); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function() { + throw new \RuntimeException('Testing!'); + }); + try { + $request->send(); + $this->fail('Did not throw'); + } catch (\RuntimeException $e) { + // Ensure it was removed + $this->assertEquals(0, count($multi)); + } + } + + /** + * @expectedException \Guzzle\Common\Exception\ExceptionCollection + * @expectedExceptionMessage Thrown before sending! + */ + public function testCatchesExceptionsBeforeSendingMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function() { + throw new \RuntimeException('Thrown before sending!'); + }); + $client->send(array($request)); + } + + public function testCatchesExceptionsWhenRemovingQueuedRequests() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.sent', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + try { + $r->send(); + $this->fail('Did not throw'); + } catch (BadResponseException $e) { + $this->assertCount(0, $client->getCurlMulti()); + } + } + + public function testCatchesExceptionsWhenRemovingQueuedRequestsBeforeSending() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.before_send', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + try { + $r->send(); + $this->fail('Did not throw'); + } catch (BadResponseException $e) { + $this->assertCount(0, $client->getCurlMulti()); + } + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage test + */ + public function testDoesNotCatchRandomExceptionsThrownDuringPerform() + { + $client = new Client($this->getServer()->getUrl()); + $multi = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('perform')); + $multi->expects($this->once()) + ->method('perform') + ->will($this->throwException(new \RuntimeException('test'))); + $multi->add($client->get()); + $multi->send(); + } + + public function testDoesNotSendRequestsDecliningToBeSent() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + // Create a client that is bound to fail connecting + $client = new Client('http://127.0.0.1:123', array( + 'curl.CURLOPT_PORT' => 123, + 'curl.CURLOPT_CONNECTTIMEOUT_MS' => 1, + )); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + + // Listen for request exceptions, and when they occur, first change the + // state of the request back to transferring, and then just allow it to + // exception out + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use ($multi) { + $retries = $event['request']->getParams()->get('retries'); + // Allow the first failure to retry + if ($retries == 0) { + $event['request']->setState('transfer'); + $event['request']->getParams()->set('retries', 1); + // Remove the request to try again + $multi->remove($event['request']); + $multi->add($event['request']); + } + }); + + try { + $multi->send(); + $this->fail('Did not throw an exception at all!?!'); + } catch (\Exception $e) { + $this->assertEquals(1, $request->getParams()->get('retries')); + } + } + + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithRetry() + { + $this->getServer()->flush(); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function(Event $event) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + } + + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithSuccess() + { + // Attempt a port that 99.9% is not listening + $client = new Client('http://127.0.0.1:123'); + $request = $client->get(); + // Ensure it times out quickly if needed + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, 1)->set(CURLOPT_CONNECTTIMEOUT_MS, 1); + + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use (&$count) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + + // Ensure that the exception was caught, and the response was set manually + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + } + + public function testHardResetReopensMultiHandle() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $stream = fopen('php://temp', 'w+'); + $client = new Client($this->getServer()->getUrl()); + $client->getConfig()->set('curl.CURLOPT_VERBOSE', true)->set('curl.CURLOPT_STDERR', $stream); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $multi->reset(true); + $multi->add($request); + $multi->send(); + + rewind($stream); + $this->assertNotContains('Re-using existing connection', stream_get_contents($stream)); + } + + public function testThrowsMeaningfulExceptionsForCurlMultiErrors() + { + $multi = new CurlMulti(); + + // Set the state of the multi object to sending to trigger the exception + $reflector = new \ReflectionMethod('Guzzle\Http\Curl\CurlMulti', 'checkCurlResult'); + $reflector->setAccessible(true); + + // Successful + $reflector->invoke($multi, 0); + + // Known error + try { + $reflector->invoke($multi, CURLM_BAD_HANDLE); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertContains('The passed-in handle is not a valid CURLM handle.', $e->getMessage()); + $this->assertContains('CURLM_BAD_HANDLE', $e->getMessage()); + $this->assertContains(strval(CURLM_BAD_HANDLE), $e->getMessage()); + } + + // Unknown error + try { + $reflector->invoke($multi, 255); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertEquals('Unexpected cURL error: 255', $e->getMessage()); + } + } + + public function testRequestBeforeSendIncludesContentLengthHeaderIfEmptyBody() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = new Request('PUT', $this->getServer()->getUrl()); + $that = $this; + $request->getEventDispatcher()->addListener('request.before_send', function ($event) use ($that) { + $that->assertEquals(0, $event['request']->getHeader('Content-Length')); + }); + $this->multi->add($request); + $this->multi->send(); + } + + public function testRemovesConflictingTransferEncodingHeader() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, fopen($this->getServer()->getUrl(), 'r')); + $request->setHeader('Content-Length', 4); + $request->send(); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($received[1]->hasHeader('Transfer-Encoding')); + $this->assertEquals(4, (string) $received[1]->getHeader('Content-Length')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php new file mode 100755 index 000000000..c7b5ee6e9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php @@ -0,0 +1,39 @@ +getProperty('version'); + $refProperty->setAccessible(true); + $refProperty->setValue($instance, array()); + + $this->assertEquals($info, $instance->getAll()); + $this->assertEquals($info, $instance->getAll()); + + $this->assertEquals($info['version'], $instance->get('version')); + $this->assertFalse($instance->get('foo')); + } + + public function testIsSingleton() + { + $refObject = new \ReflectionClass('Guzzle\Http\Curl\CurlVersion'); + $refProperty = $refObject->getProperty('instance'); + $refProperty->setAccessible(true); + $refProperty->setValue(null, null); + + $this->assertInstanceOf('Guzzle\Http\Curl\CurlVersion', CurlVersion::getInstance()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php new file mode 100755 index 000000000..c69e0c904 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php @@ -0,0 +1,67 @@ +events[] = $event; + } + + public function testEmitsEvents() + { + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('foo'); + $request->setResponse(new Response(200)); + + // Ensure that IO events are emitted + $request->getCurlOptions()->set('emit_io', true); + + // Attach listeners for each event type + $request->getEventDispatcher()->addListener('curl.callback.progress', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.read', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.write', array($this, 'event')); + + $mediator = new RequestMediator($request, true); + + $mediator->progress('a', 'b', 'c', 'd'); + $this->assertEquals(1, count($this->events)); + $this->assertEquals('curl.callback.progress', $this->events[0]->getName()); + + $this->assertEquals(3, $mediator->writeResponseBody('foo', 'bar')); + $this->assertEquals(2, count($this->events)); + $this->assertEquals('curl.callback.write', $this->events[1]->getName()); + $this->assertEquals('bar', $this->events[1]['write']); + $this->assertSame($request, $this->events[1]['request']); + + $this->assertEquals('foo', $mediator->readRequestBody('a', 'b', 3)); + $this->assertEquals(3, count($this->events)); + $this->assertEquals('curl.callback.read', $this->events[2]->getName()); + $this->assertEquals('foo', $this->events[2]['read']); + $this->assertSame($request, $this->events[2]['request']); + } + + public function testDoesNotUseRequestResponseBodyWhenNotCustom() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 307 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nHI", + "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nFI", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", + )); + $client = new Client($this->getServer()->getUrl()); + $response = $client->get()->send(); + $this->assertEquals('test', $response->getBody(true)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php new file mode 100755 index 000000000..124a44da6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php @@ -0,0 +1,182 @@ +assertEquals('data', (string) $body); + $this->assertEquals(4, $body->getContentLength()); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + + $handle = fopen(__DIR__ . '/../../../../phpunit.xml.dist', 'r'); + if (!$handle) { + $this->fail('Could not open test file'); + } + $body = EntityBody::factory($handle); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertTrue($body->isLocal()); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertEquals(filesize(__DIR__ . '/../../../../phpunit.xml.dist'), $body->getContentLength()); + + // make sure that a body will return as the same object + $this->assertTrue($body === EntityBody::factory($body)); + } + + public function testFactoryCreatesTempStreamByDefault() + { + $body = EntityBody::factory(''); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + $body = EntityBody::factory(); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + } + + public function testFactoryCanCreateFromObject() + { + $body = EntityBody::factory(new QueryString(array('foo' => 'bar'))); + $this->assertEquals('foo=bar', (string) $body); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testFactoryEnsuresObjectsHaveToStringMethod() + { + EntityBody::factory(new \stdClass('a')); + } + + public function testHandlesCompression() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must initially return FALSE'); + $size = $body->getContentLength(); + $body->compress(); + $this->assertEquals('gzip', $body->getContentEncoding(), '-> getContentEncoding() must return the correct encoding after compressing'); + $this->assertEquals(gzdeflate('testing 123...testing 123'), (string) $body); + $this->assertTrue($body->getContentLength() < $size); + $this->assertTrue($body->uncompress()); + $this->assertEquals('testing 123...testing 123', (string) $body); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must reset to FALSE'); + + if (in_array('bzip2.*', stream_get_filters())) { + $this->assertTrue($body->compress('bzip2.compress')); + $this->assertEquals('compress', $body->getContentEncoding(), '-> compress() must set \'compress\' as the Content-Encoding'); + } + + $this->assertFalse($body->compress('non-existent'), '-> compress() must return false when a non-existent stream filter is used'); + + // Release the body + unset($body); + + // Use gzip compression on the initial content. This will include a + // gzip header which will need to be stripped when deflating the stream + $body = EntityBody::factory(gzencode('test')); + $this->assertSame($body, $body->setStreamFilterContentEncoding('zlib.deflate')); + $this->assertTrue($body->uncompress('zlib.inflate')); + $this->assertEquals('test', (string) $body); + unset($body); + + // Test using a very long string + $largeString = ''; + for ($i = 0; $i < 25000; $i++) { + $largeString .= chr(rand(33, 126)); + } + $body = EntityBody::factory($largeString); + $this->assertEquals($largeString, (string) $body); + $this->assertTrue($body->compress()); + $this->assertNotEquals($largeString, (string) $body); + $compressed = (string) $body; + $this->assertTrue($body->uncompress()); + $this->assertEquals($largeString, (string) $body); + $this->assertEquals($compressed, gzdeflate($largeString)); + + $body = EntityBody::factory(fopen(__DIR__ . '/../TestData/compress_test', 'w')); + $this->assertFalse($body->compress()); + unset($body); + + unlink(__DIR__ . '/../TestData/compress_test'); + } + + public function testDeterminesContentType() + { + // Test using a string/temp stream + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertNull($body->getContentType()); + + // Use a local file + $body = EntityBody::factory(fopen(__FILE__, 'r')); + $this->assertContains('text/x-', $body->getContentType()); + } + + public function testCreatesMd5Checksum() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertEquals(md5('testing 123...testing 123'), $body->getContentMd5()); + + $server = $this->getServer()->enqueue( + "HTTP/1.1 200 OK" . "\r\n" . + "Content-Length: 3" . "\r\n\r\n" . + "abc" + ); + + $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); + $this->assertFalse($body->getContentMd5()); + } + + public function testSeeksToOriginalPosAfterMd5() + { + $body = EntityBody::factory('testing 123'); + $body->seek(4); + $this->assertEquals(md5('testing 123'), $body->getContentMd5()); + $this->assertEquals(4, $body->ftell()); + $this->assertEquals('ing 123', $body->read(1000)); + } + + public function testGetTypeFormBodyFactoring() + { + $body = EntityBody::factory(array('key1' => 'val1', 'key2' => 'val2')); + $this->assertEquals('key1=val1&key2=val2', (string) $body); + } + + public function testAllowsCustomRewind() + { + $body = EntityBody::factory('foo'); + $rewound = false; + $body->setRewindFunction(function ($body) use (&$rewound) { + $rewound = true; + return $body->seek(0); + }); + $body->seek(2); + $this->assertTrue($body->rewind()); + $this->assertTrue($rewound); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testCustomRewindFunctionMustBeCallable() + { + $body = EntityBody::factory(); + $body->setRewindFunction('foo'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php new file mode 100755 index 000000000..df3e4b791 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php @@ -0,0 +1,27 @@ +assertNull($e->getError()); + $this->assertNull($e->getErrorNo()); + $this->assertSame($e, $e->setError('test', 12)); + $this->assertEquals('test', $e->getError()); + $this->assertEquals(12, $e->getErrorNo()); + + $handle = new CurlHandle(curl_init(), array()); + $e->setCurlHandle($handle); + $this->assertSame($handle, $e->getCurlHandle()); + $handle->close(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php new file mode 100755 index 000000000..12cfd369e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php @@ -0,0 +1,66 @@ +setRequest($request); + $this->assertEquals($request, $e->getRequest()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException + */ + public function testBadResponseException() + { + $e = new BadResponseException('Message'); + $response = new Response(200); + $e->setResponse($response); + $this->assertEquals($response, $e->getResponse()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesGenericErrorExceptionOnError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(307); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\BadResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesClientErrorExceptionOnClientError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(404); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ClientErrorResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesServerErrorExceptionOnServerError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(503); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ServerErrorResponseException', $e); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php new file mode 100755 index 000000000..fa4ec2626 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php @@ -0,0 +1,51 @@ +addSuccessfulRequest($r1); + $e->addFailedRequest($r2); + $this->assertEquals(array($r1), $e->getSuccessfulRequests()); + $this->assertEquals(array($r2), $e->getSuccessfulRequests()); + $this->assertEquals(array($r1, $r2), $e->getAllRequests()); + $this->assertTrue($e->containsRequest($r1)); + $this->assertTrue($e->containsRequest($r2)); + $this->assertFalse($e->containsRequest(new Request('POST', '/foo'))); + } + + public function testCanSetRequests() + { + $s = array($r1 = new Request('GET', 'http://www.foo.com')); + $f = array($r2 = new Request('GET', 'http://www.foo.com')); + $e = new MultiTransferException(); + $e->setSuccessfulRequests($s); + $e->setFailedRequests($f); + $this->assertEquals(array($r1), $e->getSuccessfulRequests()); + $this->assertEquals(array($r2), $e->getSuccessfulRequests()); + } + + public function testAssociatesExceptionsWithRequests() + { + $r1 = new Request('GET', 'http://www.foo.com'); + $re1 = new \Exception('foo'); + $re2 = new \Exception('bar'); + $e = new MultiTransferException(); + $e->add($re2); + $e->addFailedRequestWithException($r1, $re1); + $this->assertSame($re1, $e->getExceptionForFailedRequest($r1)); + $this->assertNull($e->getExceptionForFailedRequest(new Request('POST', '/foo'))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php new file mode 100755 index 000000000..cd6355f3b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php @@ -0,0 +1,47 @@ +decorated = EntityBody::factory('hello'); + $this->body = new IoEmittingEntityBody($this->decorated); + } + + public function testEmitsReadEvents() + { + $e = null; + $this->body->getEventDispatcher()->addListener('body.read', function ($event) use (&$e) { + $e = $event; + }); + $this->assertEquals('hel', $this->body->read(3)); + $this->assertEquals('hel', $e['read']); + $this->assertEquals(3, $e['length']); + $this->assertSame($this->body, $e['body']); + } + + public function testEmitsWriteEvents() + { + $e = null; + $this->body->getEventDispatcher()->addListener('body.write', function ($event) use (&$e) { + $e = $event; + }); + $this->body->seek(0, SEEK_END); + $this->assertEquals(5, $this->body->write('there')); + $this->assertEquals('there', $e['write']); + $this->assertEquals(5, $e['result']); + $this->assertSame($this->body, $e['body']); + $this->assertEquals('hellothere', (string) $this->body); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php new file mode 100755 index 000000000..9447d8c5d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php @@ -0,0 +1,136 @@ +mock = $this->getMockForAbstractClass('Guzzle\Http\Message\AbstractMessage'); + } + + public function tearDown() + { + $this->mock = $this->request = null; + } + + public function testGetParams() + { + $request = new Request('GET', 'http://example.com'); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $request->getParams()); + } + + public function testAddHeaders() + { + $this->mock->setHeader('A', 'B'); + + $this->assertEquals($this->mock, $this->mock->addHeaders(array( + 'X-Data' => '123' + ))); + + $this->assertTrue($this->mock->hasHeader('X-Data') !== false); + $this->assertTrue($this->mock->hasHeader('A') !== false); + } + + public function testAllowsHeaderToSetAsHeader() + { + $h = new Header('A', 'B'); + $this->mock->setHeader('A', $h); + $this->assertSame($h, $this->mock->getHeader('A')); + } + + public function testGetHeader() + { + $this->mock->setHeader('Test', '123'); + $this->assertEquals('123', $this->mock->getHeader('Test')); + } + + public function testGetHeaders() + { + $this->assertSame($this->mock, $this->mock->setHeaders(array('a' => 'b', 'c' => 'd'))); + $h = $this->mock->getHeaders(); + $this->assertArrayHasKey('a', $h->toArray()); + $this->assertArrayHasKey('c', $h->toArray()); + $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('a')); + $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('c')); + } + + public function testGetHeaderLinesUsesGlue() + { + $this->mock->setHeaders(array('a' => 'b', 'c' => 'd')); + $this->mock->addHeader('a', 'e'); + $this->mock->getHeader('a')->setGlue('!'); + $this->assertEquals(array( + 'a: b! e', + 'c: d' + ), $this->mock->getHeaderLines()); + } + + public function testHasHeader() + { + $this->assertFalse($this->mock->hasHeader('Foo')); + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->setHeader('foo', 'yoo'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->assertEquals(true, $this->mock->hasHeader('foo')); + $this->assertEquals(false, $this->mock->hasHeader('bar')); + } + + public function testRemoveHeader() + { + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->removeHeader('Foo'); + $this->assertFalse($this->mock->hasHeader('Foo')); + } + + public function testReturnsNullWhenHeaderIsNotFound() + { + $this->assertNull($this->mock->getHeader('foo')); + } + + public function testAddingHeadersPreservesOriginalHeaderCase() + { + $this->mock->addHeaders(array( + 'test' => '123', + 'Test' => 'abc' + )); + $this->mock->addHeader('test', '456'); + $this->mock->addHeader('test', '789'); + + $header = $this->mock->getHeader('test'); + $this->assertContains('123', $header->toArray()); + $this->assertContains('456', $header->toArray()); + $this->assertContains('789', $header->toArray()); + $this->assertContains('abc', $header->toArray()); + } + + public function testCanStoreEmptyHeaders() + { + $this->mock->setHeader('Content-Length', 0); + $this->assertTrue($this->mock->hasHeader('Content-Length')); + $this->assertEquals(0, (string) $this->mock->getHeader('Content-Length')); + } + + public function testCanSetCustomHeaderFactory() + { + $f = new Header\HeaderFactory(); + $this->mock->setHeaderFactory($f); + $this->assertSame($f, $this->readAttribute($this->mock, 'headerFactory')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php new file mode 100755 index 000000000..191b02223 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php @@ -0,0 +1,434 @@ +client = new Client(); + } + + public function tearDown() + { + $this->client = null; + } + + public function testConstructorConfiguresRequest() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com', array( + 'X-Test' => '123' + )); + $request->setBody('Test'); + $this->assertEquals('123', $request->getHeader('X-Test')); + $this->assertNull($request->getHeader('Expect')); + } + + public function testCanSetBodyWithoutOverridingContentType() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com', array('Content-Type' => 'foooooo')); + $request->setBody('{"a":"b"}'); + $this->assertEquals('foooooo', $request->getHeader('Content-Type')); + } + + public function testRequestIncludesBodyInMessage() + { + + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $this->assertEquals("PUT / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Length: 4\r\n\r\n" + . "data", (string) $request); + } + + public function testRequestIncludesPostBodyInMessageOnlyWhenNoPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => 'bar' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n" + . "foo=bar", (string) $request); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => '@' . __FILE__ + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: multipart/form-data\r\n" + . "Expect: 100-Continue\r\n\r\n", (string) $request); + } + + public function testAddsPostFieldsAndSetsContentLength() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'data' => '123' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n" + . "data=123", (string) $request); + } + + public function testAddsPostFilesAndSetsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/') + ->addPostFiles(array( + 'file' => __FILE__ + ))->addPostFields(array( + 'a' => 'b' + )); + $message = (string) $request; + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + $this->assertEquals('100-Continue', $request->getHeader('Expect')); + } + + public function testRequestBodyContainsPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/'); + $request->addPostFields(array( + 'test' => '123' + )); + $this->assertContains("\r\n\r\ntest=123", (string) $request); + } + + public function testRequestBodyAddsContentLength() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/'); + $request->setBody(EntityBody::factory('test')); + $this->assertEquals(4, (string) $request->getHeader('Content-Length')); + $this->assertFalse($request->hasHeader('Transfer-Encoding')); + } + + public function testRequestBodyDoesNotUseContentLengthWhenChunked() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'test'); + $this->assertNull($request->getHeader('Content-Length')); + $this->assertTrue($request->hasHeader('Transfer-Encoding')); + } + + public function testRequestHasMutableBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $body = $request->getBody(); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $body); + $this->assertSame($body, $request->getBody()); + + $newBody = EntityBody::factory('foobar'); + $request->setBody($newBody); + $this->assertEquals('foobar', (string) $request->getBody()); + $this->assertSame($newBody, $request->getBody()); + } + + public function testSetPostFields() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $request->getPostFields()); + + $fields = new QueryString(array( + 'a' => 'b' + )); + $request->addPostFields($fields); + $this->assertEquals($fields->getAll(), $request->getPostFields()->getAll()); + $this->assertEquals(array(), $request->getPostFiles()); + } + + public function testSetPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()) + ->setClient(new Client()) + ->addPostFiles(array(__FILE__)) + ->addPostFields(array( + 'test' => 'abc' + )); + + $request->getCurlOptions()->set('debug', true); + + $this->assertEquals(array( + 'test' => 'abc' + ), $request->getPostFields()->getAll()); + + $files = $request->getPostFiles(); + $post = $files['file'][0]; + $this->assertEquals('file', $post->getFieldName()); + $this->assertContains('text/x-', $post->getContentType()); + $this->assertEquals(__FILE__, $post->getFilename()); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + $this->assertNotNull($request->getHeader('Content-Length')); + $this->assertContains('multipart/form-data; boundary=', (string) $request->getHeader('Content-Type'), '-> cURL must add the boundary'); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testSetPostFilesThrowsExceptionWhenFileIsNotFound() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array( + 'file' => 'filenotfound.ini' + )); + } + + /** + * @expectedException Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenNonStringsAreAddedToPost() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', new \stdClass()); + } + + public function testAllowsContentTypeInPostUploads() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__, 'text/plain'); + + $this->assertEquals(array( + new PostFile('foo', __FILE__, 'text/plain') + ), $request->getPostFile('foo')); + } + + public function testGuessesContentTypeOfPostUpload() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__); + $file = $request->getPostFile('foo'); + $this->assertContains('text/x-', $file[0]->getContentType()); + } + + public function testAllowsContentDispositionFieldsInPostUploadsWhenSettingInBulk() + { + $postFile = new PostFile('foo', __FILE__, 'text/x-php'); + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array('foo' => $postFile)); + + $this->assertEquals(array($postFile), $request->getPostFile('foo')); + } + + public function testPostRequestsUseApplicationXwwwForUrlEncodedForArrays() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertContains("\r\n\r\na=b", (string) $request); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type')); + } + + public function testProcessMethodAddsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type')); + } + + public function testPostRequestsUseMultipartFormDataWithFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->addPostFiles(array('file' => __FILE__)); + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + } + + public function testCanSendMultipleRequestsUsingASingleRequestObject() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n", + )); + + // Send the first request + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()) + ->setBody('test') + ->setClient(new Client()); + $request->send(); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + + // Send the second request + $request->setBody('abcdefg', 'application/json', false); + $request->send(); + $this->assertEquals(201, $request->getResponse()->getStatusCode()); + + // Ensure that the same request was sent twice with different bodies + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($requests)); + $this->assertEquals(4, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals(7, (string) $requests[1]->getHeader('Content-Length')); + } + + public function testRemovingPostFieldRebuildsPostFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com'); + $request->setPostField('test', 'value'); + $request->removePostField('test'); + $this->assertNull($request->getPostField('test')); + } + + public function testUsesChunkedTransferWhenBodyLengthCannotBeDetermined() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenContentLengthCannotBeDeterminedAndUsingHttp1() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->setProtocolVersion('1.0'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + } + + public function testAllowsNestedPostData() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => array('b', 'c') + )); + $this->assertEquals(array( + 'a' => array('b', 'c') + ), $request->getPostFields()->getAll()); + } + + public function testAllowsEmptyFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '' + )); + $this->assertEquals(array( + 'a' => '' + ), $request->getPostFields()->getAll()); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + */ + public function testFailsOnInvalidFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'a' => new \stdClass() + )); + } + + public function testHandlesEmptyStrings() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + )); + $this->assertEquals(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + ), $request->getPostFields()->getAll()); + } + + public function testHoldsPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFile('foo', __FILE__); + $request->addPostFile(new PostFile('foo', __FILE__)); + + $this->assertArrayHasKey('foo', $request->getPostFiles()); + $foo = $request->getPostFile('foo'); + $this->assertEquals(2, count($foo)); + $this->assertEquals(__FILE__, $foo[0]->getFilename()); + $this->assertEquals(__FILE__, $foo[1]->getFilename()); + + $request->removePostFile('foo'); + $this->assertEquals(array(), $request->getPostFiles()); + } + + public function testAllowsAtPrefixWhenAddingPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'foo' => '@' . __FILE__ + )); + $foo = $request->getPostFile('foo'); + $this->assertEquals(__FILE__, $foo[0]->getFilename()); + } + + public function testSetStateToTransferWithEmptyBodySetsContentLengthToZero() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->setState($request::STATE_TRANSFER); + $this->assertEquals('0', (string) $request->getHeader('Content-Length')); + } + + public function testSettingExpectHeaderCutoffChangesRequest() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->setHeader('Expect', '100-Continue'); + $request->setExpectHeaderCutoff(false); + $this->assertNull($request->getHeader('Expect')); + // There is not body, so remove the expect header + $request->setHeader('Expect', '100-Continue'); + $request->setExpectHeaderCutoff(10); + $this->assertNull($request->getHeader('Expect')); + // The size is less than the cutoff + $request->setBody('foo'); + $this->assertNull($request->getHeader('Expect')); + // The size is greater than the cutoff + $request->setBody('foobazbarbamboo'); + $this->assertNotNull($request->getHeader('Expect')); + } + + public function testStrictRedirectsCanBeSpecifiedOnEntityEnclosingRequests() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->configureRedirects(true); + $this->assertTrue($request->getParams()->get(RedirectPlugin::STRICT_REDIRECTS)); + } + + public function testCanDisableRedirects() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->configureRedirects(false, false); + $this->assertTrue($request->getParams()->get(RedirectPlugin::DISABLE)); + } + + public function testSetsContentTypeWhenSettingBodyByGuessingFromEntityBody() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); + $request->setBody(EntityBody::factory(fopen(__FILE__, 'r'))); + $this->assertEquals('text/x-php', (string) $request->getHeader('Content-Type')); + } + + public function testDoesNotCloneBody() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); + $request->setBody('test'); + $newRequest = clone $request; + $newRequest->setBody('foo'); + $this->assertInternalType('string', (string) $request->getBody()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php new file mode 100755 index 000000000..62ca5559d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php @@ -0,0 +1,29 @@ +createHeader('Foo', 'Bar'); + $this->assertInstanceOf('Guzzle\Http\Message\Header', $h); + $this->assertEquals('Foo', $h->getName()); + $this->assertEquals('Bar', (string) $h); + } + + public function testCreatesSpecificHeaders() + { + $f = new HeaderFactory(); + $h = $f->createHeader('Link', '; rel="test"'); + $this->assertInstanceOf('Guzzle\Http\Message\Header\Link', $h); + $this->assertEquals('Link', $h->getName()); + $this->assertEquals('; rel="test"', (string) $h); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php new file mode 100755 index 000000000..c834d1016 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php @@ -0,0 +1,63 @@ +; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg", ; rel=side; type="image/jpeg"'); + $links = $link->getLinks(); + $this->assertEquals(array( + array( + 'rel' => 'front', + 'type' => 'image/jpeg', + 'url' => 'http:/.../front.jpeg', + ), + array( + 'rel' => 'back', + 'type' => 'image/jpeg', + 'url' => 'http://.../back.jpeg', + ), + array( + 'rel' => 'side', + 'type' => 'image/jpeg', + 'url' => 'http://.../side.jpeg?test=1' + ) + ), $links); + + $this->assertEquals(array( + 'rel' => 'back', + 'type' => 'image/jpeg', + 'url' => 'http://.../back.jpeg', + ), $link->getLink('back')); + + $this->assertTrue($link->hasLink('front')); + $this->assertFalse($link->hasLink('foo')); + } + + public function testCanAddLink() + { + $link = new Link('Link', '; rel=a; type="image/jpeg"'); + $link->addLink('http://test.com', 'test', array('foo' => 'bar')); + $this->assertEquals( + '; rel=a; type="image/jpeg", ; rel="test"; foo="bar"', + (string) $link + ); + } + + public function testCanParseLinksWithCommas() + { + $link = new Link('Link', '; rel="previous"; title="start, index"'); + $this->assertEquals(array( + array( + 'rel' => 'previous', + 'title' => 'start, index', + 'url' => 'http://example.com/TheBook/chapter1', + ) + ), $link->getLinks()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php new file mode 100755 index 000000000..a3f511bfc --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php @@ -0,0 +1,135 @@ +toArray(); + } + + foreach ($filteredHeaders as $k => $v) { + if ($k[0] == '_') { + // This header should be ignored + $ignore[] = str_replace('_', '', $k); + } elseif ($k[0] == '!') { + // This header must not be present + $absent[] = str_replace('!', '', $k); + } else { + $expected[$k] = $v; + } + } + + return $this->compareArray($expected, $actualHeaders, $ignore, $absent); + } + + /** + * Check if an array of HTTP headers matches another array of HTTP headers while taking * into account as a wildcard + * + * @param array $expected Expected HTTP headers (allows wildcard values) + * @param array|Collection $actual Actual HTTP header array + * @param array $ignore Headers to ignore from the comparison + * @param array $absent Array of headers that must not be present + * + * @return array|bool Returns an array of the differences or FALSE if none + */ + public function compareArray(array $expected, $actual, array $ignore = array(), array $absent = array()) + { + $differences = array(); + + // Add information about headers that were present but weren't supposed to be + foreach ($absent as $header) { + if ($this->hasKey($header, $actual)) { + $differences["++ {$header}"] = $actual[$header]; + unset($actual[$header]); + } + } + + // Check if expected headers are missing + foreach ($expected as $header => $value) { + if (!$this->hasKey($header, $actual)) { + $differences["- {$header}"] = $value; + } + } + + // Flip the ignore array so it works with the case insensitive helper + $ignore = array_flip($ignore); + // Allow case-insensitive comparisons in wildcards + $expected = array_change_key_case($expected); + + // Compare the expected and actual HTTP headers in no particular order + foreach ($actual as $key => $value) { + + // If this is to be ignored, the skip it + if ($this->hasKey($key, $ignore)) { + continue; + } + + // If the header was not expected + if (!$this->hasKey($key, $expected)) { + $differences["+ {$key}"] = $value; + continue; + } + + // Check values and take wildcards into account + $lkey = strtolower($key); + $pos = is_string($expected[$lkey]) ? strpos($expected[$lkey], '*') : false; + + foreach ((array) $actual[$key] as $v) { + if (($pos === false && $v != $expected[$lkey]) || $pos > 0 && substr($v, 0, $pos) != substr($expected[$lkey], 0, $pos)) { + $differences[$key] = "{$value} != {$expected[$lkey]}"; + } + } + } + + return empty($differences) ? false : $differences; + } + + /** + * Case insensitive check if an array have a key + * + * @param string $key Key to check + * @param array $array Array to check + * + * @return bool + */ + protected function hasKey($key, $array) + { + if ($array instanceof Collection) { + $keys = $array->getKeys(); + } else { + $keys = array_keys($array); + } + + foreach ($keys as $k) { + if (!strcasecmp($k, $key)) { + return true; + } + } + + return false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php new file mode 100755 index 000000000..86c4fe866 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php @@ -0,0 +1,115 @@ + 'Foo' + ), array( + 'Content-Length' => 'Foo' + ), false), + + // Missing header + array(array( + 'X-Foo' => 'Bar' + ), array(), array( + '- X-Foo' => 'Bar' + )), + + // Extra headers is present + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Bar', + 'X-Baz' => 'Jar' + ), array( + '+ X-Baz' => 'Jar' + )), + + // Header is present but must be absent + array(array( + '!X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), array( + '++ X-Foo' => 'Bar' + )), + + // Different values + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Baz' + ), array( + 'X-Foo' => 'Baz != Bar' + )), + + // Wildcard search passes + array(array( + 'X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), false), + + // Wildcard search fails + array(array( + 'X-Foo' => '*' + ), array(), array( + '- X-Foo' => '*' + )), + + // Ignore extra header if present + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz', + 'X-Bar' => 'Jar' + ), false), + + // Ignore extra header if present and is not + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz' + ), false), + + // Case insensitive + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + ), false), + + // Case insensitive with collection + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), new Collection(array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + )), false), + ); + } + + /** + * @dataProvider filterProvider + */ + public function testComparesHeaders($filters, $headers, $result) + { + $compare = new HeaderComparison(); + $this->assertEquals($result, $compare->compare($filters, $headers)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php new file mode 100755 index 000000000..c7502349f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php @@ -0,0 +1,162 @@ + array('foo', 'Foo'), + 'Zoo' => 'bar', + ); + + public function testStoresHeaderName() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('Zoo', $i->getName()); + } + + public function testConvertsToString() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('foo, Foo, bar', (string) $i); + $i->setGlue(';'); + $this->assertEquals('foo; Foo; bar', (string) $i); + } + + public function testNormalizesGluedHeaders() + { + $h = new Header('Zoo', array('foo, Faz', 'bar')); + $result = $h->normalize(true)->toArray(); + natsort($result); + $this->assertEquals(array('bar', 'foo', 'Faz'), $result); + } + + public function testCanSearchForValues() + { + $h = new Header('Zoo', $this->test); + $this->assertTrue($h->hasValue('foo')); + $this->assertTrue($h->hasValue('Foo')); + $this->assertTrue($h->hasValue('bar')); + $this->assertFalse($h->hasValue('moo')); + $this->assertFalse($h->hasValue('FoO')); + } + + public function testIsCountable() + { + $h = new Header('Zoo', $this->test); + $this->assertEquals(3, count($h)); + } + + public function testCanBeIterated() + { + $h = new Header('Zoo', $this->test); + $results = array(); + foreach ($h as $key => $value) { + $results[$key] = $value; + } + $this->assertEquals(array( + 'foo', 'Foo', 'bar' + ), $results); + } + + public function testAllowsFalseyValues() + { + // Allows 0 + $h = new Header('Foo', 0, ';'); + $this->assertEquals('0', (string) $h); + $this->assertEquals(1, count($h)); + $this->assertEquals(';', $h->getGlue()); + + // Does not add a null header by default + $h = new Header('Foo'); + $this->assertEquals('', (string) $h); + $this->assertEquals(0, count($h)); + + // Allows null array for a single null header + $h = new Header('Foo', array(null)); + $this->assertEquals('', (string) $h); + + // Allows empty string + $h = new Header('Foo', ''); + $this->assertEquals('', (string) $h); + $this->assertEquals(1, count($h)); + $this->assertEquals(1, count($h->normalize()->toArray())); + } + + public function testCanRemoveValues() + { + $h = new Header('Foo', array('Foo', 'baz', 'bar')); + $h->removeValue('bar'); + $this->assertTrue($h->hasValue('Foo')); + $this->assertFalse($h->hasValue('bar')); + $this->assertTrue($h->hasValue('baz')); + } + + public function testAllowsArrayInConstructor() + { + $h = new Header('Foo', array('Testing', '123', 'Foo=baz')); + $this->assertEquals(array('Testing', '123', 'Foo=baz'), $h->toArray()); + } + + public function parseParamsProvider() + { + $res1 = array( + array( + '' => '', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + array( + '' => '', + 'rel' => 'back', + 'type' => 'image/jpeg', + ), + ); + + return array( + array( + '; rel="front"; type="image/jpeg", ; rel=back; type="image/jpeg"', + $res1 + ), + array( + '; rel="front"; type="image/jpeg",; rel=back; type="image/jpeg"', + $res1 + ), + array( + 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', + array( + array('foo' => 'baz', 'bar' => '123'), + array('boo' => ''), + array('test' => '123'), + array('foobar' => 'foo;bar') + ) + ), + array( + '; rel="side"; type="image/jpeg",; rel=side; type="image/jpeg"', + array( + array('' => '', 'rel' => 'side', 'type' => 'image/jpeg'), + array('' => '', 'rel' => 'side', 'type' => 'image/jpeg') + ) + ), + array( + '', + array() + ) + ); + } + + /** + * @dataProvider parseParamsProvider + */ + public function testParseParams($header, $result) + { + $response = new Response(200, array('Link' => $header)); + $this->assertEquals($result, $response->getHeader('Link')->parseParams()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php new file mode 100755 index 000000000..be048cb95 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php @@ -0,0 +1,88 @@ +assertEquals('foo', $file->getFieldName()); + $this->assertEquals(__FILE__, $file->getFilename()); + $this->assertEquals('boo', $file->getPostName()); + $this->assertEquals('x-foo', $file->getContentType()); + } + + public function testRemovesLeadingAtSymbolFromPath() + { + $file = new PostFile('foo', '@' . __FILE__); + $this->assertEquals(__FILE__, $file->getFilename()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresFileIsReadable() + { + $file = new PostFile('foo', '/foo/baz/bar'); + } + + public function testCanChangeContentType() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setContentType('Boo'); + $this->assertEquals('Boo', $file->getContentType()); + } + + public function testCanChangeFieldName() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setFieldName('Boo'); + $this->assertEquals('Boo', $file->getFieldName()); + } + + public function testReturnsCurlValueString() + { + $file = new PostFile('foo', __FILE__); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=PostFileTest.php;type=text/x-', $file->getCurlValue()); + } else { + $c = $file->getCurlValue(); + $this->assertEquals(__FILE__, $c->getFilename()); + $this->assertEquals('PostFileTest.php', $c->getPostFilename()); + $this->assertContains('text/x-', $c->getMimeType()); + } + } + + public function testReturnsCurlValueStringAndPostname() + { + $file = new PostFile('foo', __FILE__, null, 'NewPostFileTest.php'); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=NewPostFileTest.php;type=text/x-', $file->getCurlValue()); + } else { + $c = $file->getCurlValue(); + $this->assertEquals(__FILE__, $c->getFilename()); + $this->assertEquals('NewPostFileTest.php', $c->getPostFilename()); + $this->assertContains('text/x-', $c->getMimeType()); + } + } + + public function testContentDispositionFilePathIsStripped() + { + $this->getServer()->flush(); + $client = new Client($this->getServer()->getUrl()); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = $client->post()->addPostFile('file', __FILE__); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('POST / HTTP/1.1', $requests[0]); + $this->assertContains('Content-Disposition: form-data; name="file"; filename="PostFileTest.php"', $requests[0]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php new file mode 100755 index 000000000..80b8d5415 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php @@ -0,0 +1,616 @@ +assertSame($factory, RequestFactory::getInstance()); + } + + public function testCreatesNewGetRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://www.google.com/'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\MessageInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/', $request->getPath()); + $this->assertEquals('/', $request->getResource()); + + // Create a GET request with a custom receiving body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $b = EntityBody::factory(); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl(), null, $b); + $request->setClient(new Client()); + $response = $request->send(); + $this->assertSame($b, $response->getBody()); + } + + public function testCreatesPutRequests() + { + // Test using a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + unset($request); + + // Test using an EntityBody + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, EntityBody::factory('Data')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using a resource + $resource = fopen('php://temp', 'w+'); + fwrite($resource, 'Data'); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, $resource); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using an object that can be cast as a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, Url::factory('http://www.example.com/')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('http://www.example.com/', (string) $request->getBody()); + } + + public function testCreatesHeadAndDeleteRequests() + { + $request = RequestFactory::getInstance()->create('DELETE', 'http://www.test.com/'); + $this->assertEquals('DELETE', $request->getMethod()); + $request = RequestFactory::getInstance()->create('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + } + + public function testCreatesOptionsRequests() + { + $request = RequestFactory::getInstance()->create('OPTIONS', 'http://www.example.com/'); + $this->assertEquals('OPTIONS', $request->getMethod()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + } + + public function testCreatesNewPutRequestWithBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertEquals('Data', (string) $request->getBody()); + } + + public function testCreatesNewPostRequestWithFields() + { + // Use an array + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, array( + 'a' => 'b' + )); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + unset($request); + + // Use a collection + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new Collection(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + // Use a QueryString + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new QueryString(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/', null, array( + 'a' => 'b', + 'file' => '@' . __FILE__ + )); + + $this->assertEquals(array( + 'a' => 'b' + ), $request->getPostFields()->getAll()); + + $files = $request->getPostFiles(); + $this->assertInstanceOf('Guzzle\Http\Message\PostFile', $files['file'][0]); + } + + public function testCreatesFromParts() + { + $parts = parse_url('http://michael:123@www.google.com:8080/path?q=1&v=2'); + + $request = RequestFactory::getInstance()->fromParts('PUT', $parts, null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('123', $request->getPassword()); + $this->assertEquals('8080', $request->getPort()); + $this->assertEquals(array( + 'scheme' => 'http', + 'host' => 'www.google.com', + 'port' => 8080, + 'path' => '/path', + 'query' => 'q=1&v=2', + ), parse_url($request->getUrl())); + } + + public function testCreatesFromMessage() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com:8080\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization')); + $this->assertEquals('8080', $request->getPort()); + + // Test passing a blank message returns false + $this->assertFalse($request = RequestFactory::getInstance()->fromMessage('')); + + // Test passing a url with no port + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization')); + $this->assertEquals(80, $request->getPort()); + } + + public function testCreatesNewTraceRequest() + { + $request = RequestFactory::getInstance()->create('TRACE', 'http://www.google.com/'); + $this->assertFalse($request instanceof \Guzzle\Http\Message\EntityEnclosingRequest); + $this->assertEquals('TRACE', $request->getMethod()); + } + + public function testCreatesProperTransferEncodingRequests() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'hello'); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + public function testProperlyDealsWithDuplicateHeaders() + { + $parser = new MessageParser(); + + $message = "POST / http/1.1\r\n" + . "DATE:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "host:host.foo.com\r\n" + . "ZOO:abc\r\n" + . "ZOO:123\r\n" + . "ZOO:HI\r\n" + . "zoo:456\r\n\r\n"; + + $parts = $parser->parseRequest($message); + $this->assertEquals(array ( + 'DATE' => 'Mon, 09 Sep 2011 23:36:00 GMT', + 'host' => 'host.foo.com', + 'ZOO' => array('abc', '123', 'HI'), + 'zoo' => '456', + ), $parts['headers']); + + $request = RequestFactory::getInstance()->fromMessage($message); + + $this->assertEquals(array( + 'abc', '123', 'HI', '456' + ), $request->getHeader('zoo')->toArray()); + } + + public function testCreatesHttpMessagesWithBodiesAndNormalizesLineEndings() + { + $message = "POST / http/1.1\r\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\r\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "Host:host.foo.com\r\n\r\n" + . "foo=bar"; + + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf8', (string) $request->getHeader('Content-Type')); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "POST / http/1.1\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\n" + . "Host:host.foo.com\n\n" + . "foo=bar"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "PUT / HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertTrue($request->hasHeader('Content-Length')); + $this->assertEquals(0, (string) $request->getHeader('Content-Length')); + } + + public function testBugPathIncorrectlyHandled() + { + $message = "POST /foo\r\n\r\nBODY"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertSame('POST', $request->getMethod()); + $this->assertSame('/foo', $request->getPath()); + $this->assertSame('BODY', (string) $request->getBody()); + } + + public function testHandlesChunkedTransferEncoding() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.foo.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'Test'); + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.foo.com/', array( + 'transfer-encoding' => 'chunked' + ), array( + 'foo' => 'bar' + )); + + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + } + + public function testClonesRequestsWithMethodWithoutClient() + { + $f = RequestFactory::getInstance(); + $request = $f->create('GET', 'http://www.test.com', array('X-Foo' => 'Bar')); + $request->getParams()->replace(array('test' => '123')); + $request->getCurlOptions()->set('foo', 'bar'); + $cloned = $f->cloneRequestWithMethod($request, 'PUT'); + $this->assertEquals('PUT', $cloned->getMethod()); + $this->assertEquals('Bar', (string) $cloned->getHeader('X-Foo')); + $this->assertEquals('http://www.test.com', $cloned->getUrl()); + // Ensure params are cloned and cleaned up + $this->assertEquals(1, count($cloned->getParams()->getAll())); + $this->assertEquals('123', $cloned->getParams()->get('test')); + // Ensure curl options are cloned + $this->assertEquals('bar', $cloned->getCurlOptions()->get('foo')); + // Ensure event dispatcher is cloned + $this->assertNotSame($request->getEventDispatcher(), $cloned->getEventDispatcher()); + } + + public function testClonesRequestsWithMethodWithClient() + { + $f = RequestFactory::getInstance(); + $client = new Client(); + $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); + $cloned = $f->cloneRequestWithMethod($request, 'GET'); + $this->assertEquals('GET', $cloned->getMethod()); + $this->assertNull($cloned->getHeader('Content-Length')); + $this->assertEquals('http://www.test.com', $cloned->getUrl()); + $this->assertSame($request->getClient(), $cloned->getClient()); + } + + public function testClonesRequestsWithMethodWithClientWithEntityEnclosingChange() + { + $f = RequestFactory::getInstance(); + $client = new Client(); + $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); + $cloned = $f->cloneRequestWithMethod($request, 'POST'); + $this->assertEquals('POST', $cloned->getMethod()); + $this->assertEquals('test', (string) $cloned->getBody()); + } + + public function testCanDisableRedirects() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 307\r\nLocation: " . $this->getServer()->getUrl() . "\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $response = $client->get('/', array(), array('allow_redirects' => false))->send(); + $this->assertEquals(307, $response->getStatusCode()); + } + + public function testCanAddCookies() + { + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', array(), array('cookies' => array('Foo' => 'Bar'))); + $this->assertEquals('Bar', $request->getCookie('Foo')); + } + + public function testCanAddQueryString() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'query' => array('Foo' => 'Bar') + )); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + } + + public function testCanSetDefaultQueryString() + { + $request = new Request('GET', 'http://www.foo.com?test=abc'); + RequestFactory::getInstance()->applyOptions($request, array( + 'query' => array('test' => '123', 'other' => 't123') + ), RequestFactory::OPTIONS_AS_DEFAULTS); + $this->assertEquals('abc', $request->getQuery()->get('test')); + $this->assertEquals('t123', $request->getQuery()->get('other')); + } + + public function testCanAddBasicAuth() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'auth' => array('michael', 'test') + )); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + } + + public function testCanAddDigestAuth() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'auth' => array('michael', 'test', 'digest') + )); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + } + + public function testCanAddEvents() + { + $foo = null; + $client = new Client(); + $client->addSubscriber(new MockPlugin(array(new Response(200)))); + $request = $client->get($this->getServer()->getUrl(), array(), array( + 'events' => array( + 'request.before_send' => function () use (&$foo) { $foo = true; } + ) + )); + $request->send(); + $this->assertTrue($foo); + } + + public function testCanAddEventsWithPriority() + { + $foo = null; + $client = new Client(); + $client->addSubscriber(new MockPlugin(array(new Response(200)))); + $request = $client->get($this->getServer()->getUrl(), array(), array( + 'events' => array( + 'request.before_send' => array(function () use (&$foo) { $foo = true; }, 100) + ) + )); + $request->send(); + $this->assertTrue($foo); + } + + public function testCanAddPlugins() + { + $mock = new MockPlugin(array( + new Response(200), + new Response(200) + )); + $client = new Client(); + $client->addSubscriber($mock); + $request = $client->get('/', array(), array( + 'plugins' => array($mock) + )); + $request->send(); + } + + public function testCanDisableExceptions() + { + $client = new Client(); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(500)))), + 'exceptions' => false + )); + $this->assertEquals(500, $request->send()->getStatusCode()); + } + + public function testCanDisableExceptionsWithErrorListener() + { + $client = new Client(); + $client->getEventDispatcher()->addListener('request.error', function () {}); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(500)))), + 'exceptions' => false + )); + $this->assertEquals(500, $request->send()->getStatusCode()); + } + + public function testCanChangeSaveToLocation() + { + $r = EntityBody::factory(); + $client = new Client(); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(200, array(), 'testing')))), + 'save_to' => $r + )); + $request->send(); + $this->assertEquals('testing', (string) $r); + } + + public function testCanSetProxy() + { + $client = new Client(); + $request = $client->get('/', array(), array('proxy' => '192.168.16.121')); + $this->assertEquals('192.168.16.121', $request->getCurlOptions()->get(CURLOPT_PROXY)); + } + + public function testCanSetHeadersOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('headers' => array('Foo' => 'Bar'))); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + } + + public function testCanSetDefaultHeadersOptions() + { + $request = new Request('GET', 'http://www.foo.com', array('Foo' => 'Bar')); + RequestFactory::getInstance()->applyOptions($request, array( + 'headers' => array('Foo' => 'Baz', 'Bam' => 't123') + ), RequestFactory::OPTIONS_AS_DEFAULTS); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + $this->assertEquals('t123', (string) $request->getHeader('Bam')); + } + + public function testCanSetBodyOption() + { + $client = new Client(); + $request = $client->put('/', array(), null, array('body' => 'test')); + $this->assertEquals('test', (string) $request->getBody()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesBodyOption() + { + $client = new Client(); + $client->get('/', array(), array('body' => 'test')); + } + + public function testCanSetTimeoutOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('timeout' => 1.5)); + $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_TIMEOUT_MS)); + } + + public function testCanSetConnectTimeoutOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('connect_timeout' => 1.5)); + $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_CONNECTTIMEOUT_MS)); + } + + public function testCanSetDebug() + { + $client = new Client(); + $request = $client->get('/', array(), array('debug' => true)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_VERBOSE)); + } + + public function testCanSetVerifyToOff() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => false)); + $this->assertNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(0, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertFalse($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function testCanSetVerifyToOn() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => true)); + $this->assertNotNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function testCanSetVerifyToPath() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function inputValidation() + { + return array_map(function ($option) { return array($option); }, array( + 'headers', 'query', 'cookies', 'auth', 'events', 'plugins', 'params' + )); + } + + /** + * @dataProvider inputValidation + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesInput($option) + { + $client = new Client(); + $client->get('/', array(), array($option => 'foo')); + } + + public function testCanAddRequestParams() + { + $client = new Client(); + $request = $client->put('/', array(), null, array('params' => array('foo' => 'test'))); + $this->assertEquals('test', $request->getParams()->get('foo')); + } + + public function testCanAddSslKey() + { + $client = new Client(); + $request = $client->get('/', array(), array('ssl_key' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); + } + + public function testCanAddSslKeyPassword() + { + $client = new Client(); + $request = $client->get('/', array(), array('ssl_key' => array('/foo.pem', 'bar'))); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); + $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLKEYPASSWD)); + } + + public function testCanAddSslCert() + { + $client = new Client(); + $request = $client->get('/', array(), array('cert' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); + } + + public function testCanAddSslCertPassword() + { + $client = new Client(); + $request = $client->get('/', array(), array('cert' => array('/foo.pem', 'bar'))); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); + $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLCERTPASSWD)); + } + + public function testCreatesBodyWithoutZeroString() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://test.com', array(), '0'); + $this->assertSame('0', (string) $request->getBody()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php new file mode 100755 index 000000000..5bf624829 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php @@ -0,0 +1,639 @@ +client = new Client($this->getServer()->getUrl()); + $this->request = $this->client->get(); + } + + public function tearDown() + { + unset($this->request); + unset($this->client); + } + + public function testConstructorBuildsRequestWithArrayHeaders() + { + // Test passing an array of headers + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'foo' => 'bar' + )); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http://www.guzzle-project.com/', $request->getUrl()); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', Request::getAllEvents()); + } + + public function testConstructorBuildsRequestWithCollectionHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', new Collection(array( + 'foo' => 'bar' + ))); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testConstructorBuildsRequestWithNoHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', null); + $this->assertFalse($request->hasHeader('foo')); + } + + public function testConstructorHandlesNonBasicAuth() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'Authorization' => 'Foo bar' + )); + $this->assertNull($request->getUserName()); + $this->assertNull($request->getPassword()); + $this->assertEquals('Foo bar', (string) $request->getHeader('Authorization')); + } + + public function testRequestsCanBeConvertedToRawMessageStrings() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\n" + . "Host: www.google.com\r\n" + . "Authorization: Basic {$auth}\r\n" + . "Content-Length: 4\r\n\r\nData"; + + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', array( + 'Authorization' => 'Basic ' . $auth + ), 'Data'); + + $this->assertEquals($message, $request->__toString()); + } + + /** + * Add authorization after the fact and see that it was put in the message + */ + public function testRequestStringsIncludeAuth() + { + $auth = base64_encode('michael:123'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl(), null, 'Data') + ->setClient($this->client) + ->setAuth('michael', '123', CURLAUTH_BASIC); + $request->send(); + + $this->assertContains('Authorization: Basic ' . $auth, (string) $request); + } + + public function testGetEventDispatcher() + { + $d = $this->request->getEventDispatcher(); + $this->assertInstanceOf('Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', $d); + $this->assertEquals($d, $this->request->getEventDispatcher()); + } + + public function testRequestsManageClients() + { + $request = new Request('GET', 'http://test.com'); + $this->assertNull($request->getClient()); + $request->setClient($this->client); + $this->assertSame($this->client, $request->getClient()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage A client must be set on the request + */ + public function testRequestsRequireClients() + { + $request = new Request('GET', 'http://test.com'); + $request->send(); + } + + public function testSend() + { + $response = new Response(200, array( + 'Content-Length' => 3 + ), 'abc'); + $this->request->setResponse($response, true); + $r = $this->request->send(); + + $this->assertSame($response, $r); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $this->request->getResponse()); + $this->assertSame($r, $this->request->getResponse()); + $this->assertEquals('complete', $this->request->getState()); + } + + public function testGetResponse() + { + $this->assertNull($this->request->getResponse()); + $response = new Response(200, array('Content-Length' => 3), 'abc'); + + $this->request->setResponse($response); + $this->assertEquals($response, $this->request->getResponse()); + + $client = new Client('http://www.google.com'); + $request = $client->get('http://www.google.com/'); + $request->setResponse($response, true); + $request->send(); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + + // Try again, making sure it's still the same response + $this->assertSame($requestResponse, $request->getResponse()); + + $response = new Response(204); + $request = $client->get(); + $request->setResponse($response, true); + $request->send(); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + } + + public function testRequestThrowsExceptionOnBadResponse() + { + try { + $this->request->setResponse(new Response(404, array('Content-Length' => 3), 'abc'), true); + $this->request->send(); + $this->fail('Expected exception not thrown'); + } catch (BadResponseException $e) { + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $e->getRequest()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $e->getResponse()); + $this->assertContains('Client error response', $e->getMessage()); + } + } + + public function testManagesQuery() + { + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $this->request->getQuery()); + $this->request->getQuery()->set('test', '123'); + $this->assertEquals('test=123', $this->request->getQuery(true)); + } + + public function testRequestHasMethod() + { + $this->assertEquals('GET', $this->request->getMethod()); + } + + public function testRequestHasScheme() + { + $this->assertEquals('http', $this->request->getScheme()); + $this->assertEquals($this->request, $this->request->setScheme('https')); + $this->assertEquals('https', $this->request->getScheme()); + } + + public function testRequestHasHost() + { + $this->assertEquals('127.0.0.1', $this->request->getHost()); + $this->assertEquals('127.0.0.1:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www2.google.com')); + $this->assertEquals('www2.google.com', $this->request->getHost()); + $this->assertEquals('www2.google.com:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www.test.com:8081')); + $this->assertEquals('www.test.com', $this->request->getHost()); + $this->assertEquals(8081, $this->request->getPort()); + } + + public function testRequestHasProtocol() + { + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.1')); + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.0')); + $this->assertEquals('1.0', $this->request->getProtocolVersion()); + } + + public function testRequestHasPath() + { + $this->assertEquals('/', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('/index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + } + + public function testPermitsFalsyComponents() + { + $request = new Request('GET', 'http://0/0?0'); + $this->assertSame('0', $request->getHost()); + $this->assertSame('/0', $request->getPath()); + $this->assertSame('0', $request->getQuery(true)); + + $request = new Request('GET', '0'); + $this->assertEquals('/0', $request->getPath()); + } + + public function testRequestHasPort() + { + $this->assertEquals(8124, $this->request->getPort()); + $this->assertEquals('127.0.0.1:8124', $this->request->getHeader('Host')); + + $this->assertEquals($this->request, $this->request->setPort('8080')); + $this->assertEquals('8080', $this->request->getPort()); + $this->assertEquals('127.0.0.1:8080', $this->request->getHeader('Host')); + + $this->request->setPort(80); + $this->assertEquals('127.0.0.1', $this->request->getHeader('Host')); + } + + public function testRequestHandlesAuthorization() + { + // Uninitialized auth + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Set an auth + $this->assertSame($this->request, $this->request->setAuth('michael', '123')); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('123', $this->request->getPassword()); + + // Set an auth with blank password + $this->assertSame($this->request, $this->request->setAuth('michael', '')); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('', $this->request->getPassword()); + + // Remove the auth + $this->request->setAuth(false); + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Make sure that the cURL based auth works too + $request = new Request('GET', $this->getServer()->getUrl()); + $request->setAuth('michael', 'password', CURLAUTH_DIGEST); + $this->assertEquals('michael:password', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesAuth() + { + $this->request->setAuth('foo', 'bar', 'bam'); + } + + public function testGetResourceUri() + { + $this->assertEquals('/', $this->request->getResource()); + $this->request->setPath('/index.html'); + $this->assertEquals('/index.html', $this->request->getResource()); + $this->request->getQuery()->add('v', '1'); + $this->assertEquals('/index.html?v=1', $this->request->getResource()); + } + + public function testRequestHasMutableUrl() + { + $url = 'http://www.test.com:8081/path?q=123#fragment'; + $u = Url::factory($url); + $this->assertSame($this->request, $this->request->setUrl($url)); + $this->assertEquals($url, $this->request->getUrl()); + + $this->assertSame($this->request, $this->request->setUrl($u)); + $this->assertEquals($url, $this->request->getUrl()); + } + + public function testRequestHasState() + { + $this->assertEquals(RequestInterface::STATE_NEW, $this->request->getState()); + $this->request->setState(RequestInterface::STATE_TRANSFER); + $this->assertEquals(RequestInterface::STATE_TRANSFER, $this->request->getState()); + } + + public function testSetManualResponse() + { + $response = new Response(200, array( + 'Date' => 'Sat, 16 Oct 2010 17:27:14 GMT', + 'Expires' => '-1', + 'Cache-Control' => 'private, max-age=0', + 'Content-Type' => 'text/html; charset=ISO-8859-1', + ), 'response body'); + + $this->assertSame($this->request, $this->request->setResponse($response), '-> setResponse() must use a fluent interface'); + $this->assertEquals('complete', $this->request->getState(), '-> setResponse() must change the state of the request to complete'); + $this->assertSame($response, $this->request->getResponse(), '-> setResponse() must set the exact same response that was passed in to it'); + } + + public function testRequestCanHaveManuallySetResponseBody() + { + $file = __DIR__ . '/../../TestData/temp.out'; + if (file_exists($file)) { + unlink($file); + } + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $entityBody = EntityBody::factory(fopen($file, 'w+')); + $request->setResponseBody($entityBody); + $response = $request->send(); + $this->assertSame($entityBody, $response->getBody()); + + $this->assertTrue(file_exists($file)); + $this->assertEquals('data', file_get_contents($file)); + unlink($file); + + $this->assertEquals('data', $response->getBody(true)); + } + + public function testHoldsCookies() + { + $this->assertNull($this->request->getCookie('test')); + + // Set a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + + // Multiple cookies by setting the Cookie header + $this->request->setHeader('Cookie', '__utma=1.638370270.1344367610.1374365610.1944450276.2; __utmz=1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); hl=de; PHPSESSID=ak93pqashi5uubuoq8fjv60897'); + $this->assertEquals('1.638370270.1344367610.1374365610.1944450276.2', $this->request->getCookie('__utma')); + $this->assertEquals('1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', $this->request->getCookie('__utmz')); + $this->assertEquals('de', $this->request->getCookie('hl')); + $this->assertEquals('ak93pqashi5uubuoq8fjv60897', $this->request->getCookie('PHPSESSID')); + + // Unset the cookies by setting the Cookie header to null + $this->request->setHeader('Cookie', null); + $this->assertNull($this->request->getCookie('test')); + $this->request->removeHeader('Cookie'); + + // Set and remove a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + $this->assertSame($this->request, $this->request->removeCookie('test')); + $this->assertNull($this->request->getCookie('test')); + + // Remove the cookie header + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->request->removeHeader('Cookie'); + $this->assertEquals('', (string) $this->request->getHeader('Cookie')); + + // Remove a cookie value + $this->request->addCookie('foo', 'bar')->addCookie('baz', 'boo'); + $this->request->removeCookie('foo'); + $this->assertEquals(array( + 'baz' => 'boo' + ), $this->request->getCookies()); + + $this->request->addCookie('foo', 'bar'); + $this->assertEquals('baz=boo; foo=bar', (string) $this->request->getHeader('Cookie')); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + * @expectedExceptionMessage Error completing request + */ + public function testRequestThrowsExceptionWhenSetToCompleteWithNoResponse() + { + $this->request->setState(RequestInterface::STATE_COMPLETE); + } + + public function testClonedRequestsUseNewInternalState() + { + $p = new AsyncPlugin(); + $this->request->getEventDispatcher()->addSubscriber($p); + $h = $this->request->getHeader('Host'); + + $r = clone $this->request; + $this->assertEquals(RequestInterface::STATE_NEW, $r->getState()); + $this->assertNotSame($r->getQuery(), $this->request->getQuery()); + $this->assertNotSame($r->getCurlOptions(), $this->request->getCurlOptions()); + $this->assertNotSame($r->getEventDispatcher(), $this->request->getEventDispatcher()); + $this->assertEquals($r->getHeaders(), $this->request->getHeaders()); + $this->assertNotSame($h, $r->getHeader('Host')); + $this->assertNotSame($r->getParams(), $this->request->getParams()); + $this->assertTrue($this->request->getEventDispatcher()->hasListeners('request.sent')); + } + + public function testRecognizesBasicAuthCredentialsInUrls() + { + $this->request->setUrl('http://michael:test@test.com/'); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('test', $this->request->getPassword()); + } + + public function testRequestCanBeSentUsingCurl() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 404 Not Found\r\nContent-Encoding: application/xml\r\nContent-Length: 48\r\n\r\nFile not found" + )); + + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $response = $request->send(); + + $this->assertEquals('data', $response->getBody(true)); + $this->assertEquals(200, (int) $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, $response->getContentLength()); + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $response->getExpires()); + + // Test that the same handle can be sent twice without setting state to new + $response2 = $request->send(); + $this->assertNotSame($response, $response2); + + try { + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl() . 'index.html'); + $request->setClient($this->client); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + } + + $requests = $this->getServer()->getReceivedRequests(true); + $messages = $this->getServer()->getReceivedRequests(false); + $port = $this->getServer()->getPort(); + + $userAgent = $this->client->getDefaultUserAgent(); + + $this->assertEquals('127.0.0.1:' . $port, $requests[0]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[1]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[2]->getHeader('Host')); + + $this->assertEquals('/', $requests[0]->getPath()); + $this->assertEquals('/', $requests[1]->getPath()); + $this->assertEquals('/index.html', $requests[2]->getPath()); + + $parts = explode("\r\n", $messages[0]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[1]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[2]); + $this->assertEquals('GET /index.html HTTP/1.1', $parts[0]); + } + + public function testThrowsExceptionsWhenUnsuccessfulResponseIsReceivedByDefault() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"); + + try { + $request = $this->client->get('/index.html'); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + $this->assertContains('Client error response', $e->getMessage()); + $this->assertContains('[status code] 404', $e->getMessage()); + $this->assertContains('[reason phrase] Not found', $e->getMessage()); + } + } + + public function testCanShortCircuitErrorHandling() + { + $request = $this->request; + $response = new Response(404); + $request->setResponse($response, true); + $out = ''; + $that = $this; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$out, $that) { + $out .= $event['request'] . "\n" . $event['response'] . "\n"; + $event->stopPropagation(); + }); + $request->send(); + $this->assertContains((string) $request, $out); + $this->assertContains((string) $request->getResponse(), $out); + $this->assertSame($response, $request->getResponse()); + } + + public function testCanOverrideUnsuccessfulResponses() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 404 NOT FOUND\r\n" . + "Content-Length: 0\r\n" . + "\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $newResponse = null; + + $request = $this->request; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$newResponse) { + if ($event['response']->getStatusCode() == 404) { + $newRequest = clone $event['request']; + $newResponse = $newRequest->send(); + // Override the original response and bypass additional response processing + $event['response'] = $newResponse; + // Call $event['request']->setResponse($newResponse); to re-apply events + $event->stopPropagation(); + } + }); + + $request->send(); + + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertSame($newResponse, $request->getResponse()); + $this->assertEquals(2, count($this->getServer()->getReceivedRequests())); + } + + public function testCanRetrieveUrlObject() + { + $request = new Request('GET', 'http://www.example.com/foo?abc=d'); + $this->assertInstanceOf('Guzzle\Http\Url', $request->getUrl(true)); + $this->assertEquals('http://www.example.com/foo?abc=d', $request->getUrl()); + $this->assertEquals('http://www.example.com/foo?abc=d', (string) $request->getUrl(true)); + } + + public function testUnresolvedRedirectsReturnResponse() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 SEE OTHER\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n" + )); + $request = $this->request; + $this->assertEquals(303, $request->send()->getStatusCode()); + $request->getParams()->set(RedirectPlugin::DISABLE, true); + $this->assertEquals(301, $request->send()->getStatusCode()); + } + + public function testCanSendCustomRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = $this->client->createRequest('PROPFIND', $this->getServer()->getUrl(), array( + 'Content-Type' => 'text/plain' + ), 'foo'); + $response = $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PROPFIND', $requests[0]->getMethod()); + $this->assertEquals(3, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals('foo', (string) $requests[0]->getBody()); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Warning + */ + public function testEnsuresFileCanBeCreated() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->client->get('/')->setResponseBody('/wefwefefefefwewefwe/wefwefwefefwe/wefwefewfw.txt')->send(); + } + + public function testAllowsFilenameForDownloadingContent() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $name = sys_get_temp_dir() . '/foo.txt'; + $this->client->get('/')->setResponseBody($name)->send(); + $this->assertEquals('test', file_get_contents($name)); + unlink($name); + } + + public function testUsesCustomResponseBodyWhenItIsCustom() + { + $en = EntityBody::factory(); + $request = $this->client->get(); + $request->setResponseBody($en); + $request->setResponse(new Response(200, array(), 'foo')); + $this->assertEquals('foo', (string) $en); + } + + public function testCanChangePortThroughScheme() + { + $request = new Request('GET', 'http://foo.com'); + $request->setScheme('https'); + $this->assertEquals('https://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + $request->setScheme('http'); + $this->assertEquals('http://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + $request->setPort(null); + $this->assertEquals('http://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php new file mode 100755 index 000000000..08b4df878 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php @@ -0,0 +1,677 @@ +response = new Response(200, new Collection(array( + 'Accept-Ranges' => 'bytes', + 'Age' => '12', + 'Allow' => 'GET, HEAD', + 'Cache-Control' => 'no-cache', + 'Content-Encoding' => 'gzip', + 'Content-Language' => 'da', + 'Content-Length' => '348', + 'Content-Location' => '/index.htm', + 'Content-Disposition' => 'attachment; filename=fname.ext', + 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==', + 'Content-Range' => 'bytes 21010-47021/47022', + 'Content-Type' => 'text/html; charset=utf-8', + 'Date' => 'Tue, 15 Nov 1994 08:12:31 GMT', + 'ETag' => '737060cd8c284d8af7ad3082f209582d', + 'Expires' => 'Thu, 01 Dec 1994 16:00:00 GMT', + 'Last-Modified' => 'Tue, 15 Nov 1994 12:45:26 GMT', + 'Location' => 'http://www.w3.org/pub/WWW/People.html', + 'Pragma' => 'no-cache', + 'Proxy-Authenticate' => 'Basic', + 'Retry-After' => '120', + 'Server' => 'Apache/1.3.27 (Unix) (Red-Hat/Linux)', + 'Set-Cookie' => 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'Trailer' => 'Max-Forwards', + 'Transfer-Encoding' => 'chunked', + 'Vary' => '*', + 'Via' => '1.0 fred, 1.1 nowhere.com (Apache/1.1)', + 'Warning' => '199 Miscellaneous warning', + 'WWW-Authenticate' => 'Basic' + )), 'body'); + } + + public function tearDown() + { + unset($this->response); + } + + public function testConstructor() + { + $params = new Collection(); + $body = EntityBody::factory(''); + $response = new Response(200, $params, $body); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals($body, $response->getBody()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Make sure Content-Length is set automatically + $response = new Response(200, $params); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Pass bodies to the response + $response = new Response(200, null, 'data'); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $response = new Response(200, null, EntityBody::factory('data')); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $this->assertEquals('data', $response->getBody(true)); + $response = new Response(200, null, '0'); + $this->assertSame('0', $response->getBody(true), 'getBody(true) should return "0" if response body is "0".'); + + // Make sure the proper exception is thrown + try { + //$response = new Response(200, null, array('foo' => 'bar')); + //$this->fail('Response did not throw exception when passing invalid body'); + } catch (HttpException $e) { + } + + // Ensure custom codes can be set + $response = new Response(2); + $this->assertEquals(2, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + // Make sure the proper exception is thrown when sending invalid headers + try { + $response = new Response(200, 'adidas'); + $this->fail('Response did not throw exception when passing invalid $headers'); + } catch (BadResponseException $e) { + } + } + + public function test__toString() + { + $response = new Response(200); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); + + // Add another header + $response = new Response(200, array( + 'X-Test' => 'Guzzle' + )); + $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); + + $response = new Response(200, array( + 'Content-Length' => 4 + ), 'test'); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); + } + + public function testFactory() + { + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + + // Make sure that automatic Content-Length works + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + } + + public function testFactoryCanCreateHeadResponses() + { + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('', $response->getBody(true)); + } + + public function testFactoryRequiresMessage() + { + $this->assertFalse(Response::fromMessage('')); + } + + public function testGetBody() + { + $body = EntityBody::factory(''); + $response = new Response(403, new Collection(), $body); + $this->assertEquals($body, $response->getBody()); + $response->setBody('foo'); + $this->assertEquals('foo', $response->getBody(true)); + } + + public function testManagesStatusCode() + { + $response = new Response(403); + $this->assertEquals(403, $response->getStatusCode()); + } + + public function testGetMessage() + { + $response = new Response(200, new Collection(array( + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nbody", $response->getMessage()); + } + + public function testGetRawHeaders() + { + $response = new Response(200, new Collection(array( + 'Keep-Alive' => 155, + 'User-Agent' => 'Guzzle', + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nKeep-Alive: 155\r\nUser-Agent: Guzzle\r\nContent-Length: 4\r\n\r\n", $response->getRawHeaders()); + } + + public function testHandlesStatusAndStatusCodes() + { + $response = new Response(200, new Collection(), 'body'); + $this->assertEquals('OK', $response->getReasonPhrase()); + + $this->assertSame($response, $response->setStatus(204)); + $this->assertEquals('No Content', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $this->assertSame($response, $response->setStatus(204, 'Testing!')); + $this->assertEquals('Testing!', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $response->setStatus(2000); + $this->assertEquals(2000, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + $response->setStatus(200, 'Foo'); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('Foo', $response->getReasonPhrase()); + } + + public function testIsClientError() + { + $response = new Response(403); + $this->assertTrue($response->isClientError()); + $response = new Response(200); + $this->assertFalse($response->isClientError()); + } + + public function testIsError() + { + $response = new Response(403); + $this->assertTrue($response->isError()); + $response = new Response(200); + $this->assertFalse($response->isError()); + $response = new Response(500); + $this->assertTrue($response->isError()); + } + + public function testIsInformational() + { + $response = new Response(100); + $this->assertTrue($response->isInformational()); + $response = new Response(200); + $this->assertFalse($response->isInformational()); + } + + public function testIsRedirect() + { + $response = new Response(301); + $this->assertTrue($response->isRedirect()); + $response = new Response(200); + $this->assertFalse($response->isRedirect()); + } + + public function testIsServerError() + { + $response = new Response(500); + $this->assertTrue($response->isServerError()); + $response = new Response(400); + $this->assertFalse($response->isServerError()); + } + + public function testIsSuccessful() + { + $response = new Response(200); + $this->assertTrue($response->isSuccessful()); + $response = new Response(403); + $this->assertFalse($response->isSuccessful()); + } + + public function testGetAcceptRanges() + { + $this->assertEquals('bytes', $this->response->getAcceptRanges()); + } + + public function testCalculatesAge() + { + $this->assertEquals(12, $this->response->calculateAge()); + + $this->response->removeHeader('Age'); + $this->response->removeHeader('Date'); + $this->assertNull($this->response->calculateAge()); + + $this->response->setHeader('Date', gmdate(ClientInterface::HTTP_DATE, strtotime('-1 minute'))); + // If the test runs slowly, still pass with a +5 second allowance + $this->assertTrue($this->response->getAge() - 60 <= 5); + } + + public function testGetAllow() + { + $this->assertEquals('GET, HEAD', $this->response->getAllow()); + } + + public function testGetCacheControl() + { + $this->assertEquals('no-cache', $this->response->getCacheControl()); + } + + public function testGetContentEncoding() + { + $this->assertEquals('gzip', $this->response->getContentEncoding()); + } + + public function testGetContentLanguage() + { + $this->assertEquals('da', $this->response->getContentLanguage()); + } + + public function testGetContentLength() + { + $this->assertEquals('348', $this->response->getContentLength()); + } + + public function testGetContentLocation() + { + $this->assertEquals('/index.htm', $this->response->getContentLocation()); + } + + public function testGetContentDisposition() + { + $this->assertEquals('attachment; filename=fname.ext', $this->response->getContentDisposition()); + } + + public function testGetContentMd5() + { + $this->assertEquals('Q2hlY2sgSW50ZWdyaXR5IQ==', $this->response->getContentMd5()); + } + + public function testGetContentRange() + { + $this->assertEquals('bytes 21010-47021/47022', $this->response->getContentRange()); + } + + public function testGetContentType() + { + $this->assertEquals('text/html; charset=utf-8', $this->response->getContentType()); + } + + public function testGetDate() + { + $this->assertEquals('Tue, 15 Nov 1994 08:12:31 GMT', $this->response->getDate()); + } + + public function testGetEtag() + { + $this->assertEquals('737060cd8c284d8af7ad3082f209582d', $this->response->getEtag()); + } + + public function testGetExpires() + { + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $this->response->getExpires()); + } + + public function testGetLastModified() + { + $this->assertEquals('Tue, 15 Nov 1994 12:45:26 GMT', $this->response->getLastModified()); + } + + public function testGetLocation() + { + $this->assertEquals('http://www.w3.org/pub/WWW/People.html', $this->response->getLocation()); + } + + public function testGetPragma() + { + $this->assertEquals('no-cache', $this->response->getPragma()); + } + + public function testGetProxyAuthenticate() + { + $this->assertEquals('Basic', $this->response->getProxyAuthenticate()); + } + + public function testGetServer() + { + $this->assertEquals('Apache/1.3.27 (Unix) (Red-Hat/Linux)', $this->response->getServer()); + } + + public function testGetSetCookie() + { + $this->assertEquals('UserID=JohnDoe; Max-Age=3600; Version=1', $this->response->getSetCookie()); + } + + public function testGetMultipleSetCookie() + { + $this->response->addHeader('Set-Cookie', 'UserID=Mike; Max-Age=200'); + $this->assertEquals(array( + 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'UserID=Mike; Max-Age=200', + ), $this->response->getHeader('Set-Cookie')->toArray()); + } + + public function testGetSetCookieNormalizesHeaders() + { + $this->response->addHeaders(array( + 'Set-Cooke' => 'boo', + 'set-cookie' => 'foo' + )); + + $this->assertEquals(array( + 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'foo' + ), $this->response->getHeader('Set-Cookie')->toArray()); + + $this->response->addHeaders(array( + 'set-cookie' => 'fubu' + )); + $this->assertEquals( + array('UserID=JohnDoe; Max-Age=3600; Version=1', 'foo', 'fubu'), + $this->response->getHeader('Set-Cookie')->toArray() + ); + } + + public function testGetTrailer() + { + $this->assertEquals('Max-Forwards', $this->response->getTrailer()); + } + + public function testGetTransferEncoding() + { + $this->assertEquals('chunked', $this->response->getTransferEncoding()); + } + + public function testGetVary() + { + $this->assertEquals('*', $this->response->getVary()); + } + + public function testReturnsViaHeader() + { + $this->assertEquals('1.0 fred, 1.1 nowhere.com (Apache/1.1)', $this->response->getVia()); + } + public function testGetWarning() + { + $this->assertEquals('199 Miscellaneous warning', $this->response->getWarning()); + } + + public function testReturnsWwwAuthenticateHeader() + { + $this->assertEquals('Basic', $this->response->getWwwAuthenticate()); + } + + public function testReturnsConnectionHeader() + { + $this->assertEquals(null, $this->response->getConnection()); + $this->response->setHeader('Connection', 'close'); + $this->assertEquals('close', $this->response->getConnection()); + } + + public function testReturnsHeaders() + { + $this->assertEquals('Basic', $this->response->getHeader('WWW-Authenticate', null, true)); + $this->assertEquals('chunked', $this->response->getHeader('Transfer-Encoding', null, false)); + } + + public function testHasTransferInfo() + { + $stats = array ( + 'url' => 'http://www.google.com/', + 'content_type' => 'text/html; charset=ISO-8859-1', + 'http_code' => 200, + 'header_size' => 606, + 'request_size' => 53, + 'filetime' => -1, + 'ssl_verify_result' => 0, + 'redirect_count' => 0, + 'total_time' => 0.093284, + 'namelookup_time' => 0.001349, + 'connect_time' => 0.01635, + 'pretransfer_time' => 0.016358, + 'size_upload' => 0, + 'size_download' => 10330, + 'speed_download' => 110737, + 'speed_upload' => 0, + 'download_content_length' => -1, + 'upload_content_length' => 0, + 'starttransfer_time' => 0.07066, + 'redirect_time' => 0, + ); + + // Uninitialized state + $this->assertNull($this->response->getInfo('url')); + $this->assertEquals(array(), $this->response->getInfo()); + + // Set the stats + $this->response->setInfo($stats); + $this->assertEquals($stats, $this->response->getInfo()); + $this->assertEquals(606, $this->response->getInfo('header_size')); + $this->assertNull($this->response->getInfo('does_not_exist')); + } + + /** + * @return Response + */ + private function getResponse($code, array $headers = null, EntityBody $body = null) + { + return new Response($code, $headers, $body); + } + + public function testDeterminesIfItCanBeCached() + { + $this->assertTrue($this->getResponse(200)->canCache()); + $this->assertTrue($this->getResponse(410)->canCache()); + $this->assertFalse($this->getResponse(404)->canCache()); + $this->assertTrue($this->getResponse(200, array( + 'Cache-Control' => 'public' + ))->canCache()); + + // This has the no-store directive + $this->assertFalse($this->getResponse(200, array( + 'Cache-Control' => 'private, no-store' + ))->canCache()); + + // The body cannot be read, so it cannot be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertFalse($this->getResponse(200, array( + 'Transfer-Encoding' => 'chunked' + ), EntityBody::factory($resource, 10))->canCache()); + unlink($tmp); + + // The body is 0 length, cannot be read, so it can be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertTrue($this->getResponse(200, array(array( + 'Content-Length' => 0 + )), EntityBody::factory($resource, 0))->canCache()); + unlink($tmp); + } + + public function testDeterminesResponseMaxAge() + { + $this->assertEquals(null, $this->getResponse(200)->getMaxAge()); + + // Uses the response's s-maxage + $this->assertEquals(140, $this->getResponse(200, array( + 'Cache-Control' => 's-maxage=140' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120', + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + } + + public function testDeterminesIfItCanValidate() + { + $response = new Response(200); + $this->assertFalse($response->canValidate()); + $response->setHeader('ETag', '123'); + $this->assertTrue($response->canValidate()); + $response->removeHeader('ETag'); + $this->assertFalse($response->canValidate()); + $response->setHeader('Last-Modified', '123'); + $this->assertTrue($response->canValidate()); + } + + public function testCalculatesFreshness() + { + $response = new Response(200); + $this->assertNull($response->isFresh()); + $this->assertNull($response->getFreshness()); + + $response->setHeader('Cache-Control', 'max-age=120'); + $response->setHeader('Age', 100); + $this->assertEquals(20, $response->getFreshness()); + $this->assertTrue($response->isFresh()); + + $response->setHeader('Age', 120); + $this->assertEquals(0, $response->getFreshness()); + $this->assertTrue($response->isFresh()); + + $response->setHeader('Age', 150); + $this->assertEquals(-30, $response->getFreshness()); + $this->assertFalse($response->isFresh()); + } + + public function testHandlesProtocols() + { + $this->assertSame($this->response, $this->response->setProtocol('HTTP', '1.0')); + $this->assertEquals('HTTP', $this->response->getProtocol()); + $this->assertEquals('1.0', $this->response->getProtocolVersion()); + } + + public function testComparesContentType() + { + $response = new Response(200, array( + 'Content-Type' => 'text/html; charset=ISO-8859-4' + )); + + $this->assertTrue($response->isContentType('text/html')); + $this->assertTrue($response->isContentType('TExT/html')); + $this->assertTrue($response->isContentType('charset=ISO-8859-4')); + $this->assertFalse($response->isContentType('application/xml')); + } + + public function testResponseDeterminesIfMethodIsAllowedBaseOnAllowHeader() + { + $response = new Response(200, array( + 'Allow' => 'OPTIONS, POST, deletE,GET' + )); + + $this->assertTrue($response->isMethodAllowed('get')); + $this->assertTrue($response->isMethodAllowed('GET')); + $this->assertTrue($response->isMethodAllowed('options')); + $this->assertTrue($response->isMethodAllowed('post')); + $this->assertTrue($response->isMethodAllowed('Delete')); + $this->assertFalse($response->isMethodAllowed('put')); + $this->assertFalse($response->isMethodAllowed('PUT')); + + $response = new Response(200); + $this->assertFalse($response->isMethodAllowed('get')); + } + + public function testParsesJsonResponses() + { + $response = new Response(200, array(), '{"foo": "bar"}'); + $this->assertEquals(array('foo' => 'bar'), $response->json()); + // Return array when null is a service response + $response = new Response(200); + $this->assertEquals(array(), $response->json()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Unable to parse response body into JSON: 4 + */ + public function testThrowsExceptionWhenFailsToParseJsonResponse() + { + $response = new Response(200, array(), '{"foo": "'); + $response->json(); + } + + public function testParsesXmlResponses() + { + $response = new Response(200, array(), 'bar'); + $this->assertEquals('bar', (string) $response->xml()->foo); + // Always return a SimpleXMLElement from the xml method + $response = new Response(200); + $this->assertEmpty((string) $response->xml()->foo); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML + */ + public function testThrowsExceptionWhenFailsToParseXmlResponse() + { + $response = new Response(200, array(), 'xml(); + } + + public function testResponseIsSerializable() + { + $response = new Response(200, array('Foo' => 'bar'), 'test'); + $r = unserialize(serialize($response)); + $this->assertEquals(200, $r->getStatusCode()); + $this->assertEquals('bar', (string) $r->getHeader('Foo')); + $this->assertEquals('test', (string) $r->getBody()); + } + + public function testPreventsComplexExternalEntities() + { + $xml = ']>&test;'; + $response = new Response(200, array(), $xml); + + $oldCwd = getcwd(); + chdir(__DIR__); + try { + $xml = $response->xml(); + chdir($oldCwd); + $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); + } catch (\Exception $e) { + chdir($oldCwd); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php new file mode 100755 index 000000000..722845340 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php @@ -0,0 +1,31 @@ +assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); + } + + public function testGetsFromFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); + } + + public function testGetsFromCaseInsensitiveFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); + } + + public function testReturnsNullWhenNoMatchFound() + { + $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php new file mode 100755 index 000000000..549d3edd9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php @@ -0,0 +1,30 @@ +aggregate($key, $value, $query); + $this->assertEquals(array('test%20123' => 'foo%20123,baz,bar'), $result); + } + + public function testEncodes() + { + $query = new QueryString(); + $query->useUrlEncoding(false); + $a = new Ag(); + $key = 'test 123'; + $value = array('foo 123', 'baz', 'bar'); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array('test 123' => 'foo 123,baz,bar'), $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php new file mode 100755 index 000000000..6a4d9d95b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php @@ -0,0 +1,30 @@ +aggregate($key, $value, $query); + $this->assertEquals(array('facet%201' => array('size%20a', 'width%20b')), $result); + } + + public function testEncodes() + { + $query = new QueryString(); + $query->useUrlEncoding(false); + $a = new Ag(); + $key = 'facet 1'; + $value = array('size a', 'width b'); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array('facet 1' => array('size a', 'width b')), $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php new file mode 100755 index 000000000..1e7f0c27a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php @@ -0,0 +1,32 @@ +useUrlEncoding(false); + $a = new Ag(); + $key = 't'; + $value = array( + 'v1' => 'a', + 'v2' => 'b', + 'v3' => array( + 'v4' => 'c', + 'v5' => 'd', + ) + ); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array( + 't[v1]' => 'a', + 't[v2]' => 'b', + 't[v3][v4]' => 'c', + 't[v3][v5]' => 'd', + ), $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php new file mode 100755 index 000000000..948db442d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php @@ -0,0 +1,233 @@ +q = new QueryString(); + } + + public function testGetFieldSeparator() + { + $this->assertEquals('&', $this->q->getFieldSeparator()); + } + + public function testGetValueSeparator() + { + $this->assertEquals('=', $this->q->getValueSeparator()); + } + + public function testIsUrlEncoding() + { + $this->assertEquals('RFC 3986', $this->q->getUrlEncoding()); + $this->assertTrue($this->q->isUrlEncoding()); + $this->assertEquals('foo%20bar', $this->q->encodeValue('foo bar')); + + $this->q->useUrlEncoding(QueryString::FORM_URLENCODED); + $this->assertTrue($this->q->isUrlEncoding()); + $this->assertEquals(QueryString::FORM_URLENCODED, $this->q->getUrlEncoding()); + $this->assertEquals('foo+bar', $this->q->encodeValue('foo bar')); + + $this->assertSame($this->q, $this->q->useUrlEncoding(false)); + $this->assertFalse($this->q->isUrlEncoding()); + $this->assertFalse($this->q->isUrlEncoding()); + } + + public function testSetFieldSeparator() + { + $this->assertEquals($this->q, $this->q->setFieldSeparator('/')); + $this->assertEquals('/', $this->q->getFieldSeparator()); + } + + public function testSetValueSeparator() + { + $this->assertEquals($this->q, $this->q->setValueSeparator('/')); + $this->assertEquals('/', $this->q->getValueSeparator()); + } + + public function testUrlEncode() + { + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array('v1', 'v2', 'v3'), + 'ሴ' => 'bar' + ); + $encoded = array( + 'test' => 'value', + 'test%202' => rawurlencode('this is a test?'), + 'test3%5B0%5D' => 'v1', + 'test3%5B1%5D' => 'v2', + 'test3%5B2%5D' => 'v3', + '%E1%88%B4' => 'bar' + ); + $this->q->replace($params); + $this->assertEquals($encoded, $this->q->urlEncode()); + + // Disable encoding + $testData = array('test 2' => 'this is a test'); + $this->q->replace($testData); + $this->q->useUrlEncoding(false); + $this->assertEquals($testData, $this->q->urlEncode()); + } + + public function testToString() + { + // Check with no parameters + $this->assertEquals('', $this->q->__toString()); + + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array('v1', 'v2', 'v3'), + 'test4' => null, + ); + $this->q->replace($params); + $this->assertEquals('test=value&test%202=this%20is%20a%20test%3F&test3%5B0%5D=v1&test3%5B1%5D=v2&test3%5B2%5D=v3&test4', $this->q->__toString()); + $this->q->useUrlEncoding(false); + $this->assertEquals('test=value&test 2=this is a test?&test3[0]=v1&test3[1]=v2&test3[2]=v3&test4', $this->q->__toString()); + + // Use an alternative aggregator + $this->q->setAggregator(new CommaAggregator()); + $this->assertEquals('test=value&test 2=this is a test?&test3=v1,v2,v3&test4', $this->q->__toString()); + } + + public function testAllowsMultipleValuesPerKey() + { + $q = new QueryString(); + $q->add('facet', 'size'); + $q->add('facet', 'width'); + $q->add('facet.field', 'foo'); + // Use the duplicate aggregator + $q->setAggregator(new DuplicateAggregator()); + $this->assertEquals('facet=size&facet=width&facet.field=foo', $q->__toString()); + } + + public function testAllowsNestedQueryData() + { + $this->q->replace(array( + 'test' => 'value', + 't' => array( + 'v1' => 'a', + 'v2' => 'b', + 'v3' => array( + 'v4' => 'c', + 'v5' => 'd', + ) + ) + )); + + $this->q->useUrlEncoding(false); + $this->assertEquals('test=value&t[v1]=a&t[v2]=b&t[v3][v4]=c&t[v3][v5]=d', $this->q->__toString()); + } + + public function parseQueryProvider() + { + return array( + // Ensure that multiple query string values are allowed per value + array('q=a&q=b', array('q' => array('a', 'b'))), + // Ensure that PHP array style query string values are parsed + array('q[]=a&q[]=b', array('q' => array('a', 'b'))), + // Ensure that a single PHP array style query string value is parsed into an array + array('q[]=a', array('q' => array('a'))), + // Ensure that decimals are allowed in query strings + array('q.a=a&q.b=b', array( + 'q.a' => 'a', + 'q.b' => 'b' + )), + // Ensure that query string values are percent decoded + array('q%20a=a%20b', array('q a' => 'a b')), + // Ensure null values can be added + array('q&a', array('q' => false, 'a' => false)), + ); + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesQueryStrings($query, $data) + { + $query = QueryString::fromString($query); + $this->assertEquals($data, $query->getAll()); + } + + public function testProperlyDealsWithDuplicateQueryStringValues() + { + $query = QueryString::fromString('foo=a&foo=b&?µ=c'); + $this->assertEquals(array('a', 'b'), $query->get('foo')); + $this->assertEquals('c', $query->get('?µ')); + } + + public function testAllowsBlankQueryStringValues() + { + $query = QueryString::fromString('foo'); + $this->assertEquals('foo', (string) $query); + $query->set('foo', QueryString::BLANK); + $this->assertEquals('foo', (string) $query); + } + + public function testAllowsFalsyQueryStringValues() + { + $query = QueryString::fromString('0'); + $this->assertEquals('0', (string) $query); + $query->set('0', QueryString::BLANK); + $this->assertSame('0', (string) $query); + } + + public function testFromStringIgnoresQuestionMark() + { + $query = QueryString::fromString('foo=baz&bar=boo'); + $this->assertEquals('foo=baz&bar=boo', (string) $query); + } + + public function testConvertsPlusSymbolsToSpaces() + { + $query = QueryString::fromString('var=foo+bar'); + $this->assertEquals('foo bar', $query->get('var')); + } + + public function testFromStringDoesntMangleZeroes() + { + $query = QueryString::fromString('var=0'); + $this->assertSame('0', $query->get('var')); + } + + public function testAllowsZeroValues() + { + $query = new QueryString(array( + 'foo' => 0, + 'baz' => '0', + 'bar' => null, + 'boo' => false, + 'bam' => '' + )); + $this->assertEquals('foo=0&baz=0&bar&boo&bam=', (string) $query); + } + + public function testFromStringDoesntStripTrailingEquals() + { + $query = QueryString::fromString('data=mF0b3IiLCJUZWFtIERldiJdfX0='); + $this->assertEquals('mF0b3IiLCJUZWFtIERldiJdfX0=', $query->get('data')); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsed() + { + $query = QueryString::fromString('test=a&test=b'); + $this->assertEquals('test=a&test=b', (string) $query); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsedAndChecksForPhpStyle() + { + $query = QueryString::fromString('test[]=a&test[]=b'); + $this->assertEquals('test%5B0%5D=a&test%5B1%5D=b', (string) $query); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php new file mode 100755 index 000000000..6bb3fed18 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php @@ -0,0 +1,81 @@ +decorated = EntityBody::factory(fopen(__FILE__, 'r')); + $this->body = new ReadLimitEntityBody($this->decorated, 10, 3); + } + + public function testReturnsSubsetWhenCastToString() + { + $body = EntityBody::factory('foo_baz_bar'); + $limited = new ReadLimitEntityBody($body, 3, 4); + $this->assertEquals('baz', (string) $limited); + } + + public function testReturnsSubsetOfEmptyBodyWhenCastToString() + { + $body = EntityBody::factory(''); + $limited = new ReadLimitEntityBody($body, 0, 10); + $this->assertEquals('', (string) $limited); + } + + public function testSeeksWhenConstructed() + { + $this->assertEquals(3, $this->body->ftell()); + } + + public function testAllowsBoundedSeek() + { + $this->body->seek(100); + $this->assertEquals(13, $this->body->ftell()); + $this->body->seek(0); + $this->assertEquals(3, $this->body->ftell()); + $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); + } + + public function testReadsOnlySubsetOfData() + { + $data = $this->body->read(100); + $this->assertEquals(10, strlen($data)); + $this->assertFalse($this->body->read(1000)); + + $this->body->setOffset(10); + $newData = $this->body->read(100); + $this->assertEquals(10, strlen($newData)); + $this->assertNotSame($data, $newData); + } + + public function testClaimsConsumedWhenReadLimitIsReached() + { + $this->assertFalse($this->body->isConsumed()); + $this->body->read(1000); + $this->assertTrue($this->body->isConsumed()); + } + + public function testContentLengthIsBounded() + { + $this->assertEquals(10, $this->body->getContentLength()); + } + + public function testContentMd5IsBasedOnSubsection() + { + $this->assertNotSame($this->body->getContentMd5(), $this->decorated->getContentMd5()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php new file mode 100755 index 000000000..886236ddd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php @@ -0,0 +1,277 @@ +getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + // Create a client that uses the default redirect behavior + $client = new Client($this->getServer()->getUrl()); + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + $request = $client->get('/foo'); + $response = $request->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + // Ensure that two requests were sent + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/foo', $requests[0]->getResource()); + $this->assertEquals('GET', $requests[0]->getMethod()); + $this->assertEquals('/redirect1', $requests[1]->getResource()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('/redirect2', $requests[2]->getResource()); + $this->assertEquals('GET', $requests[2]->getMethod()); + + // Ensure that the redirect count was incremented + $this->assertEquals(2, $request->getParams()->get(RedirectPlugin::REDIRECT_COUNT)); + $this->assertCount(3, $history); + $requestHistory = $history->getAll(); + + $this->assertEquals(301, $requestHistory[0]['response']->getStatusCode()); + $this->assertEquals('/redirect1', (string) $requestHistory[0]['response']->getHeader('Location')); + $this->assertEquals(301, $requestHistory[1]['response']->getStatusCode()); + $this->assertEquals('/redirect2', (string) $requestHistory[1]['response']->getHeader('Location')); + $this->assertEquals(200, $requestHistory[2]['response']->getStatusCode()); + } + + public function testCanLimitNumberOfRedirects() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" + )); + + try { + $client = new Client($this->getServer()->getUrl()); + $client->get('/foo')->send(); + $this->fail('Did not throw expected exception'); + } catch (TooManyRedirectsException $e) { + $this->assertContains( + "5 redirects were issued for this request:\nGET /foo HTTP/1.1\r\n", + $e->getMessage() + ); + } + } + + public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $client->post('/foo', array('X-Baz' => 'bar'), 'testing')->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('GET', $requests[2]->getMethod()); + } + + public function testCanRedirectWithStrictRfcCompliance() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo', array('X-Baz' => 'bar'), 'testing'); + $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('POST', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('POST', $requests[2]->getMethod()); + } + + public function testRedirect303WithGet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo'); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRedirect303WithGetWithStrictRfcCompliance() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo'); + $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRewindsStreamWhenRedirectingIfNeeded() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(true); + $body = EntityBody::factory('foo'); + $body->read(1); + $request->setBody($body); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('foo', (string) $requests[0]->getBody()); + } + + /** + * @expectedException \Guzzle\Http\Exception\CouldNotRewindStreamException + */ + public function testThrowsExceptionWhenStreamCannotBeRewound() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(true); + $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); + $body->read(1); + $request->setBody($body)->send(); + } + + public function testRedirectsCanBeDisabledPerRequest() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(false, 0); + $this->assertEquals(301, $request->send()->getStatusCode()); + } + + public function testCanRedirectWithNoLeadingSlashAndQuery() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect?foo=bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('?foo=bar'); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $requests[0]->getUrl()); + $this->assertEquals($this->getServer()->getUrl() . 'redirect?foo=bar', $requests[1]->getUrl()); + // Ensure that the history on the actual request is correct + $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $request->getUrl()); + } + + public function testRedirectWithStrictRfc386Compliance() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/foo'); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/redirect', $requests[1]->getResource()); + } + + public function testResetsHistoryEachSend() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + // Create a client that uses the default redirect behavior + $client = new Client($this->getServer()->getUrl()); + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + $request = $client->get('/foo'); + $response = $request->send(); + $this->assertEquals(3, count($history)); + $this->assertTrue($request->getParams()->hasKey('redirect.count')); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + $request->send(); + $this->assertFalse($request->getParams()->hasKey('redirect.count')); + } + + public function testHandlesRedirectsWithSpacesProperly() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/foo'); + $request->send(); + $reqs = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/redirect%201', $reqs[1]->getResource()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php new file mode 100755 index 000000000..94eb59a4d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php @@ -0,0 +1,191 @@ +port = $port ?: self::DEFAULT_PORT; + $this->client = new Client($this->getUrl()); + register_shutdown_function(array($this, 'stop')); + } + + /** + * Flush the received requests from the server + * @throws RuntimeException + */ + public function flush() + { + $this->client->delete('guzzle-server/requests')->send(); + } + + /** + * Queue an array of responses or a single response on the server. + * + * Any currently queued responses will be overwritten. Subsequent requests + * on the server will return queued responses in FIFO order. + * + * @param array|Response $responses A single or array of Responses to queue + * @throws BadResponseException + */ + public function enqueue($responses) + { + $data = array(); + foreach ((array) $responses as $response) { + + // Create the response object from a string + if (is_string($response)) { + $response = Response::fromMessage($response); + } elseif (!($response instanceof Response)) { + throw new BadResponseException('Responses must be strings or implement Response'); + } + + $data[] = array( + 'statusCode' => $response->getStatusCode(), + 'reasonPhrase' => $response->getReasonPhrase(), + 'headers' => $response->getHeaders()->toArray(), + 'body' => $response->getBody(true) + ); + } + + $request = $this->client->put('guzzle-server/responses', null, json_encode($data)); + $request->send(); + } + + /** + * Check if the server is running + * + * @return bool + */ + public function isRunning() + { + if ($this->running) { + return true; + } + + try { + $this->client->get('guzzle-server/perf', array(), array('timeout' => 5))->send(); + $this->running = true; + return true; + } catch (\Exception $e) { + return false; + } + } + + /** + * Get the URL to the server + * + * @return string + */ + public function getUrl() + { + return 'http://127.0.0.1:' . $this->getPort() . '/'; + } + + /** + * Get the port that the server is listening on + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Get all of the received requests + * + * @param bool $hydrate Set to TRUE to turn the messages into + * actual {@see RequestInterface} objects. If $hydrate is FALSE, + * requests will be returned as strings. + * + * @return array + * @throws RuntimeException + */ + public function getReceivedRequests($hydrate = false) + { + $response = $this->client->get('guzzle-server/requests')->send(); + $data = array_filter(explode(self::REQUEST_DELIMITER, $response->getBody(true))); + if ($hydrate) { + $data = array_map(function($message) { + return RequestFactory::getInstance()->fromMessage($message); + }, $data); + } + + return $data; + } + + /** + * Start running the node.js server in the background + */ + public function start() + { + if (!$this->isRunning()) { + exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR + . 'server.js ' . $this->port + . ' >> /tmp/server.log 2>&1 &'); + // Wait at most 5 seconds for the server the setup before + // proceeding. + $start = time(); + while (!$this->isRunning() && time() - $start < 5); + if (!$this->running) { + throw new RuntimeException( + 'Unable to contact server.js. Have you installed node.js v0.5.0+? node must be in your path.' + ); + } + } + } + + /** + * Stop running the node.js server + */ + public function stop() + { + if (!$this->isRunning()) { + return false; + } + + $this->running = false; + $this->client->delete('guzzle-server')->send(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php new file mode 100755 index 000000000..091314bb7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php @@ -0,0 +1,67 @@ +assertTrue(class_exists('FooBazBar')); + $this->assertSame($client, $this->readAttribute('Guzzle\Http\StaticClient', 'client')); + } + + public function requestProvider() + { + return array_map( + function ($m) { return array($m); }, + array('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS') + ); + } + + /** + * @dataProvider requestProvider + */ + public function testSendsRequests($method) + { + $mock = new MockPlugin(array(new Response(200))); + call_user_func('Guzzle\Http\StaticClient::' . $method, 'http://foo.com', array( + 'plugins' => array($mock) + )); + $requests = $mock->getReceivedRequests(); + $this->assertCount(1, $requests); + $this->assertEquals($method, $requests[0]->getMethod()); + } + + public function testCanCreateStreamsUsingDefaultFactory() + { + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); + $stream = StaticClient::get($this->getServer()->getUrl(), array('stream' => true)); + $this->assertInstanceOf('Guzzle\Stream\StreamInterface', $stream); + $this->assertEquals('test', (string) $stream); + } + + public function testCanCreateStreamsUsingCustomFactory() + { + $stream = $this->getMockBuilder('Guzzle\Stream\StreamRequestFactoryInterface') + ->setMethods(array('fromRequest')) + ->getMockForAbstractClass(); + $resource = new Stream(fopen('php://temp', 'r+')); + $stream->expects($this->once()) + ->method('fromRequest') + ->will($this->returnValue($resource)); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); + $result = StaticClient::get($this->getServer()->getUrl(), array('stream' => $stream)); + $this->assertSame($resource, $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php new file mode 100755 index 000000000..28f26718b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php @@ -0,0 +1,303 @@ +assertEquals('', (string) $url); + } + + public function testPortIsDeterminedFromScheme() + { + $this->assertEquals(80, Url::factory('http://www.test.com/')->getPort()); + $this->assertEquals(443, Url::factory('https://www.test.com/')->getPort()); + $this->assertEquals(null, Url::factory('ftp://www.test.com/')->getPort()); + $this->assertEquals(8192, Url::factory('http://www.test.com:8192/')->getPort()); + } + + public function testCloneCreatesNewInternalObjects() + { + $u1 = Url::factory('http://www.test.com/'); + $u2 = clone $u1; + $this->assertNotSame($u1->getQuery(), $u2->getQuery()); + } + + public function testValidatesUrlPartsInFactory() + { + $url = Url::factory('/index.php'); + $this->assertEquals('/index.php', (string) $url); + $this->assertFalse($url->isAbsolute()); + + $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; + $u = Url::factory($url); + $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); + $this->assertTrue($u->isAbsolute()); + } + + public function testAllowsFalsyUrlParts() + { + $url = Url::factory('http://0:50/0?0#0'); + $this->assertSame('0', $url->getHost()); + $this->assertEquals(50, $url->getPort()); + $this->assertSame('/0', $url->getPath()); + $this->assertEquals('0', (string) $url->getQuery()); + $this->assertSame('0', $url->getFragment()); + $this->assertEquals('http://0:50/0?0#0', (string) $url); + + $url = Url::factory(''); + $this->assertSame('', (string) $url); + + $url = Url::factory('0'); + $this->assertSame('0', (string) $url); + } + + public function testBuildsRelativeUrlsWithFalsyParts() + { + $url = Url::buildUrl(array( + 'host' => '0', + 'path' => '0', + )); + + $this->assertSame('//0/0', $url); + + $url = Url::buildUrl(array( + 'path' => '0', + )); + $this->assertSame('0', $url); + } + + public function testUrlStoresParts() + { + $url = Url::factory('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); + $this->assertEquals('http', $url->getScheme()); + $this->assertEquals('test', $url->getUsername()); + $this->assertEquals('pass', $url->getPassword()); + $this->assertEquals('www.test.com', $url->getHost()); + $this->assertEquals(8081, $url->getPort()); + $this->assertEquals('/path/path2/', $url->getPath()); + $this->assertEquals('fragment', $url->getFragment()); + $this->assertEquals('a=1&b=2', (string) $url->getQuery()); + + $this->assertEquals(array( + 'fragment' => 'fragment', + 'host' => 'www.test.com', + 'pass' => 'pass', + 'path' => '/path/path2/', + 'port' => 8081, + 'query' => 'a=1&b=2', + 'scheme' => 'http', + 'user' => 'test' + ), $url->getParts()); + } + + public function testHandlesPathsCorrectly() + { + $url = Url::factory('http://www.test.com'); + $this->assertEquals('', $url->getPath()); + $url->setPath('test'); + $this->assertEquals('test', $url->getPath()); + + $url->setPath('/test/123/abc'); + $this->assertEquals(array('test', '123', 'abc'), $url->getPathSegments()); + + $parts = parse_url('http://www.test.com/test'); + $parts['path'] = ''; + $this->assertEquals('http://www.test.com', Url::buildUrl($parts)); + $parts['path'] = 'test'; + $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); + } + + public function testAddsQueryStringIfPresent() + { + $this->assertEquals('?foo=bar', Url::buildUrl(array( + 'query' => 'foo=bar' + ))); + } + + public function testAddsToPath() + { + // Does nothing here + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(false)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(null)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(array())); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(new \stdClass())); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('')); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/')); + $this->assertEquals('http://e.com/baz/foo', (string) Url::factory('http://e.com/baz/')->addPath('foo')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('relative')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/relative')); + $this->assertEquals('http://e.com/base/0', (string) Url::factory('http://e.com/base')->addPath('0')); + $this->assertEquals('http://e.com/base/0/1', (string) Url::factory('http://e.com/base')->addPath('0')->addPath('1')); + } + + /** + * URL combination data provider + * + * @return array + */ + public function urlCombineDataProvider() + { + return array( + array('http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'), + array('http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'), + array('http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'), + array('http://www.example.com/path', 'more', 'http://www.example.com/path/more'), + array('http://www.example.com/path', 'more?q=1', 'http://www.example.com/path/more?q=1'), + array('http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'), + array('http://www.example.com/path', 'http://test.com', 'http://test.com'), + array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com'), + array('http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'), + array('http://u:a@www.example.com/path', 'test', 'http://u:a@www.example.com/path/test'), + array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'), + array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'), + array('http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'), + array('http://www.example.com/?foo=bar', 'some/path', 'http://www.example.com/some/path?foo=bar'), + array('http://www.example.com/?foo=bar', 'some/path?boo=moo', 'http://www.example.com/some/path?boo=moo&foo=bar'), + array('http://www.example.com/some/', 'path?foo=bar&foo=baz', 'http://www.example.com/some/path?foo=bar&foo=baz'), + ); + } + + /** + * @dataProvider urlCombineDataProvider + */ + public function testCombinesUrls($a, $b, $c) + { + $this->assertEquals($c, (string) Url::factory($a)->combine($b)); + } + + public function testHasGettersAndSetters() + { + $url = Url::factory('http://www.test.com/'); + $this->assertEquals('example.com', $url->setHost('example.com')->getHost()); + $this->assertEquals('8080', $url->setPort(8080)->getPort()); + $this->assertEquals('/foo/bar', $url->setPath(array('foo', 'bar'))->getPath()); + $this->assertEquals('a', $url->setPassword('a')->getPassword()); + $this->assertEquals('b', $url->setUsername('b')->getUsername()); + $this->assertEquals('abc', $url->setFragment('abc')->getFragment()); + $this->assertEquals('https', $url->setScheme('https')->getScheme()); + $this->assertEquals('a=123', (string) $url->setQuery('a=123')->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?a=123#abc', (string) $url); + $this->assertEquals('b=boo', (string) $url->setQuery(new QueryString(array( + 'b' => 'boo' + )))->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?b=boo#abc', (string) $url); + } + + public function testSetQueryAcceptsArray() + { + $url = Url::factory('http://www.test.com'); + $url->setQuery(array('a' => 'b')); + $this->assertEquals('http://www.test.com?a=b', (string) $url); + } + + public function urlProvider() + { + return array( + array('/foo/..', '/'), + array('//foo//..', '/'), + array('/foo/../..', '/'), + array('/foo/../.', '/'), + array('/./foo/..', '/'), + array('/./foo', '/foo'), + array('/./foo/', '/foo/'), + array('/./foo/bar/baz/pho/../..', '/foo/bar'), + array('*', '*'), + array('/foo', '/foo'), + array('/abc/123/../foo/', '/abc/foo/'), + array('/a/b/c/./../../g', '/a/g'), + array('/b/c/./../../g', '/g'), + array('/b/c/./../../g', '/g'), + array('/c/./../../g', '/g'), + array('/./../../g', '/g'), + ); + } + + /** + * @dataProvider urlProvider + */ + public function testNormalizesPaths($path, $result) + { + $url = Url::factory('http://www.example.com/'); + $url->setPath($path)->normalizePath(); + $this->assertEquals($result, $url->getPath()); + } + + public function testSettingHostWithPortModifiesPort() + { + $url = Url::factory('http://www.example.com'); + $url->setHost('foo:8983'); + $this->assertEquals('foo', $url->getHost()); + $this->assertEquals(8983, $url->getPort()); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesUrlCanBeParsed() + { + Url::factory('foo:////'); + } + + public function testConvertsSpecialCharsInPathWhenCastingToString() + { + $url = Url::factory('http://foo.com/baz bar?a=b'); + $url->addPath('?'); + $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); + } + + /** + * @link http://tools.ietf.org/html/rfc3986#section-5.4.1 + */ + public function rfc3986UrlProvider() + { + $result = array( + array('g', 'http://a/b/c/g'), + array('./g', 'http://a/b/c/g'), + array('g/', 'http://a/b/c/g/'), + array('/g', 'http://a/g'), + array('?y', 'http://a/b/c/d;p?y'), + array('g?y', 'http://a/b/c/g?y'), + array('#s', 'http://a/b/c/d;p?q#s'), + array('g#s', 'http://a/b/c/g#s'), + array('g?y#s', 'http://a/b/c/g?y#s'), + array(';x', 'http://a/b/c/;x'), + array('g;x', 'http://a/b/c/g;x'), + array('g;x?y#s', 'http://a/b/c/g;x?y#s'), + array('', 'http://a/b/c/d;p?q'), + array('.', 'http://a/b/c'), + array('./', 'http://a/b/c/'), + array('..', 'http://a/b'), + array('../', 'http://a/b/'), + array('../g', 'http://a/b/g'), + array('../..', 'http://a/'), + array('../../', 'http://a/'), + array('../../g', 'http://a/g') + ); + + // This support was added in PHP 5.4.7: https://bugs.php.net/bug.php?id=62844 + if (version_compare(PHP_VERSION, '5.4.7', '>=')) { + $result[] = array('//g', 'http://g'); + } + + return $result; + } + + /** + * @dataProvider rfc3986UrlProvider + */ + public function testCombinesUrlsUsingRfc3986($relative, $result) + { + $a = Url::factory('http://a/b/c/d;p?q'); + $b = Url::factory($relative); + $this->assertEquals($result, trim((string) $a->combine($b, true), '=')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js new file mode 100755 index 000000000..4156f1aad --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js @@ -0,0 +1,146 @@ +/** + * Guzzle node.js test server to return queued responses to HTTP requests and + * expose a RESTful API for enqueueing responses and retrieving the requests + * that have been received. + * + * - Delete all requests that have been received: + * DELETE /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Enqueue responses + * PUT /guzzle-server/responses + * Host: 127.0.0.1:8124 + * + * [{ "statusCode": 200, "reasonPhrase": "OK", "headers": {}, "body": "" }] + * + * - Get the received requests + * GET /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Shutdown the server + * DELETE /guzzle-server + * Host: 127.0.0.1:8124 + * + * @package Guzzle PHP + * @license See the LICENSE file that was distributed with this source code. + */ + +var http = require("http"); + +/** + * Guzzle node.js server + * @class + */ +var GuzzleServer = function(port, log) { + + this.port = port; + this.log = log; + this.responses = []; + this.requests = []; + var that = this; + + var controlRequest = function(request, req, res) { + if (req.url == '/guzzle-server/perf') { + res.writeHead(200, "OK", {"Content-Length": 16}); + res.end("Body of response"); + } else if (req.method == "DELETE") { + if (req.url == "/guzzle-server/requests") { + // Clear the received requests + that.requests = []; + res.writeHead(200, "OK", { "Content-Length": 0 }); + res.end(); + if (this.log) { + console.log("Flushing requests"); + } + } else if (req.url == "/guzzle-server") { + // Shutdown the server + res.writeHead(200, "OK", { "Content-Length": 0, "Connection": "close" }); + res.end(); + if (this.log) { + console.log("Shutting down"); + } + that.server.close(); + } + } else if (req.method == "GET") { + if (req.url === "/guzzle-server/requests") { + // Get received requests + var data = that.requests.join("\n----[request]\n"); + res.writeHead(200, "OK", { "Content-Length": data.length }); + res.end(data); + if (that.log) { + console.log("Sending receiving requests"); + } + } + } else if (req.method == "PUT") { + if (req.url == "/guzzle-server/responses") { + if (that.log) { + console.log("Adding responses..."); + } + // Received response to queue + var data = request.split("\r\n\r\n")[1]; + if (!data) { + if (that.log) { + console.log("No response data was provided"); + } + res.writeHead(400, "NO RESPONSES IN REQUEST", { "Content-Length": 0 }); + } else { + that.responses = eval("(" + data + ")"); + if (that.log) { + console.log(that.responses); + } + res.writeHead(200, "OK", { "Content-Length": 0 }); + } + res.end(); + } + } + }; + + var receivedRequest = function(request, req, res) { + if (req.url.indexOf("/guzzle-server") === 0) { + controlRequest(request, req, res); + } else if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) { + res.writeHead(500); + res.end("No responses in queue"); + } else { + var response = that.responses.shift(); + res.writeHead(response.statusCode, response.reasonPhrase, response.headers); + res.end(response.body); + that.requests.push(request); + } + }; + + this.start = function() { + + that.server = http.createServer(function(req, res) { + + var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n"; + for (var i in req.headers) { + request += i + ": " + req.headers[i] + "\r\n"; + } + request += "\r\n"; + + // Receive each chunk of the request body + req.addListener("data", function(chunk) { + request += chunk; + }); + + // Called when the request completes + req.addListener("end", function() { + receivedRequest(request, req, res); + }); + }); + that.server.listen(port, "127.0.0.1"); + + if (this.log) { + console.log("Server running at http://127.0.0.1:8124/"); + } + }; +}; + +// Get the port from the arguments +port = process.argv.length >= 3 ? process.argv[2] : 8124; +log = process.argv.length >= 4 ? process.argv[3] : false; + +// Start the server +server = new GuzzleServer(port, log); +server.start(); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php new file mode 100755 index 000000000..990c0af66 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php @@ -0,0 +1,37 @@ +assertSame(Inflector::getDefault(), Inflector::getDefault()); + } + + public function testSnake() + { + $this->assertEquals('camel_case', Inflector::getDefault()->snake('camelCase')); + $this->assertEquals('camel_case', Inflector::getDefault()->snake('CamelCase')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCaseWords')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCase_words')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('expect100_continue', Inflector::getDefault()->snake('Expect100Continue')); + } + + public function testCamel() + { + $this->assertEquals('CamelCase', Inflector::getDefault()->camel('camel_case')); + $this->assertEquals('CamelCaseWords', Inflector::getDefault()->camel('camel_case_words')); + $this->assertEquals('Test', Inflector::getDefault()->camel('test')); + $this->assertEquals('Expect100Continue', ucfirst(Inflector::getDefault()->camel('expect100_continue'))); + // Get from cache + $this->assertEquals('Test', Inflector::getDefault()->camel('test', false)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php new file mode 100755 index 000000000..f00b7fad8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php @@ -0,0 +1,46 @@ +getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->will($this->returnValue('foo_bar')); + $mock->expects($this->once())->method('camel')->will($this->returnValue('FooBar')); + + $inflector = new MemoizingInflector($mock); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + } + + public function testProtectsAgainstCacheOverflow() + { + $inflector = new MemoizingInflector(new Inflector(), 10); + for ($i = 1; $i < 11; $i++) { + $inflector->camel('foo_' . $i); + $inflector->snake('Foo' . $i); + } + + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(10, count($cache['snake'])); + $this->assertEquals(10, count($cache['camel'])); + + $inflector->camel('baz!'); + $inflector->snake('baz!'); + + // Now ensure that 20% of the cache was removed (2), then the item was added + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(9, count($cache['snake'])); + $this->assertEquals(9, count($cache['camel'])); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php new file mode 100755 index 000000000..ff2654cf6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->with('Test')->will($this->returnValue('test')); + $mock->expects($this->once())->method('camel')->with('Test')->will($this->returnValue('Test')); + $inflector = new PreComputedInflector($mock, array('FooBar' => 'foo_bar'), array('foo_bar' => 'FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('Test', $inflector->camel('Test')); + $this->assertEquals('test', $inflector->snake('Test')); + } + + public function testMirrorsPrecomputedValues() + { + $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array(), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + } + + public function testMirrorsPrecomputedValuesByMerging() + { + $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array('foo' => 'Foo'), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + $this->assertEquals('Foo', $inflector->camel('foo')); + $this->assertEquals('foo', $inflector->snake('Foo')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php new file mode 100755 index 000000000..8d6ae845a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php @@ -0,0 +1,29 @@ + 1, + 'b' => 2 + )); + $b = new \ArrayIterator(array()); + $c = new \ArrayIterator(array( + 'c' => 3, + 'd' => 4 + )); + $i = new AppendIterator(); + $i->append($a); + $i->append($b); + $i->append($c); + $this->assertEquals(array(1, 2, 3, 4), iterator_to_array($i, false)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php new file mode 100755 index 000000000..ec4c1294c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php @@ -0,0 +1,52 @@ +assertEquals(11, count($chunks)); + foreach ($chunks as $j => $chunk) { + $this->assertEquals(range($j * 10, min(100, $j * 10 + 9)), $chunk); + } + } + + public function testChunksIteratorWithOddValues() + { + $chunked = new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2); + $chunks = iterator_to_array($chunked, false); + $this->assertEquals(3, count($chunks)); + $this->assertEquals(array(1, 2), $chunks[0]); + $this->assertEquals(array(3, 4), $chunks[1]); + $this->assertEquals(array(5), $chunks[2]); + } + + public function testMustNotTerminateWithTraversable() + { + $traversable = simplexml_load_string('')->foo; + $chunked = new ChunkedIterator($traversable, 2); + $actual = iterator_to_array($chunked, false); + $this->assertCount(2, $actual); + } + + public function testSizeOfZeroMakesIteratorInvalid() { + $chunked = new ChunkedIterator(new \ArrayIterator(range(1, 5)), 0); + $chunked->rewind(); + $this->assertFalse($chunked->valid()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSizeLowerZeroThrowsException() { + new ChunkedIterator(new \ArrayIterator(range(1, 5)), -1); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php new file mode 100755 index 000000000..73b4f6987 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(1, 99, 2), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new FilterIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php new file mode 100755 index 000000000..4de4a6bc1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(0, 1000, 10), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new MapIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php new file mode 100755 index 000000000..5bcf06fb0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php @@ -0,0 +1,28 @@ +append('a'); + $proxy->append('b'); + $this->assertEquals(array('a', 'b'), $i->getArrayCopy()); + $this->assertEquals(array('a', 'b'), $proxy->getArrayCopy()); + } + + public function testUsesInnerIterator() + { + $i = new MethodProxyIterator(new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2)); + $this->assertEquals(3, count(iterator_to_array($i, false))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php new file mode 100755 index 000000000..a66882f6f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php @@ -0,0 +1,23 @@ +log('test', \LOG_NOTICE, '127.0.0.1'); + $this->assertEquals(array(array('message' => 'test', 'priority' => \LOG_NOTICE, 'extras' => '127.0.0.1')), $adapter->getLogs()); + } + + public function testClearLog() + { + $adapter = new ArrayLogAdapter(); + $adapter->log('test', \LOG_NOTICE, '127.0.0.1'); + $adapter->clearLogs(); + $this->assertEquals(array(), $adapter->getLogs()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php new file mode 100755 index 000000000..0177dc071 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php @@ -0,0 +1,30 @@ +adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that, &$modified) { + $modified = array($message, $priority, $extras); + }); + $this->adapter->log('test', LOG_NOTICE, '127.0.0.1'); + $this->assertEquals(array('test', LOG_NOTICE, '127.0.0.1'), $modified); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenNotCallable() + { + $this->adapter = new ClosureLogAdapter(123); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php new file mode 100755 index 000000000..3ff4b0737 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php @@ -0,0 +1,143 @@ +request = new EntityEnclosingRequest('POST', 'http://foo.com?q=test', array( + 'X-Foo' => 'bar', + 'Authorization' => 'Baz' + )); + $this->request->setBody(EntityBody::factory('Hello')); + + $this->response = new Response(200, array( + 'X-Test' => 'Abc' + ), 'Foo'); + + $this->handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') + ->disableOriginalConstructor() + ->setMethods(array('getError', 'getErrorNo', 'getStderr', 'getInfo')) + ->getMock(); + + $this->handle->expects($this->any()) + ->method('getError') + ->will($this->returnValue('e')); + + $this->handle->expects($this->any()) + ->method('getErrorNo') + ->will($this->returnValue('123')); + + $this->handle->expects($this->any()) + ->method('getStderr') + ->will($this->returnValue('testing')); + + $this->handle->expects($this->any()) + ->method('getInfo') + ->will($this->returnValueMap(array( + array(CURLINFO_CONNECT_TIME, '123'), + array(CURLINFO_TOTAL_TIME, '456') + ))); + } + + public function logProvider() + { + return array( + // Uses the cache for the second time + array('{method} - {method}', 'POST - POST'), + array('{url}', 'http://foo.com?q=test'), + array('{port}', '80'), + array('{resource}', '/?q=test'), + array('{host}', 'foo.com'), + array('{hostname}', gethostname()), + array('{protocol}/{version}', 'HTTP/1.1'), + array('{code} {phrase}', '200 OK'), + array('{req_header_Foo}', ''), + array('{req_header_X-Foo}', 'bar'), + array('{req_header_Authorization}', 'Baz'), + array('{res_header_foo}', ''), + array('{res_header_X-Test}', 'Abc'), + array('{req_body}', 'Hello'), + array('{res_body}', 'Foo'), + array('{curl_stderr}', 'testing'), + array('{curl_error}', 'e'), + array('{curl_code}', '123'), + array('{connect_time}', '123'), + array('{total_time}', '456') + ); + } + + /** + * @dataProvider logProvider + */ + public function testFormatsMessages($template, $output) + { + $formatter = new MessageFormatter($template); + $this->assertEquals($output, $formatter->format($this->request, $this->response, $this->handle)); + } + + public function testFormatsRequestsAndResponses() + { + $formatter = new MessageFormatter(); + $formatter->setTemplate('{request}{response}'); + $this->assertEquals($this->request . $this->response, $formatter->format($this->request, $this->response)); + } + + public function testAddsTimestamp() + { + $formatter = new MessageFormatter('{ts}'); + $this->assertNotEmpty($formatter->format($this->request, $this->response)); + } + + public function testUsesResponseWhenNoHandleAndGettingCurlInformation() + { + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setConstructorArgs(array(200)) + ->setMethods(array('getInfo')) + ->getMock(); + $response->expects($this->exactly(2)) + ->method('getInfo') + ->will($this->returnValueMap(array( + array('connect_time', '1'), + array('total_time', '2'), + ))); + $this->assertEquals('1/2', $formatter->format($this->request, $response)); + } + + public function testUsesEmptyStringWhenNoHandleAndNoResponse() + { + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $this->assertEquals('/', $formatter->format($this->request)); + } + + public function testInjectsTotalTime() + { + $out = ''; + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $adapter = new ClosureLogAdapter(function ($m) use (&$out) { $out .= $m; }); + $log = new LogPlugin($adapter, $formatter); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI"); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber($log); + $client->get('/')->send(); + $this->assertNotEquals('/', $out); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php new file mode 100755 index 000000000..7b72dd6a0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php @@ -0,0 +1,25 @@ +pushHandler($handler); + $adapter = new PsrLogAdapter($log); + $adapter->log('test!', LOG_INFO); + $this->assertTrue($handler->hasInfoRecords()); + $this->assertSame($log, $adapter->getLogObject()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php new file mode 100755 index 000000000..1b6128365 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php @@ -0,0 +1,51 @@ +stream = fopen('php://temp', 'r+'); + $this->log = new Logger(); + $this->log->addWriter(new Stream($this->stream)); + $this->adapter = new Zf2LogAdapter($this->log); + + } + + public function testLogsMessagesToAdaptedObject() + { + // Test without a priority + $this->adapter->log('Zend_Test!', \LOG_NOTICE); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(1, substr_count($contents, 'Zend_Test!')); + + // Test with a priority + $this->adapter->log('Zend_Test!', \LOG_ALERT); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(2, substr_count($contents, 'Zend_Test!')); + } + + public function testExposesAdaptedLogObject() + { + $this->assertEquals($this->log, $this->adapter->getLogObject()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php new file mode 100755 index 000000000..3fb6527be --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php @@ -0,0 +1,21 @@ +command = $command; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php new file mode 100755 index 000000000..aabb15f9c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php @@ -0,0 +1,25 @@ +command = $command; + $this->response = $response; + $this->message = 'Error from ' . $response; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php new file mode 100755 index 000000000..97a197487 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php @@ -0,0 +1,11 @@ +multiHandle; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php new file mode 100755 index 000000000..11e22eb46 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php @@ -0,0 +1,65 @@ +events as $event) { + if ($event->getName() == $eventName) { + return true; + } + } + + return false; + } + + public function getLastEvent() + { + return end($this->events); + } + + public function count() + { + return count($this->events); + } + + public function getGrouped() + { + $events = array(); + foreach ($this->events as $event) { + if (!isset($events[$event->getName()])) { + $events[$event->getName()] = array(); + } + $events[$event->getName()][] = $event; + } + + return $events; + } + + public function getData($event, $key, $occurrence = 0) + { + $grouped = $this->getGrouped(); + if (isset($grouped[$event])) { + return $grouped[$event][$occurrence][$key]; + } + + return null; + } + + public function update(Event $event) + { + $this->events[] = $event; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php new file mode 100755 index 000000000..e011959bb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php @@ -0,0 +1,7 @@ + 'allseeing-i.com', + 'path' => '/', + 'data' => array( + 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c' + ), + 'max_age' => NULL, + 'expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', + 'version' => NULL, + 'secure' => NULL, + 'discard' => NULL, + 'port' => NULL, + 'cookies' => array( + 'ASIHTTPRequestTestCookie' => 'This+is+the+value' + ), + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array('', false), + array('foo', false), + // Test setting a blank value for a cookie + array(array( + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), + array( + 'cookies' => array( + 'foo' => '' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting a value and removing quotes + array(array( + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), + array( + 'cookies' => array( + 'foo' => '1' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting multiple values + array(array( + 'foo=1; bar=2;', 'foo =1; bar = "2"', 'foo=1; bar=2'), + array( + 'cookies' => array( + 'foo' => '1', + 'bar' => '2', + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Tests getting the domain and path from a reference request + array(array( + 'foo=1; port="80,8081"; httponly', 'foo=1; port="80,8081"; domain=www.test.com; HttpOnly;', 'foo=1; ; domain=www.test.com; path=/path; port="80,8081"; HttpOnly;'), + array( + 'cookies' => array( + 'foo' => 1 + ), + 'data' => array(), + 'discard' => null, + 'domain' => 'www.test.com', + 'expires' => null, + 'max_age' => null, + 'path' => '/path', + 'port' => array('80', '8081'), + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => true + ), + 'http://www.test.com/path/' + ), + // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php + array( + 'justacookie=foo; domain=example.com', + array( + 'cookies' => array( + 'justacookie' => 'foo' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', + array( + 'cookies' => array( + 'expires' => 'tomorrow' + ), + 'domain' => '.example.com', + 'path' => '/Space Out/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'data' => array(), + 'discard' => null, + 'port' => null, + 'secure' => true, + 'version' => null, + 'max_age' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', + array( + 'cookies' => array( + 'domain' => 'unittests' + ), + 'domain' => 'example.com', + 'path' => '/some value/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', + array( + 'cookies' => array( + 'path' => 'indexAction' + ), + 'domain' => '.foo.com', + 'path' => '/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', + array( + 'cookies' => array( + 'secure' => 'sha1' + ), + 'domain' => 'some.really.deep.domain.com', + 'path' => '/', + 'secure' => true, + 'data' => array(), + 'discard' => null, + 'expires' => time() + 86400, + 'max_age' => 86400, + 'port' => null, + 'version' => 1, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', + array( + 'cookies' => array( + 'PHPSESSID' => '123456789+abcd%2Cef' + ), + 'domain' => '.localdomain', + 'path' => '/foo/baz', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => true, + 'data' => array(), + 'discard' => true, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // rfc6265#section-5.1.4 + array( + 'cookie=value', + array( + 'cookies' => array( + 'cookie' => 'value' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/some/path', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/some/path/test.html' + ), + array( + 'empty=path', + array( + 'cookies' => array( + 'empty' => 'path' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/test.html' + ), + array( + 'baz=qux', + array( + 'cookies' => array( + 'baz' => 'qux' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com?query=here' + ), + array( + 'test=noSlashPath; path=someString', + array( + 'cookies' => array( + 'test' => 'noSlashPath' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/real/path', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/real/path/' + ), + ); + } + + /** + * @dataProvider cookieParserDataProvider + */ + public function testParseCookie($cookie, $parsed, $url = null) + { + $c = $this->cookieParserClass; + $parser = new $c(); + + $request = null; + if ($url) { + $url = Url::factory($url); + $host = $url->getHost(); + $path = $url->getPath(); + } else { + $host = ''; + $path = ''; + } + + foreach ((array) $cookie as $c) { + $p = $parser->parseCookie($c, $host, $path); + + // Remove expires values from the assertion if they are relatively equal by allowing a 5 minute difference + if ($p['expires'] != $parsed['expires']) { + if (abs($p['expires'] - $parsed['expires']) < 300) { + unset($p['expires']); + unset($parsed['expires']); + } + } + + if (is_array($parsed)) { + foreach ($parsed as $key => $value) { + $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + + foreach ($p as $key => $value) { + $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + } else { + $this->assertEquals($parsed, $p); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php new file mode 100755 index 000000000..75d336fa5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php @@ -0,0 +1,22 @@ +parseCookie('foo=baz+bar', null, null, true); + $this->assertEquals(array( + 'foo' => 'baz bar' + ), $result['cookies']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php new file mode 100755 index 000000000..da58bb465 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php @@ -0,0 +1,225 @@ + 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => '', + 'port' => '', + 'path' => '/', + 'query' => '' + ), + 'headers' => array(), + 'body' => '' + )), + // Path and query string, multiple header values per header and case sensitive storage + array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( + 'method' => 'HEAD', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '', + 'path' => '/path', + 'query' => 'query=foo' + ), + 'headers' => array( + 'Host' => 'example.com', + 'X-Foo' => array('foo', 'foo', 'Baz'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + // Includes a body + array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( + 'method' => 'PUT', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'https', + 'host' => 'example.com', + 'port' => '443', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'host' => 'example.com:443', + 'Content-Length' => '4' + ), + 'body' => 'test' + )), + // Includes Authorization headers + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'Authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + // Include authorization header + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + ); + } + + public function responseProvider() + { + return array( + // Empty request + array('', false), + + array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'OK', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '400', + 'reason_phrase' => 'Bad Request', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 100 Continue\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '100', + 'reason_phrase' => 'Continue', + 'headers' => array(), + 'body' => '' + )), + array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '204', + 'reason_phrase' => 'No Content', + 'headers' => array( + 'X-Foo' => array('foo', 'foo'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'Ok that is great!', + 'headers' => array( + 'Content-Length' => 4 + ), + 'body' => 'Test' + )), + ); + } + + public function compareRequestResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['method'], $expected['method']); + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['request_url'], $expected['request_url']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + public function compareResponseResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['code'], $expected['code']); + $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + protected function normalizeHeaders($headers) + { + $normalized = array(); + foreach ($headers as $key => $value) { + $key = strtolower($key); + if (!isset($normalized[$key])) { + $normalized[$key] = $value; + } elseif (!is_array($normalized[$key])) { + $normalized[$key] = array($value); + } else { + $normalized[$key][] = $value; + } + } + + foreach ($normalized as $key => &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $normalized; + } + + public function compareHttpHeaders($result, $expected) + { + // Aggregate all headers case-insensitively + $result = $this->normalizeHeaders($result); + $expected = $this->normalizeHeaders($expected); + $this->assertEquals($result, $expected); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php new file mode 100755 index 000000000..2f5222893 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php @@ -0,0 +1,58 @@ +compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new MessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } + + public function testParsesRequestsWithMissingProtocol() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } + + public function testParsesRequestsWithMissingVersion() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } + + public function testParsesResponsesWithMissingReasonPhrase() + { + $parser = new MessageParser(); + $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); + $this->assertEquals('200', $parts['code']); + $this->assertEquals('', $parts['reason_phrase']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php new file mode 100755 index 000000000..6706e2063 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php @@ -0,0 +1,36 @@ +markTestSkipped('pecl_http is not available.'); + } + } + + /** + * @dataProvider requestProvider + */ + public function testParsesRequests($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php new file mode 100755 index 000000000..7675efb96 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php @@ -0,0 +1,33 @@ +registerParser('foo', $c); + $this->assertSame($c, $r->getParser('foo')); + } + + public function testReturnsNullWhenNotFound() + { + $r = new ParserRegistry(); + $this->assertNull($r->getParser('FOO')); + } + + public function testReturnsLazyLoadedDefault() + { + $r = new ParserRegistry(); + $c = $r->getParser('cookie'); + $this->assertInstanceOf('Guzzle\Parser\Cookie\CookieParser', $c); + $this->assertSame($c, $r->getParser('cookie')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php new file mode 100755 index 000000000..a05fc2e4d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php @@ -0,0 +1,113 @@ + 'value', + 'hello' => 'Hello World!', + 'empty' => '', + 'path' => '/foo/bar', + 'x' => '1024', + 'y' => '768', + 'null' => null, + 'list' => array('red', 'green', 'blue'), + 'keys' => array( + "semi" => ';', + "dot" => '.', + "comma" => ',' + ), + 'empty_keys' => array(), + ); + + return array_map(function($t) use ($params) { + $t[] = $params; + return $t; + }, array( + array('foo', 'foo'), + array('{var}', 'value'), + array('{hello}', 'Hello%20World%21'), + array('{+var}', 'value'), + array('{+hello}', 'Hello%20World!'), + array('{+path}/here', '/foo/bar/here'), + array('here?ref={+path}', 'here?ref=/foo/bar'), + array('X{#var}', 'X#value'), + array('X{#hello}', 'X#Hello%20World!'), + array('map?{x,y}', 'map?1024,768'), + array('{x,hello,y}', '1024,Hello%20World%21,768'), + array('{+x,hello,y}', '1024,Hello%20World!,768'), + array('{+path,x}/here', '/foo/bar,1024/here'), + array('{#x,hello,y}', '#1024,Hello%20World!,768'), + array('{#path,x}/here', '#/foo/bar,1024/here'), + array('X{.var}', 'X.value'), + array('X{.x,y}', 'X.1024.768'), + array('{/var}', '/value'), + array('{/var,x}/here', '/value/1024/here'), + array('{;x,y}', ';x=1024;y=768'), + array('{;x,y,empty}', ';x=1024;y=768;empty'), + array('{?x,y}', '?x=1024&y=768'), + array('{?x,y,empty}', '?x=1024&y=768&empty='), + array('?fixed=yes{&x}', '?fixed=yes&x=1024'), + array('{&x,y,empty}', '&x=1024&y=768&empty='), + array('{var:3}', 'val'), + array('{var:30}', 'value'), + array('{list}', 'red,green,blue'), + array('{list*}', 'red,green,blue'), + array('{keys}', 'semi,%3B,dot,.,comma,%2C'), + array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), + array('{+path:6}/here', '/foo/b/here'), + array('{+list}', 'red,green,blue'), + array('{+list*}', 'red,green,blue'), + array('{+keys}', 'semi,;,dot,.,comma,,'), + array('{+keys*}', 'semi=;,dot=.,comma=,'), + array('{#path:6}/here', '#/foo/b/here'), + array('{#list}', '#red,green,blue'), + array('{#list*}', '#red,green,blue'), + array('{#keys}', '#semi,;,dot,.,comma,,'), + array('{#keys*}', '#semi=;,dot=.,comma=,'), + array('X{.var:3}', 'X.val'), + array('X{.list}', 'X.red,green,blue'), + array('X{.list*}', 'X.red.green.blue'), + array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), + array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), + array('{/var:1,var}', '/v/value'), + array('{/list}', '/red,green,blue'), + array('{/list*}', '/red/green/blue'), + array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), + array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), + array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), + array('{;hello:5}', ';hello=Hello'), + array('{;list}', ';list=red,green,blue'), + array('{;list*}', ';list=red;list=green;list=blue'), + array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), + array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), + array('{?var:3}', '?var=val'), + array('{?list}', '?list=red,green,blue'), + array('{?list*}', '?list=red&list=green&list=blue'), + array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), + array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), + array('{&var:3}', '&var=val'), + array('{&list}', '&list=red,green,blue'), + array('{&list*}', '&list=red&list=green&list=blue'), + array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), + array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), + array('{.null}', ''), + array('{.null,var}', '.value'), + array('X{.empty_keys*}', 'X'), + array('X{.empty_keys}', 'X'), + // Test that missing expansions are skipped + array('test{&missing*}', 'test'), + // Test that multiple expansions can be set + array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + // Test more complex query string stuff + array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php new file mode 100755 index 000000000..633c5d539 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php @@ -0,0 +1,27 @@ +markTestSkipped('uri_template PECL extension must be installed to test PeclUriTemplate'); + } + } + + /** + * @dataProvider templateProvider + */ + public function testExpandsUriTemplates($template, $expansion, $params) + { + $uri = new PeclUriTemplate($template); + $this->assertEquals($expansion, $uri->expand($template, $params)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php new file mode 100755 index 000000000..5130d6f4b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php @@ -0,0 +1,106 @@ +assertEquals($expansion, $uri->expand($template, $params)); + } + + public function expressionProvider() + { + return array( + array( + '{+var*}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'var', 'modifier' => '*') + ) + ), + ), + array( + '{?keys,var,val}', array( + 'operator' => '?', + 'values' => array( + array('value' => 'keys', 'modifier' => ''), + array('value' => 'var', 'modifier' => ''), + array('value' => 'val', 'modifier' => '') + ) + ), + ), + array( + '{+x,hello,y}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'x', 'modifier' => ''), + array('value' => 'hello', 'modifier' => ''), + array('value' => 'y', 'modifier' => '') + ) + ) + ) + ); + } + + /** + * @dataProvider expressionProvider + */ + public function testParsesExpressions($exp, $data) + { + $template = new UriTemplate($exp); + + // Access the config object + $class = new \ReflectionClass($template); + $method = $class->getMethod('parseExpression'); + $method->setAccessible(true); + + $exp = substr($exp, 1, -1); + $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/90 + */ + public function testAllowsNestedArrayExpansion() + { + $template = new UriTemplate(); + + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => array('fun', 'ice cream') + ), + 'foo' => array( + 'baz' => array( + 'bar' => 'fizz', + 'test' => 'buzz' + ), + 'bam' => 'boo' + ) + )); + + $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/426 + */ + public function testSetRegex() + { + $template = new UriTemplate(); + $template->setRegex('/\<\$(.+)\>/'); + $this->assertSame('/foo', $template->expand('/<$a>', array('a' => 'foo'))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php new file mode 100755 index 000000000..16990a5a8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php @@ -0,0 +1,93 @@ +assertArrayHasKey('request.before_send', $events); + $this->assertArrayHasKey('request.exception', $events); + $this->assertArrayHasKey('curl.callback.progress', $events); + } + + public function testEnablesProgressCallbacks() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $event = new Event(array( + 'request' => $request + )); + $p->onBeforeSend($event); + $this->assertEquals(true, $request->getCurlOptions()->get('progress')); + } + + public function testAddsTimesOutAfterSending() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $handle = CurlHandle::factory($request); + $event = new Event(array( + 'request' => $request, + 'handle' => $handle->getHandle(), + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + } + + public function testEnsuresRequestIsSet() + { + $p = new AsyncPlugin(); + $event = new Event(array( + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + } + + public function testMasksCurlExceptions() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $e = new CurlException('Error'); + $event = new Event(array( + 'request' => $request, + 'exception' => $e + )); + $p->onRequestTimeout($event); + $this->assertEquals(RequestInterface::STATE_COMPLETE, $request->getState()); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + } + + public function testEnsuresIntegration() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 204 FOO\r\nContent-Length: 4\r\n\r\ntest"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/', null, array( + 'foo' => 'bar' + )); + $request->getEventDispatcher()->addSubscriber(new AsyncPlugin()); + $request->send(); + $this->assertEquals('', $request->getResponse()->getBody(true)); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $received[0]->getMethod()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php new file mode 100755 index 000000000..72af26308 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php @@ -0,0 +1,86 @@ +getMockBuilder('Guzzle\Plugin\Backoff\AbstractBackoffStrategy') + ->setMethods(array('getDelay', 'makesDecision')) + ->getMockForAbstractClass(); + } + + public function testReturnsZeroWhenNoNextAndGotNull() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); + $this->assertEquals(0, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsFalse() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(false)); + $this->assertEquals(false, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsNextValueWhenNullOrTrue() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); + $mock->expects($this->any())->method('makesDecision')->will($this->returnValue(false)); + + $mock2 = $this->getMockStrategy(); + $mock2->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(10)); + $mock2->expects($this->atLeastOnce())->method('makesDecision')->will($this->returnValue(true)); + $mock->setNext($mock2); + + $this->assertEquals(10, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsFalseWhenNullAndNoNext() + { + $request = new Request('GET', 'http://www.foo.com'); + $s = new TruncatedBackoffStrategy(2); + $this->assertFalse($s->getBackoffPeriod(0, $request)); + } + + public function testHasNext() + { + $a = new TruncatedBackoffStrategy(2); + $b = new TruncatedBackoffStrategy(2); + $a->setNext($b); + $this->assertSame($b, $a->getNext()); + } + + public function testSkipsOtherDecisionsInChainWhenOneReturnsTrue() + { + $a = new CallbackBackoffStrategy(function () { return null; }, true); + $b = new CallbackBackoffStrategy(function () { return true; }, true); + $c = new CallbackBackoffStrategy(function () { return null; }, true); + $d = new CallbackBackoffStrategy(function () { return 10; }, false); + $a->setNext($b); + $b->setNext($c); + $c->setNext($d); + $this->assertEquals(10, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); + } + + public function testReturnsZeroWhenDecisionMakerReturnsTrueButNoFurtherStrategiesAreInTheChain() + { + $a = new CallbackBackoffStrategy(function () { return null; }, true); + $b = new CallbackBackoffStrategy(function () { return true; }, true); + $a->setNext($b); + $this->assertSame(0, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php new file mode 100755 index 000000000..a64dd826e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php @@ -0,0 +1,110 @@ +message = ''; + } + + public function testHasEventList() + { + $this->assertEquals(1, count(BackoffLogger::getSubscribedEvents())); + } + + public function testLogsEvents() + { + list($logPlugin, $request, $response) = $this->getMocks(); + + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setConstructorArgs(array(503)) + ->setMethods(array('getInfo')) + ->getMock(); + + $response->expects($this->any()) + ->method('getInfo') + ->will($this->returnValue(2)); + + $handle = $this->getMockHandle(); + + $event = new Event(array( + 'request' => $request, + 'response' => $response, + 'retries' => 1, + 'delay' => 3, + 'handle' => $handle + )); + + $logPlugin->onRequestRetry($event); + $this->assertContains( + '] PUT http://www.example.com - 503 Service Unavailable - Retries: 1, Delay: 3, Time: 2, 2, cURL: 30 Foo', + $this->message + ); + } + + public function testCanSetTemplate() + { + $l = new BackoffLogger(new ClosureLogAdapter(function () {})); + $l->setTemplate('foo'); + $t = $this->readAttribute($l, 'formatter'); + $this->assertEquals('foo', $this->readAttribute($t, 'template')); + } + + /** + * @return array + */ + protected function getMocks() + { + $that = $this; + $logger = new ClosureLogAdapter(function ($message) use ($that) { + $that->message .= $message . "\n"; + }); + $logPlugin = new BackoffLogger($logger); + $response = new Response(503); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com', array( + 'Content-Length' => 3, + 'Foo' => 'Bar' + )); + + return array($logPlugin, $request, $response); + } + + /** + * @return CurlHandle + */ + protected function getMockHandle() + { + $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') + ->disableOriginalConstructor() + ->setMethods(array('getError', 'getErrorNo', 'getInfo')) + ->getMock(); + + $handle->expects($this->once()) + ->method('getError') + ->will($this->returnValue('Foo')); + + $handle->expects($this->once()) + ->method('getErrorNo') + ->will($this->returnValue(30)); + + $handle->expects($this->any()) + ->method('getInfo') + ->will($this->returnValue(2)); + + return $handle; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php new file mode 100755 index 000000000..496e49eb2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php @@ -0,0 +1,297 @@ +retried = false; + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + public function onRequestRetry(Event $event) + { + $this->retried = $event; + } + + public function testHasEventList() + { + $this->assertEquals(1, count(BackoffPlugin::getAllEvents())); + } + + public function testCreatesDefaultExponentialBackoffPlugin() + { + $plugin = BackoffPlugin::getExponentialBackoff(3, array(204), array(10)); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\BackoffPlugin', $plugin); + $strategy = $this->readAttribute($plugin, 'strategy'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\TruncatedBackoffStrategy', $strategy); + $this->assertEquals(3, $this->readAttribute($strategy, 'max')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\HttpBackoffStrategy', $strategy); + $this->assertEquals(array(204 => true), $this->readAttribute($strategy, 'errorCodes')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\CurlBackoffStrategy', $strategy); + $this->assertEquals(array(10 => true), $this->readAttribute($strategy, 'errorCodes')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\ExponentialBackoffStrategy', $strategy); + } + + public function testDoesNotRetryUnlessStrategyReturnsNumber() + { + $request = new Request('GET', 'http://www.example.com'); + $request->setState('transfer'); + + $mock = $this->getMockBuilder('Guzzle\Plugin\Backoff\BackoffStrategyInterface') + ->setMethods(array('getBackoffPeriod')) + ->getMockForAbstractClass(); + + $mock->expects($this->once()) + ->method('getBackoffPeriod') + ->will($this->returnValue(false)); + + $plugin = new BackoffPlugin($mock); + $plugin->addSubscriber($this); + $plugin->onRequestSent(new Event(array('request' => $request))); + $this->assertFalse($this->retried); + } + + public function testUpdatesRequestForRetry() + { + $request = new Request('GET', 'http://www.example.com'); + $request->setState('transfer'); + $response = new Response(500); + $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')->disableOriginalConstructor()->getMock(); + $e = new CurlException(); + $e->setCurlHandle($handle); + + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); + $plugin->addSubscriber($this); + + $event = new Event(array( + 'request' => $request, + 'response' => $response, + 'exception' => $e + )); + + $plugin->onRequestSent($event); + $this->assertEquals(array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle, + 'retries' => 1, + 'delay' => 10 + ), $this->readAttribute($this->retried, 'context')); + + $plugin->onRequestSent($event); + $this->assertEquals(array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle, + 'retries' => 2, + 'delay' => 10 + ), $this->readAttribute($this->retried, 'context')); + } + + public function testDoesNothingWhenNotRetryingAndPollingRequest() + { + $request = new Request('GET', 'http://www.foo.com'); + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); + $plugin->onRequestPoll(new Event(array('request' => $request))); + } + + public function testRetriesRequests() + { + // Create a script to return several 500 and 503 response codes + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new BackoffPlugin( + new TruncatedBackoffStrategy(3, + new HttpBackoffStrategy(null, + new CurlBackoffStrategy(null, + new ConstantBackoffStrategy(0.05) + ) + ) + ) + ); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + + // Make sure it eventually completed successfully + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('data', $request->getResponse()->getBody(true)); + + // Check that three requests were made to retry this request + $this->assertEquals(3, count($this->getServer()->getReceivedRequests(false))); + $this->assertEquals(2, $request->getParams()->get(BackoffPlugin::RETRY_PARAM)); + } + + /** + * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException + */ + public function testFailsOnTruncation() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n" + )); + + $plugin = new BackoffPlugin( + new TruncatedBackoffStrategy(2, + new HttpBackoffStrategy(null, + new ConstantBackoffStrategy(0.05) + ) + ) + ); + + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber($plugin); + $client->get()->send(); + } + + public function testRetriesRequestsWhenInParallel() + { + // Create a script to return several 500 and 503 response codes + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new BackoffPlugin( + new HttpBackoffStrategy(null, + new TruncatedBackoffStrategy(3, + new CurlBackoffStrategy(null, + new ConstantBackoffStrategy(0.1) + ) + ) + ) + ); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $requests = array(); + for ($i = 0; $i < 5; $i++) { + $requests[] = $client->get(); + } + $client->send($requests); + + $this->assertEquals(15, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Plugin\Backoff\BackoffPlugin + * @covers Guzzle\Http\Curl\CurlMulti + */ + public function testRetriesPooledRequestsUsingDelayAndPollingEvent() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + // Need to sleep for some time ensure that the polling works correctly in the observer + $plugin = new BackoffPlugin(new HttpBackoffStrategy(null, + new TruncatedBackoffStrategy(1, + new ConstantBackoffStrategy(0.5)))); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + // Make sure it eventually completed successfully + $this->assertEquals('data', $request->getResponse()->getBody(true)); + // Check that two requests were made to retry this request + $this->assertEquals(2, count($this->getServer()->getReceivedRequests(false))); + } + + public function testSeeksToBeginningOfRequestBodyWhenRetrying() + { + // Create a request with a body + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('abc'); + // Set the retry time to be something that will be retried always + $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2); + // Seek to the end of the stream + $request->getBody()->seek(3); + $this->assertEquals('', $request->getBody()->read(1)); + // Create a plugin that does not delay when retrying + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0)); + $plugin->onRequestPoll($this->getMockEvent($request)); + // Ensure that the stream was seeked to 0 + $this->assertEquals('a', $request->getBody()->read(1)); + } + + public function testDoesNotSeekOnRequestsWithNoBodyWhenRetrying() + { + // Create a request with a body + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2); + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0)); + $plugin->onRequestPoll($this->getMockEvent($request)); + } + + protected function getMockEvent(RequestInterface $request) + { + // Create a mock curl multi object + $multi = $this->getMockBuilder('Guzzle\Http\Curl\CurlMulti') + ->setMethods(array('remove', 'add')) + ->getMock(); + + // Create an event that is expected for the Poll event + $event = new Event(array( + 'request' => $request, + 'curl_multi' => $multi + )); + $event->setName(CurlMultiInterface::POLLING_REQUEST); + + return $event; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php new file mode 100755 index 000000000..c0ce10d5c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php @@ -0,0 +1,31 @@ +getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $strategy = new CallbackBackoffStrategy(function () { return 10; }, true); + $this->assertTrue($strategy->makesDecision()); + $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request)); + // Ensure it chains correctly when null is returned + $strategy = new CallbackBackoffStrategy(function () { return null; }, false); + $this->assertFalse($strategy->makesDecision()); + $this->assertFalse($strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php new file mode 100755 index 000000000..703eb4a22 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php @@ -0,0 +1,20 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(3.5, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(3.5, $strategy->getBackoffPeriod(1, $request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php new file mode 100755 index 000000000..0a5c3e28d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php @@ -0,0 +1,36 @@ +assertNotEmpty(CurlBackoffStrategy::getDefaultFailureCodes()); + $strategy = new CurlBackoffStrategy(); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $e = new CurlException(); + $e->setError('foo', CURLE_BAD_CALLING_ORDER); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, null, $e)); + + foreach (CurlBackoffStrategy::getDefaultFailureCodes() as $code) { + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, null, $e->setError('foo', $code))); + } + } + + public function testIgnoresNonErrors() + { + $strategy = new CurlBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, new Response(200))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php new file mode 100755 index 000000000..09965bcbf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php @@ -0,0 +1,23 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(1, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(2, $strategy->getBackoffPeriod(1, $request)); + $this->assertEquals(4, $strategy->getBackoffPeriod(2, $request)); + $this->assertEquals(8, $strategy->getBackoffPeriod(3, $request)); + $this->assertEquals(16, $strategy->getBackoffPeriod(4, $request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php new file mode 100755 index 000000000..ae68a4eb8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php @@ -0,0 +1,47 @@ +assertNotEmpty(HttpBackoffStrategy::getDefaultFailureCodes()); + $strategy = new HttpBackoffStrategy(); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + + $response = new Response(200); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(400); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + + foreach (HttpBackoffStrategy::getDefaultFailureCodes() as $code) { + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response->setStatus($code))); + } + } + + public function testAllowsCustomCodes() + { + $strategy = new HttpBackoffStrategy(array(204)); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $response = new Response(204); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(500); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + } + + public function testIgnoresNonErrors() + { + $strategy = new HttpBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php new file mode 100755 index 000000000..b4ce8e4af --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php @@ -0,0 +1,21 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(5, $strategy->getBackoffPeriod(1, $request)); + $this->assertEquals(10, $strategy->getBackoffPeriod(2, $request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php new file mode 100755 index 000000000..dea5a6878 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php @@ -0,0 +1,32 @@ +assertEmpty(ReasonPhraseBackoffStrategy::getDefaultFailureCodes()); + $strategy = new ReasonPhraseBackoffStrategy(array('Foo', 'Internal Server Error')); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $response = new Response(200); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(200, 'Foo'); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); + } + + public function testIgnoresNonErrors() + { + $strategy = new ReasonPhraseBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php new file mode 100755 index 000000000..5590dfb1c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php @@ -0,0 +1,30 @@ +assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertFalse($strategy->getBackoffPeriod(0, $request)); + $this->assertFalse($strategy->getBackoffPeriod(1, $request)); + $this->assertFalse($strategy->getBackoffPeriod(2, $request)); + + $response = new Response(500); + $strategy->setNext(new HttpBackoffStrategy(null, new ConstantBackoffStrategy(10))); + $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request, $response)); + $this->assertEquals(10, $strategy->getBackoffPeriod(1, $request, $response)); + $this->assertFalse($strategy->getBackoffPeriod(2, $request, $response)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php new file mode 100755 index 000000000..69da60a93 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php @@ -0,0 +1,441 @@ +assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + } + + public function testAddsDefaultCollaborators() + { + $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); + $plugin = new CachePlugin(array( + 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass() + )); + $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\CanCacheStrategyInterface', + $this->readAttribute($plugin, 'canCache') + ); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\RevalidationInterface', + $this->readAttribute($plugin, 'revalidation') + ); + } + + public function testAddsCallbackCollaborators() + { + $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); + $plugin = new CachePlugin(array('can_cache' => function () {})); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\CallbackCanCacheStrategy', + $this->readAttribute($plugin, 'canCache') + ); + } + + public function testCanPassCacheAsOnlyArgumentToConstructor() + { + $p = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); + $p = new CachePlugin(new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()))); + } + + public function testUsesCreatedCacheStorage() + { + $plugin = new CachePlugin(array( + 'adapter' => $this->getMockBuilder('Guzzle\Cache\CacheAdapterInterface')->getMockForAbstractClass() + )); + $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + } + + public function testUsesProvidedOptions() + { + $can = $this->getMockBuilder('Guzzle\Plugin\Cache\CanCacheStrategyInterface')->getMockForAbstractClass(); + $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\RevalidationInterface')->getMockForAbstractClass(); + $plugin = new CachePlugin(array( + 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(), + 'can_cache' => $can, + 'revalidation' => $revalidate + )); + $this->assertSame($can, $this->readAttribute($plugin, 'canCache')); + $this->assertSame($revalidate, $this->readAttribute($plugin, 'revalidation')); + } + + public function satisfyProvider() + { + $req1 = new Request('GET', 'http://foo.com', array('Cache-Control' => 'no-cache')); + + return array( + // The response is too old to satisfy the request + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-age=20')), new Response(200, array('Age' => 100)), false, false), + // The response cannot satisfy the request because it is stale + array(new Request('GET', 'http://foo.com'), new Response(200, array('Cache-Control' => 'max-age=10', 'Age' => 100)), false, false), + // Allows the expired response to satisfy the request because of the max-stale + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=15')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), true, false), + // Max stale is > than the allowed staleness + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=5')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), false, false), + // Performs cache revalidation + array($req1, new Response(200), true, true), + // Performs revalidation due to ETag on the response and no cache-control on the request + array(new Request('GET', 'http://foo.com'), new Response(200, array( + 'ETag' => 'ABC', + 'Expires' => date('c', strtotime('+1 year')) + )), true, true), + ); + } + + /** + * @dataProvider satisfyProvider + */ + public function testChecksIfResponseCanSatisfyRequest($request, $response, $can, $revalidates) + { + $didRevalidate = false; + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(); + $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') + ->setMethods(array('revalidate')) + ->setConstructorArgs(array($storage)) + ->getMockForAbstractClass(); + + $revalidate->expects($this->any()) + ->method('revalidate') + ->will($this->returnCallback(function () use (&$didRevalidate) { + $didRevalidate = true; + return true; + })); + + $plugin = new CachePlugin(array( + 'storage' => $storage, + 'revalidation' => $revalidate + )); + + $this->assertEquals($can, $plugin->canResponseSatisfyRequest($request, $response)); + $this->assertEquals($didRevalidate, $revalidates); + } + + public function satisfyFailedProvider() + { + return array( + // Neither has stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100)), false), + // Request has stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), + // Request has valid stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), + // Request has expired stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), false), + // Response has permanent stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error', )), true), + // Response has valid stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), true), + // Response has expired stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), + // Request has valid stale-if-error but response does not + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), + // Response has valid stale-if-error but request does not + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), false), + ); + } + + /** + * @dataProvider satisfyFailedProvider + */ + public function testChecksIfResponseCanSatisfyFailedRequest($request, $response, $can) + { + $plugin = new CachePlugin(); + + $this->assertEquals($can, $plugin->canResponseSatisfyFailedRequest($request, $response)); + } + + public function testDoesNothingWhenRequestIsNotCacheable() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->never())->method('fetch'); + + $plugin = new CachePlugin(array( + 'storage' => $storage, + 'can_cache' => new CallbackCanCacheStrategy(function () { return false; }) + )); + + $plugin->onRequestBeforeSend(new Event(array( + 'request' => new Request('GET', 'http://foo.com') + ))); + } + + public function satisfiableProvider() + { + $date = new \DateTime('-10 seconds'); + + return array( + // Fresh response + array(new Response(200, array(), 'foo')), + // Stale response + array(new Response(200, array('Date' => $date->format('c'), 'Cache-Control' => 'max-age=5'), 'foo')) + ); + } + + /** + * @dataProvider satisfiableProvider + */ + public function testInjectsSatisfiableResponses($response) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + + $storage->expects($this->once())->method('fetch')->will($this->returnValue($response)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + $plugin->onRequestSent(new Event(array('request' => $request, 'response' => $request->getResponse()))); + $this->assertEquals($response->getStatusCode(), $request->getResponse()->getStatusCode()); + $this->assertEquals((string) $response->getBody(), (string) $request->getResponse()->getBody()); + $this->assertTrue($request->getResponse()->hasHeader('Age')); + if ($request->getResponse()->isFresh() === false) { + $this->assertContains('110', (string) $request->getResponse()->getHeader('Warning')); + } + $this->assertSame( + sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), + (string) $request->getHeader('Via') + ); + $this->assertSame( + sprintf('%s GuzzleCache/%s',$request->getProtocolVersion(), Version::VERSION), + (string) $request->getResponse()->getHeader('Via') + ); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertTrue($request->getParams()->get('cache.hit')); + $this->assertTrue($request->getResponse()->hasHeader('X-Cache-Lookup')); + $this->assertTrue($request->getResponse()->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache-Lookup')); + } + + public function satisfiableOnErrorProvider() + { + $date = new \DateTime('-10 seconds'); + return array( + array( + new Response(200, array( + 'Date' => $date->format('c'), + 'Cache-Control' => 'max-age=5, stale-if-error' + ), 'foo'), + ) + ); + } + + /** + * @dataProvider satisfiableOnErrorProvider + */ + public function testInjectsSatisfiableResponsesOnError($cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + $plugin->onRequestError( + $event = new Event(array( + 'request' => $request, + 'response' => $request->getResponse(), + )) + ); + $response = $event['response']; + $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); + $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); + $this->assertTrue($response->hasHeader('Age')); + if ($response->isFresh() === false) { + $this->assertContains('110', (string) $response->getHeader('Warning')); + } + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertSame('error', $request->getParams()->get('cache.hit')); + $this->assertTrue($response->hasHeader('X-Cache-Lookup')); + $this->assertTrue($response->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + /** + * @dataProvider satisfiableOnErrorProvider + */ + public function testInjectsSatisfiableResponsesOnException($cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestException( + new Event(array( + 'request' => $request, + 'response' => $request->getResponse(), + 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), + )) + ); + $plugin->onRequestSent( + new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + )) + ); + $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); + $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); + $this->assertTrue($response->hasHeader('Age')); + if ($response->isFresh() === false) { + $this->assertContains('110', (string) $response->getHeader('Warning')); + } + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertSame('error', $request->getParams()->get('cache.hit')); + $this->assertTrue($response->hasHeader('X-Cache-Lookup')); + $this->assertTrue($response->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + public function unsatisfiableOnErrorProvider() + { + $date = new \DateTime('-10 seconds'); + + return array( + // no-store on request + array( + false, + array('Cache-Control' => 'no-store'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), + ), + // request expired + array( + true, + array('Cache-Control' => 'stale-if-error=4'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), + ), + // response expired + array( + true, + array('Cache-Control' => 'stale-if-error'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error=4'), 'foo'), + ), + ); + } + + /** + * @dataProvider unsatisfiableOnErrorProvider + */ + public function testDoesNotInjectUnsatisfiableResponsesOnError($requestCanCache, $requestHeaders, $cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', $requestHeaders); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestError( + $event = new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + )) + ); + + $this->assertSame($response, $event['response']); + } + + /** + * @dataProvider unsatisfiableOnErrorProvider + */ + public function testDoesNotInjectUnsatisfiableResponsesOnException($requestCanCache, $requestHeaders, $responseParts) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($responseParts)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', $requestHeaders); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestException( + $event = new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), + )) + ); + + $this->assertSame($response, $request->getResponse()); + } + + public function testCachesResponsesWhenCacheable() + { + $cache = new ArrayCache(); + $plugin = new CachePlugin($cache); + + $request = new Request('GET', 'http://foo.com'); + $response = new Response(200, array(), 'Foo'); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestSent(new Event(array( + 'request' => $request, + 'response' => $response + ))); + $data = $this->readAttribute($cache, 'data'); + $this->assertNotEmpty($data); + } + + public function testPurgesRequests() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('purge')) + ->getMockForAbstractClass(); + $storage->expects($this->atLeastOnce())->method('purge'); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('X-Foo' => 'Bar')); + $plugin->purge($request); + } + + public function testAutoPurgesRequests() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('purge')) + ->getMockForAbstractClass(); + $storage->expects($this->atLeastOnce())->method('purge'); + $plugin = new CachePlugin(array('storage' => $storage, 'auto_purge' => true)); + $client = new Client(); + $request = $client->put('http://foo.com', array('X-Foo' => 'Bar')); + $request->addSubscriber($plugin); + $request->setResponse(new Response(200), true); + $request->send(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php new file mode 100755 index 000000000..f3d9bafe2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php @@ -0,0 +1,72 @@ +assertTrue($c->canCacheRequest(new Request('DELETE', 'http://www.foo.com'))); + } + + /** + * The following is a bit of an integration test to ensure that the CachePlugin honors a + * custom can cache strategy. + */ + public function testIntegrationWithCachePlugin() + { + $c = new CallbackCanCacheStrategy( + function ($request) { return true; }, + function ($response) { return true; } + ); + + // Make a request and response that have no business being cached + $request = new Request('DELETE', 'http://www.foo.com'); + $response = Response::fromMessage( + "HTTP/1.1 200 OK\r\n" + . "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" + . "Last-Modified: Wed, 09 Jan 2013 08:48:53 GMT\r\n" + . "Content-Length: 2\r\n" + . "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n\r\n" + . "hi" + ); + + $this->assertTrue($c->canCacheRequest($request)); + $this->assertTrue($c->canCacheResponse($response)); + + $s = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultCacheStorage') + ->setConstructorArgs(array(new DoctrineCacheAdapter(new ArrayCache()))) + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + + $s->expects($this->once()) + ->method('fetch') + ->will($this->returnValue($response)); + + $plugin = new CachePlugin(array('can_cache' => $c, 'storage' => $s)); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('hi', $request->getResponse()->getBody(true)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php new file mode 100755 index 000000000..701a0155f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php @@ -0,0 +1,193 @@ + 'application/json')); + $response = new Response(200, array( + 'Content-Type' => 'application/json', + 'Connection' => 'close', + 'X-Foo' => 'Bar', + 'Vary' => 'Accept' + ), 'test'); + $s->cache($request, $response); + $data = $this->readAttribute($a, 'data'); + + return array( + 'cache' => $a, + 'adapter' => $c, + 'storage' => $s, + 'request' => $request, + 'response' => $response, + 'serialized' => end($data) + ); + } + + public function testReturnsNullForCacheMiss() + { + $cache = $this->getCache(); + $this->assertNull($cache['storage']->fetch(new Request('GET', 'http://test.com'))); + } + + public function testCachesRequests() + { + $cache = $this->getCache(); + $foundRequest = $foundBody = $bodyKey = false; + foreach ($this->readAttribute($cache['cache'], 'data') as $key => $v) { + if (strpos($v, 'foo.com')) { + $foundRequest = true; + $data = unserialize($v); + $bodyKey = $data[0][3]; + $this->assertInternalType('integer', $data[0][4]); + $this->assertFalse(isset($data[0][0]['connection'])); + $this->assertEquals('foo.com', $data[0][0]['host']); + } elseif ($v == 'test') { + $foundBody = $key; + } + } + $this->assertContains($bodyKey, $foundBody); + $this->assertTrue($foundRequest); + } + + public function testFetchesResponse() + { + $cache = $this->getCache(); + $response = $cache['storage']->fetch($cache['request']); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertFalse($response->hasHeader('Connection')); + $this->assertEquals('Bar', (string) $response->getHeader('X-Foo')); + $this->assertEquals('test', (string) $response->getBody()); + $this->assertTrue(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + } + + public function testDeletesRequestItemsAndBody() + { + $cache = $this->getCache(); + $cache['storage']->delete($cache['request']); + $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); + $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + } + + public function testCachesMultipleRequestsWithVary() + { + $cache = $this->getCache(); + $cache['request']->setHeader('Accept', 'application/xml'); + $response = $cache['response']->setHeader('Content-Type', 'application/xml'); + $response->setBody('123'); + $cache['storage']->cache($cache['request'], $response); + $data = $this->readAttribute($cache['cache'], 'data'); + foreach ($data as $v) { + if (strpos($v, 'foo.com')) { + $u = unserialize($v); + $this->assertEquals(2, count($u)); + $this->assertEquals($u[0][0]['accept'], 'application/xml'); + $this->assertEquals($u[0][1]['content-type'], 'application/xml'); + $this->assertEquals($u[1][0]['accept'], 'application/json'); + $this->assertEquals($u[1][1]['content-type'], 'application/json'); + $this->assertNotSame($u[0][3], $u[1][3]); + break; + } + } + } + + public function testPurgeRemovesAllMethodCaches() + { + $cache = $this->getCache(); + foreach (array('HEAD', 'POST', 'PUT', 'DELETE') as $method) { + $request = RequestFactory::getInstance()->cloneRequestWithMethod($cache['request'], $method); + $cache['storage']->cache($request, $cache['response']); + } + $cache['storage']->purge('http://foo.com'); + $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); + $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + $this->assertEquals( + array('DoctrineNamespaceCacheKey[]'), + array_keys($this->readAttribute($cache['cache'], 'data')) + ); + } + + public function testRemovesExpiredResponses() + { + $cache = $this->getCache(); + $request = new Request('GET', 'http://xyz.com'); + $response = new Response(200, array('Age' => 1000, 'Cache-Control' => 'max-age=-10000')); + $cache['storage']->cache($request, $response); + $this->assertNull($cache['storage']->fetch($request)); + $data = $this->readAttribute($cache['cache'], 'data'); + $this->assertFalse(in_array('xyz.com', $data)); + $this->assertTrue(in_array($cache['serialized'], $data)); + } + + public function testUsesVaryToDetermineResult() + { + $cache = $this->getCache(); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $cache['storage']->fetch($cache['request'])); + $request = new Request('GET', 'http://foo.com', array('Accept' => 'application/xml')); + $this->assertNull($cache['storage']->fetch($request)); + } + + public function testEnsuresResponseIsStillPresent() + { + $cache = $this->getCache(); + $data = $this->readAttribute($cache['cache'], 'data'); + $key = array_search('test', $data); + $cache['cache']->delete(substr($key, 1, -4)); + $this->assertNull($cache['storage']->fetch($cache['request'])); + } + + public function staleProvider() + { + return array( + array( + new Request('GET', 'http://foo.com', array('Accept' => 'foo')), + new Response(200, array('Cache-Control' => 'stale-if-error=100', 'Vary' => 'Accept')) + ), + array( + new Request('GET', 'http://foo.com', array('Accept' => 'foo')), + new Response(200, array('Cache-Control' => 'stale-if-error', 'Vary' => 'Accept')) + ) + ); + } + + /** + * @dataProvider staleProvider + */ + public function testUsesStaleTimeDirectiveForTtd($request, $response) + { + $cache = $this->getCache(); + $cache['storage']->cache($request, $response); + $data = $this->readAttribute($cache['cache'], 'data'); + foreach ($data as $v) { + if (strpos($v, 'foo.com')) { + $u = unserialize($v); + $this->assertGreaterThan($u[1][4], $u[0][4]); + break; + } + } + } + + public function testCanFilterCacheKeys() + { + $cache = $this->getCache(); + $cache['request']->getQuery()->set('auth', 'foo'); + $this->assertNull($cache['storage']->fetch($cache['request'])); + $cache['request']->getParams()->set('cache.key_filter', 'auth'); + $this->assertNotNull($cache['storage']->fetch($cache['request'])); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php new file mode 100755 index 000000000..de4d182a2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php @@ -0,0 +1,40 @@ +assertTrue($strategy->canCacheRequest($request)); + } + + public function testDoesNotCacheNoStore() + { + $strategy = new DefaultCanCacheStrategy(); + $request = new Request('GET', 'http://foo.com', array('cache-control' => 'no-store')); + $this->assertFalse($strategy->canCacheRequest($request)); + } + + public function testCanCacheResponse() + { + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setMethods(array('canCache')) + ->setConstructorArgs(array(200)) + ->getMock(); + $response->expects($this->once()) + ->method('canCache') + ->will($this->returnValue(true)); + $strategy = new DefaultCanCacheStrategy(); + $this->assertTrue($strategy->canCacheResponse($response)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php new file mode 100755 index 000000000..0699cb266 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php @@ -0,0 +1,248 @@ +getHttpDate('-100 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nCache-Control: max-age=2000000\r\nContent-Length: 0\r\n\r\n", + ), + // Forces revalidation that overwrites what is in cache + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: must-revalidate, no-cache\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nDatas", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nDate: " . $this->getHttpDate('now') . "\r\n\r\nDatas" + ), + // Throws an exception during revalidation + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . $this->getHttpDate('-3 hours') . "\r\n\r\nData", + "HTTP/1.1 500 INTERNAL SERVER ERROR\r\nContent-Length: 0\r\n\r\n" + ), + // ETag mismatch + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nETag: \"123\"\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nETag: \"123456\"\r\n\r\n", + ), + ); + } + + /** + * @dataProvider cacheRevalidationDataProvider + */ + public function testRevalidatesResponsesAgainstOriginServer($can, $request, $response, $validate = null, $result = null) + { + // Send some responses to the test server for cache validation + $server = $this->getServer(); + $server->flush(); + + if ($validate) { + $server->enqueue($validate); + } + + $request = RequestFactory::getInstance()->fromMessage("GET / HTTP/1.1\r\nHost: 127.0.0.1:" . $server->getPort() . "\r\n" . $request); + $response = Response::fromMessage($response); + $request->setClient(new Client()); + + $plugin = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); + $this->assertEquals( + $can, + $plugin->canResponseSatisfyRequest($request, $response), + '-> ' . $request . "\n" . $response + ); + + if ($result) { + $result = Response::fromMessage($result); + $result->removeHeader('Date'); + $request->getResponse()->removeHeader('Date'); + $request->getResponse()->removeHeader('Connection'); + // Get rid of dates + $this->assertEquals((string) $result, (string) $request->getResponse()); + } + + if ($validate) { + $this->assertEquals(1, count($server->getReceivedRequests())); + } + } + + public function testHandles404RevalidationResponses() + { + $request = new Request('GET', 'http://foo.com'); + $request->setClient(new Client()); + $badResponse = new Response(404, array(), 'Oh no!'); + $badRequest = clone $request; + $badRequest->setResponse($badResponse, true); + $response = new Response(200, array(), 'foo'); + + // Seed the cache + $s = new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); + $s->cache($request, $response); + $this->assertNotNull($s->fetch($request)); + + $rev = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') + ->setConstructorArgs(array($s)) + ->setMethods(array('createRevalidationRequest')) + ->getMock(); + + $rev->expects($this->once()) + ->method('createRevalidationRequest') + ->will($this->returnValue($badRequest)); + + try { + $rev->revalidate($request, $response); + $this->fail('Should have thrown an exception'); + } catch (BadResponseException $e) { + $this->assertSame($badResponse, $e->getResponse()); + $this->assertNull($s->fetch($request)); + } + } + + public function testCanRevalidateWithPlugin() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . + "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Content-Length: 2\r\n\r\nhi", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber(new CachePlugin()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(3, count($this->getServer()->getReceivedRequests())); + } + + public function testCanHandleRevalidationFailures() + { + $client = new Client($this->getServer()->getUrl()); + $lm = gmdate('c', time() - 60); + $mock = new MockPlugin(array( + new Response(200, array( + 'Date' => $lm, + 'Cache-Control' => 'max-age=100, must-revalidate, stale-if-error=9999', + 'Last-Modified' => $lm, + 'Content-Length' => 2 + ), 'hi'), + new CurlException('Bleh'), + new CurlException('Bleh') + )); + $client->addSubscriber(new CachePlugin()); + $client->addSubscriber($mock); + $client->get()->send(); + $response = $client->get()->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('hi', $response->getBody(true)); + $this->assertEquals(3, count($mock->getReceivedRequests())); + $this->assertEquals(0, count($mock->getQueue())); + } + + public function testCanHandleStaleIfErrorWhenRevalidating() + { + $lm = gmdate('c', time() - 60); + $mock = new MockPlugin(array( + new Response(200, array( + 'Date' => $lm, + 'Cache-Control' => 'must-revalidate, max-age=0, stale-if-error=1200', + 'Last-Modified' => $lm, + 'Content-Length' => 2 + ), 'hi'), + new CurlException('Oh no!'), + new CurlException('Oh no!') + )); + $cache = new CachePlugin(); + $client = new Client('http://www.example.com'); + $client->addSubscriber($cache); + $client->addSubscriber($mock); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $response = $client->get()->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertCount(0, $mock); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + /** + * @group issue-437 + */ + public function testDoesNotTouchClosureListeners() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . + "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Content-Length: 2\r\n\r\nhi", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber(new CachePlugin()); + $client->getEventDispatcher()->addListener('command.after_send', function(){}); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + } + +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php new file mode 100755 index 000000000..9af80f255 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php @@ -0,0 +1,19 @@ +assertFalse($deny->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php new file mode 100755 index 000000000..4bcc04bfa --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php @@ -0,0 +1,19 @@ +assertTrue($skip->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php new file mode 100755 index 000000000..5d0f668a6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php @@ -0,0 +1,385 @@ +jar = new ArrayCookieJar(); + } + + protected function getTestCookies() + { + return array( + new Cookie(array('name' => 'foo', 'value' => 'bar', 'domain' => 'foo.com', 'path' => '/', 'discard' => true)), + new Cookie(array('name' => 'test', 'value' => '123', 'domain' => 'baz.com', 'path' => '/foo', 'expires' => 2)), + new Cookie(array('name' => 'you', 'value' => '123', 'domain' => 'bar.com', 'path' => '/boo', 'expires' => time() + 1000)) + ); + } + + /** + * Provides test data for cookie cookieJar retrieval + */ + public function getCookiesDataProvider() + { + return array( + array(array('foo', 'baz', 'test', 'muppet', 'googoo'), '', '', '', false), + array(array('foo', 'baz', 'muppet', 'googoo'), '', '', '', true), + array(array('googoo'), 'www.example.com', '', '', false), + array(array('muppet', 'googoo'), 'test.y.example.com', '', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/test/', '', false), + array(array('googoo'), 'x.y.example.com', '/test/acme/test/', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('baz'), 'example.com', '', 'baz', false), + ); + } + + public function testStoresAndRetrievesCookies() + { + $cookies = $this->getTestCookies(); + foreach ($cookies as $cookie) { + $this->assertTrue($this->jar->add($cookie)); + } + + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(3, count($this->jar->getIterator())); + $this->assertEquals($cookies, $this->jar->all(null, null, null, false, false)); + } + + public function testRemovesExpiredCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeExpired(); + $this->assertEquals(array($cookies[0], $cookies[2]), $this->jar->all()); + } + + public function testRemovesTemporaryCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeTemporary(); + $this->assertEquals(array($cookies[2]), $this->jar->all()); + } + + public function testIsSerializable() + { + $this->assertEquals('[]', $this->jar->serialize()); + $this->jar->unserialize('[]'); + $this->assertEquals(array(), $this->jar->all()); + + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove discard and expired cookies + $serialized = $this->jar->serialize(); + $data = json_decode($serialized, true); + $this->assertEquals(1, count($data)); + + $a = new ArrayCookieJar(); + $a->unserialize($serialized); + $this->assertEquals(1, count($a)); + } + + public function testRemovesSelectively() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove foo.com cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + // Try again, removing no further cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + + // Remove bar.com cookies with path of /boo + $this->jar->remove('bar.com', '/boo'); + $this->assertEquals(1, count($this->jar)); + + // Remove cookie by name + $this->jar->remove(null, null, 'test'); + $this->assertEquals(0, count($this->jar)); + } + + public function testDoesNotAddIncompleteCookies() + { + $this->assertEquals(false, $this->jar->add(new Cookie())); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => 'foo' + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => false + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => true + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com' + )))); + } + + public function testDoesAddValidCookies() + { + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => 0 + )))); + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => 0.0 + )))); + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => '0' + )))); + } + + public function testOverwritesCookiesThatAreOlderOrDiscardable() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + unset($data['discard']); + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals(false, $c[0]->getDiscard()); + + // Make sure it doesn't duplicate the cookie + $this->jar->add(new Cookie($data)); + $this->assertEquals(1, count($this->jar)); + + // Make sure the more future-ful expiration date supersede the other + $data['expires'] = time() + 2000; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + $c = $this->jar->all(); + $this->assertNotEquals($t, $c[0]->getExpires()); + } + + public function testOverwritesCookiesThatHaveChanged() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + $data['value'] = 'boo'; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + // Changing the value plus a parameter also must overwrite the existing one + $data['value'] = 'zoo'; + $data['secure'] = false; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals('zoo', $c[0]->getValue()); + } + + public function testAddsCookiesFromResponseWithNoRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => array( + "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "FPCK3=AgBNbvoQAGpGEABZLRAAbFsQAF1tEABkDhAAeO0=; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "CH=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1", + "CH=AgBNbvoQAAEcEAApuhAAMJcQADQvEAAvGxAALe0QAD6uEAATwhAAC1AQAC8t; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1" + ) + )); + + $this->jar->addCookiesFromResponse($response); + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(1, count($this->jar->all(null, null, 'fpc'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'FPCK3'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'CH'))); + } + + public function testAddsCookiesFromResponseWithRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" + )); + $request = new Request('GET', 'http://www.example.com'); + $this->jar->addCookiesFromResponse($response, $request); + $this->assertEquals(1, count($this->jar)); + } + + public function getMatchingCookiesDataProvider() + { + return array( + array('https://example.com', array(0)), + array('http://example.com', array()), + array('https://example.com:8912', array()), + array('https://foo.example.com', array(0)), + array('http://foo.example.com/test/acme/', array(4)) + ); + } + + /** + * @dataProvider getMatchingCookiesDataProvider + */ + public function testReturnsCookiesMatchingRequests($url, $cookies) + { + $bag = array( + new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(443, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'baz', + 'value' => 'foobar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'test', + 'value' => '123', + 'domain' => 'www.foobar.com', + 'path' => '/path/', + 'discard' => true + )), + new Cookie(array( + 'name' => 'muppet', + 'value' => 'cookie_monster', + 'domain' => '.y.example.com', + 'path' => '/acme/', + 'comment' => 'Comment goes here...', + 'expires' => time() + 86400 + )), + new Cookie(array( + 'name' => 'googoo', + 'value' => 'gaga', + 'domain' => '.example.com', + 'path' => '/test/acme/', + 'max_age' => 1500, + 'version' => 2 + )) + ); + + foreach ($bag as $cookie) { + $this->jar->add($cookie); + } + + $request = new Request('GET', $url); + $results = $this->jar->getMatchingCookies($request); + $this->assertEquals(count($cookies), count($results)); + foreach ($cookies as $i) { + $this->assertContains($bag[$i], $results); + } + } + + /** + * @expectedException \Guzzle\Plugin\Cookie\Exception\InvalidCookieException + * @expectedExceptionMessage The cookie name must not contain invalid characters: abc:@123 + */ + public function testThrowsExceptionWithStrictMode() + { + $a = new ArrayCookieJar(); + $a->setStrictMode(true); + $a->add(new Cookie(array( + 'name' => 'abc:@123', + 'value' => 'foo', + 'domain' => 'bar' + ))); + } + + public function testRemoveExistingCookieIfEmpty() + { + // Add a cookie that should not be affected + $a = new Cookie(array( + 'name' => 'foo', + 'value' => 'nope', + 'domain' => 'foo.com', + 'path' => '/abc' + )); + $this->jar->add($a); + + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'foo.com', + 'path' => '/' + ); + + $b = new Cookie($data); + $this->assertTrue($this->jar->add($b)); + $this->assertEquals(2, count($this->jar)); + + // Try to re-set the same cookie with no value: assert that cookie is not added + $data['value'] = null; + $this->assertFalse($this->jar->add(new Cookie($data))); + // assert that original cookie has been deleted + $cookies = $this->jar->all('foo.com'); + $this->assertTrue(in_array($a, $cookies, true)); + $this->assertFalse(in_array($b, $cookies, true)); + $this->assertEquals(1, count($this->jar)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php new file mode 100755 index 000000000..ac9471fd8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php @@ -0,0 +1,63 @@ +file = tempnam('/tmp', 'file-cookies'); + } + + public function testLoadsFromFileFile() + { + $jar = new FileCookieJar($this->file); + $this->assertEquals(array(), $jar->all()); + unlink($this->file); + } + + public function testPersistsToFileFile() + { + $jar = new FileCookieJar($this->file); + $jar->add(new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'baz', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'boo', + 'value' => 'bar', + 'domain' => 'foo.com', + ))); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the file + $contents = file_get_contents($this->file); + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new FileCookieJar($this->file); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unlink($this->file); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php new file mode 100755 index 000000000..f8c175cce --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php @@ -0,0 +1,134 @@ +getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar') + ->setMethods(array('addCookiesFromResponse')) + ->getMock(); + + $mock->expects($this->exactly(1)) + ->method('addCookiesFromResponse') + ->with($response); + + $plugin = new CookiePlugin($mock); + $plugin->onRequestSent(new Event(array( + 'response' => $response + ))); + } + + public function testAddsCookiesToRequests() + { + $cookie = new Cookie(array( + 'name' => 'foo', + 'value' => 'bar' + )); + + $mock = $this->getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar') + ->setMethods(array('getMatchingCookies')) + ->getMock(); + + $mock->expects($this->once()) + ->method('getMatchingCookies') + ->will($this->returnValue(array($cookie))); + + $plugin = new CookiePlugin($mock); + + $client = new Client(); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get('http://www.example.com'); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + + $this->assertEquals('bar', $request->getCookie('foo')); + } + + public function testCookiesAreExtractedFromRedirectResponses() + { + $plugin = new CookiePlugin(new ArrayCookieJar()); + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 302 Moved Temporarily\r\n" . + "Set-Cookie: test=583551; expires=Wednesday, 23-Mar-2050 19:49:45 GMT; path=/\r\n" . + "Location: /redirect\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $client->get()->send(); + $request = $client->get(); + $request->send(); + $this->assertEquals('test=583551', $request->getHeader('Cookie')); + + $requests = $this->getServer()->getReceivedRequests(true); + // Confirm subsequent requests have the cookie. + $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); + // Confirm the redirected request has the cookie. + $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie')); + } + + public function testCookiesAreNotAddedWhenParamIsSet() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + + $jar->add(new Cookie(array( + 'domain' => 'example.com', + 'path' => '/', + 'name' => 'test', + 'value' => 'hi', + 'expires' => time() + 3600 + ))); + + $client = new Client('http://example.com'); + $client->getEventDispatcher()->addSubscriber($plugin); + + // Ensure that it is normally added + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertEquals('hi', $request->getCookie('test')); + + // Now ensure that it is not added + $request = $client->get(); + $request->getParams()->set('cookies.disable', true); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertNull($request->getCookie('test')); + } + + public function testProvidesCookieJar() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + $this->assertSame($jar, $plugin->getCookieJar()); + } + + public function testEscapesCookieDomains() + { + $cookie = new Cookie(array('domain' => '/foo/^$[A-Z]+/')); + $this->assertFalse($cookie->matchesDomain('foo')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php new file mode 100755 index 000000000..9fb0b4310 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php @@ -0,0 +1,223 @@ +assertEquals('/', $cookie->getPath()); + $this->assertEquals(array(), $cookie->getPorts()); + } + + public function testConvertsDateTimeMaxAgeToUnixTimestamp() + { + $cookie = new Cookie(array( + 'expires' => 'November 20, 1984' + )); + $this->assertTrue(is_numeric($cookie->getExpires())); + } + + public function testAddsExpiresBasedOnMaxAge() + { + $t = time(); + $cookie = new Cookie(array( + 'max_age' => 100 + )); + $this->assertEquals($t + 100, $cookie->getExpires()); + } + + public function testHoldsValues() + { + $t = time(); + $data = array( + 'name' => 'foo', + 'value' => 'baz', + 'path' => '/bar', + 'domain' => 'baz.com', + 'expires' => $t, + 'max_age' => 100, + 'comment' => 'Hi', + 'comment_url' => 'foo.com', + 'port' => array(1, 2), + 'version' => 2, + 'secure' => true, + 'discard' => true, + 'http_only' => true, + 'data' => array( + 'foo' => 'baz', + 'bar' => 'bam' + ) + ); + + $cookie = new Cookie($data); + $this->assertEquals($data, $cookie->toArray()); + + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('baz', $cookie->getValue()); + $this->assertEquals('baz.com', $cookie->getDomain()); + $this->assertEquals('/bar', $cookie->getPath()); + $this->assertEquals($t, $cookie->getExpires()); + $this->assertEquals(100, $cookie->getMaxAge()); + $this->assertEquals('Hi', $cookie->getComment()); + $this->assertEquals('foo.com', $cookie->getCommentUrl()); + $this->assertEquals(array(1, 2), $cookie->getPorts()); + $this->assertEquals(2, $cookie->getVersion()); + $this->assertTrue($cookie->getSecure()); + $this->assertTrue($cookie->getDiscard()); + $this->assertTrue($cookie->getHttpOnly()); + $this->assertEquals('baz', $cookie->getAttribute('foo')); + $this->assertEquals('bam', $cookie->getAttribute('bar')); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'bam' + ), $cookie->getAttributes()); + + $cookie->setName('a') + ->setValue('b') + ->setPath('c') + ->setDomain('bar.com') + ->setExpires(10) + ->setMaxAge(200) + ->setComment('e') + ->setCommentUrl('f') + ->setPorts(array(80)) + ->setVersion(3) + ->setSecure(false) + ->setHttpOnly(false) + ->setDiscard(false) + ->setAttribute('snoop', 'dog'); + + $this->assertEquals('a', $cookie->getName()); + $this->assertEquals('b', $cookie->getValue()); + $this->assertEquals('c', $cookie->getPath()); + $this->assertEquals('bar.com', $cookie->getDomain()); + $this->assertEquals(10, $cookie->getExpires()); + $this->assertEquals(200, $cookie->getMaxAge()); + $this->assertEquals('e', $cookie->getComment()); + $this->assertEquals('f', $cookie->getCommentUrl()); + $this->assertEquals(array(80), $cookie->getPorts()); + $this->assertEquals(3, $cookie->getVersion()); + $this->assertFalse($cookie->getSecure()); + $this->assertFalse($cookie->getDiscard()); + $this->assertFalse($cookie->getHttpOnly()); + $this->assertEquals('dog', $cookie->getAttribute('snoop')); + } + + public function testDeterminesIfExpired() + { + $c = new Cookie(); + $c->setExpires(10); + $this->assertTrue($c->isExpired()); + $c->setExpires(time() + 10000); + $this->assertFalse($c->isExpired()); + } + + public function testMatchesPorts() + { + $cookie = new Cookie(); + // Always matches when nothing is set + $this->assertTrue($cookie->matchesPort(2)); + + $cookie->setPorts(array(1, 2)); + $this->assertTrue($cookie->matchesPort(2)); + $this->assertFalse($cookie->matchesPort(100)); + } + + public function testMatchesDomain() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('baz.com'); + $this->assertTrue($cookie->matchesDomain('baz.com')); + $this->assertFalse($cookie->matchesDomain('bar.com')); + + $cookie->setDomain('.baz.com'); + $this->assertTrue($cookie->matchesDomain('.baz.com')); + $this->assertTrue($cookie->matchesDomain('foo.baz.com')); + $this->assertFalse($cookie->matchesDomain('baz.bar.com')); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('.com.'); + $this->assertFalse($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.local'); + $this->assertTrue($cookie->matchesDomain('example.local')); + } + + public function testMatchesPath() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesPath('/foo')); + + $cookie->setPath('/foo'); + + // o The cookie-path and the request-path are identical. + $this->assertTrue($cookie->matchesPath('/foo')); + $this->assertFalse($cookie->matchesPath('/bar')); + + // o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- + // path is a %x2F ("/") character. + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/fooBar')); + + // o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + $cookie->setPath('/foo/'); + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/fooBaz')); + $this->assertFalse($cookie->matchesPath('/foo')); + + } + + public function cookieValidateProvider() + { + return array( + array('foo', 'baz', 'bar', true), + array('0', '0', '0', true), + array('', 'baz', 'bar', 'The cookie name must not be empty'), + array('foo', '', 'bar', 'The cookie value must not be empty'), + array('foo', 'baz', '', 'The cookie domain must not be empty'), + array('foo\\', 'baz', '0', 'The cookie name must not contain invalid characters: foo\\'), + ); + } + + /** + * @dataProvider cookieValidateProvider + */ + public function testValidatesCookies($name, $value, $domain, $result) + { + $cookie = new Cookie(array( + 'name' => $name, + 'value' => $value, + 'domain' => $domain + )); + $this->assertSame($result, $cookie->validate()); + } + + public function testCreatesInvalidCharacterString() + { + $m = new \ReflectionMethod('Guzzle\Plugin\Cookie\Cookie', 'getInvalidCharacters'); + $m->setAccessible(true); + $p = new \ReflectionProperty('Guzzle\Plugin\Cookie\Cookie', 'invalidCharString'); + $p->setAccessible(true); + $p->setValue(''); + // Expects a string containing 51 invalid characters + $this->assertEquals(51, strlen($m->invoke($m))); + $this->assertContains('@', $m->invoke($m)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php new file mode 100755 index 000000000..2a4b49eb6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php @@ -0,0 +1,39 @@ +getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + Version::$emitWarnings = true; + } + + public function testAddsDigestAuthentication() + { + Version::$emitWarnings = false; + $plugin = new CurlAuthPlugin('julian', 'test', CURLAUTH_DIGEST); + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('julian', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + $this->assertEquals('julian:test', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + Version::$emitWarnings = true; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php new file mode 100755 index 000000000..6f94186b5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php @@ -0,0 +1,137 @@ +flush(); + } + + public function setUp() + { + $mockError = 'Guzzle\Tests\Mock\ErrorResponseMock'; + $description = ServiceDescription::factory(array( + 'operations' => array( + 'works' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => $mockError), + array('code' => 503, 'reason' => 'foo', 'class' => $mockError), + array('code' => 200, 'reason' => 'Error!', 'class' => $mockError) + ) + ), + 'bad_class' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => 'Does\\Not\\Exist') + ) + ), + 'does_not_implement' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => __CLASS__) + ) + ), + 'no_errors' => array('httpMethod' => 'GET'), + 'no_class' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500) + ) + ), + ) + )); + $this->client = new Client($this->getServer()->getUrl()); + $this->client->setDescription($description); + } + + /** + * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException + */ + public function testSkipsWhenErrorResponsesIsNotSet() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_errors')->execute(); + } + + public function testSkipsWhenErrorResponsesIsNotSetAndAllowsSuccess() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_errors')->execute(); + } + + /** + * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException + * @expectedExceptionMessage Does\Not\Exist does not exist + */ + public function testEnsuresErrorResponseExists() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('bad_class')->execute(); + } + + /** + * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException + * @expectedExceptionMessage must implement Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface + */ + public function testEnsuresErrorResponseImplementsInterface() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('does_not_implement')->execute(); + } + + public function testThrowsSpecificErrorResponseOnMatch() + { + try { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $command = $this->client->getCommand('works'); + $command->execute(); + $this->fail('Exception not thrown'); + } catch (ErrorResponseMock $e) { + $this->assertSame($command, $e->command); + $this->assertEquals(500, $e->response->getStatusCode()); + } + } + + /** + * @expectedException \Guzzle\Tests\Mock\ErrorResponseMock + */ + public function testThrowsWhenCodeAndPhraseMatch() + { + $this->getServer()->enqueue("HTTP/1.1 200 Error!\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('works')->execute(); + } + + public function testSkipsWhenReasonDoesNotMatch() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('works')->execute(); + } + + public function testSkipsWhenNoClassIsSet() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_class')->execute(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php new file mode 100755 index 000000000..41aa673fd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php @@ -0,0 +1,140 @@ +get(); + $requests[$i]->setResponse(new Response(200), true); + $requests[$i]->send(); + $h->add($requests[$i]); + } + + return $requests; + } + + public function testDescribesSubscribedEvents() + { + $this->assertInternalType('array', HistoryPlugin::getSubscribedEvents()); + } + + public function testMaintainsLimitValue() + { + $h = new HistoryPlugin(); + $this->assertSame($h, $h->setLimit(10)); + $this->assertEquals(10, $h->getLimit()); + } + + public function testAddsRequests() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 1); + $this->assertEquals(1, count($h)); + $i = $h->getIterator(); + $this->assertEquals(1, count($i)); + $this->assertEquals($requests[0], $i[0]); + } + + /** + * @depends testAddsRequests + */ + public function testMaintainsLimit() + { + $h = new HistoryPlugin(); + $h->setLimit(2); + $requests = $this->addRequests($h, 3); + $this->assertEquals(2, count($h)); + $i = 0; + foreach ($h as $request) { + if ($i > 0) { + $this->assertSame($requests[$i], $request); + } + } + } + + public function testReturnsLastRequest() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests), $h->getLastRequest()); + } + + public function testReturnsLastResponse() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests)->getResponse(), $h->getLastResponse()); + } + + public function testClearsHistory() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertEquals(5, count($h)); + $h->clear(); + $this->assertEquals(0, count($h)); + } + + /** + * @depends testAddsRequests + */ + public function testUpdatesAddRequests() + { + $h = new HistoryPlugin(); + $client = new Client('http://127.0.0.1/'); + $client->getEventDispatcher()->addSubscriber($h); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + + $this->assertSame($request, $h->getLastRequest()); + } + + public function testCanCastToString() + { + $client = new Client('http://127.0.0.1/'); + $h = new HistoryPlugin(); + $client->getEventDispatcher()->addSubscriber($h); + + $mock = new MockPlugin(array( + new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), + new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), + new Response(200, array('Content-Length' => '2'), 'HI') + )); + + $client->getEventDispatcher()->addSubscriber($mock); + $request = $client->get(); + $request->send(); + $this->assertEquals(3, count($h)); + $this->assertEquals(3, count($mock->getReceivedRequests())); + + $h = str_replace("\r", '', $h); + $this->assertContains("> GET / HTTP/1.1\nHost: 127.0.0.1\nUser-Agent:", $h); + $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); + $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); + $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php new file mode 100755 index 000000000..ad663a53c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php @@ -0,0 +1,95 @@ +adapter = new ClosureLogAdapter(function ($message) { + echo $message; + }); + } + + public function testIgnoresCurlEventsWhenNotWiringBodies() + { + $p = new LogPlugin($this->adapter); + $this->assertNotEmpty($p->getSubscribedEvents()); + $event = new Event(array('request' => new Request('GET', 'http://foo.com'))); + $p->onCurlRead($event); + $p->onCurlWrite($event); + $p->onRequestBeforeSend($event); + } + + public function testLogsWhenComplete() + { + $output = ''; + $p = new LogPlugin(new ClosureLogAdapter(function ($message) use (&$output) { + $output = $message; + }), '{method} {resource} | {code} {res_body}'); + + $p->onRequestSent(new Event(array( + 'request' => new Request('GET', 'http://foo.com'), + 'response' => new Response(200, array(), 'Foo') + ))); + + $this->assertEquals('GET / | 200 Foo', $output); + } + + public function testWiresBodiesWhenNeeded() + { + $client = new Client($this->getServer()->getUrl()); + $plugin = new LogPlugin($this->adapter, '{req_body} | {res_body}', true); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->put(); + + // Send the response from the dummy server as the request body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nsend"); + $stream = fopen($this->getServer()->getUrl(), 'r'); + $request->setBody(EntityBody::factory($stream, 4)); + + $tmpFile = tempnam(sys_get_temp_dir(), 'non_repeatable'); + $request->setResponseBody(EntityBody::factory(fopen($tmpFile, 'w'))); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse"); + + ob_start(); + $request->send(); + $message = ob_get_clean(); + + unlink($tmpFile); + $this->assertContains("send", $message); + $this->assertContains("response", $message); + } + + public function testHasHelpfulStaticFactoryMethod() + { + $s = fopen('php://temp', 'r+'); + $client = new Client(); + $client->addSubscriber(LogPlugin::getDebugPlugin(true, $s)); + $request = $client->put('http://foo.com', array('Content-Type' => 'Foo'), 'Bar'); + $request->setresponse(new Response(200), true); + $request->send(); + rewind($s); + $contents = stream_get_contents($s); + $this->assertContains('# Request:', $contents); + $this->assertContainsIns('PUT / HTTP/1.1', $contents); + $this->assertContains('# Response:', $contents); + $this->assertContainsIns('HTTP/1.1 200 OK', $contents); + $this->assertContains('# Errors:', $contents); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php new file mode 100755 index 000000000..4bd411113 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php @@ -0,0 +1,97 @@ + array( + 'test' => array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'ContentMD5' => array(), + 'Body' => array( + 'location' => 'body' + ) + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + + return $client; + } + + public function testHasEvents() + { + $this->assertNotEmpty(CommandContentMd5Plugin::getSubscribedEvents()); + } + + public function testValidatesMd5WhenParamExists() + { + $client = $this->getClient(); + $command = $client->getCommand('test', array( + 'Body' => 'Foo', + 'ContentMD5' => true + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $this->assertEquals('E1bGfXrRY42Ba/uCLdLCXQ==', (string) $request->getHeader('Content-MD5')); + } + + public function testDoesNothingWhenNoPayloadExists() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test'); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $this->assertNull($request->getHeader('Content-MD5')); + } + + public function testAddsValidationToResponsesOfContentMd5() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test', array( + 'ValidateMD5' => true + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $listeners = $request->getEventDispatcher()->getListeners('request.complete'); + $this->assertNotEmpty($listeners); + } + + public function testIgnoresValidationWhenDisabled() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test', array( + 'ValidateMD5' => false + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $listeners = $request->getEventDispatcher()->getListeners('request.complete'); + $this->assertEmpty($listeners); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php new file mode 100755 index 000000000..482e92b28 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php @@ -0,0 +1,120 @@ +create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $body = 'abc'; + $hash = md5($body); + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Length' => 3 + ), 'abc'); + + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with no Content-MD5 + $response->removeHeader('Content-MD5'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testThrowsExceptionOnInvalidMd5() + { + $plugin = new Md5ValidatorPlugin(); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + public function testSkipsWhenContentLengthIsTooLarge() + { + $plugin = new Md5ValidatorPlugin(false, 1); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + public function testProperlyValidatesWhenUsingContentEncoding() + { + $plugin = new Md5ValidatorPlugin(true); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + // Content-MD5 is the MD5 hash of the canonical content after all + // content-encoding has been applied. Because cURL will automatically + // decompress entity bodies, we need to re-compress it to calculate. + $body = EntityBody::factory('abc'); + $body->compress(); + $hash = $body->getContentMd5(); + $body->uncompress(); + + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'gzip' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + $this->assertEquals('abc', $response->getBody(true)); + + // Try again with an unknown encoding + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'foobar' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with compress + $body->compress('bzip2.compress'); + $response = new Response(200, array( + 'Content-MD5' => $body->getContentMd5(), + 'Content-Encoding' => 'compress' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with encoding and disabled content-encoding checks + $request->getEventDispatcher()->removeSubscriber($plugin); + $plugin = new Md5ValidatorPlugin(false); + $request->getEventDispatcher()->addSubscriber($plugin); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php new file mode 100755 index 000000000..3af8fefcc --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php @@ -0,0 +1,199 @@ +assertInternalType('array', MockPlugin::getSubscribedEvents()); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', MockPlugin::getAllEvents()); + } + + public function testCanBeTemporary() + { + $plugin = new MockPlugin(); + $this->assertFalse($plugin->isTemporary()); + $plugin = new MockPlugin(null, true); + $this->assertTrue($plugin->isTemporary()); + } + + public function testIsCountable() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $this->assertEquals(1, count($plugin)); + } + + /** + * @depends testIsCountable + */ + public function testCanClearQueue() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $plugin->clearQueue(); + $this->assertEquals(0, count($plugin)); + } + + public function testCanInspectQueue() + { + $plugin = new MockPlugin(); + $this->assertInternalType('array', $plugin->getQueue()); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $queue = $plugin->getQueue(); + $this->assertInternalType('array', $queue); + $this->assertEquals(1, count($queue)); + } + + public function testRetrievesResponsesFromFiles() + { + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response); + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenResponseFileIsNotFound() + { + MockPlugin::getMockFile('missing/filename'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidResponsesThrowAnException() + { + $p = new MockPlugin(); + $p->addResponse($this); + } + + public function testAddsResponseObjectsToQueue() + { + $p = new MockPlugin(); + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $p->addResponse($response); + $this->assertEquals(array($response), $p->getQueue()); + } + + public function testAddsResponseFilesToQueue() + { + $p = new MockPlugin(); + $p->addResponse(__DIR__ . '/../../TestData/mock_response'); + $this->assertEquals(1, count($p)); + } + + /** + * @depends testAddsResponseFilesToQueue + */ + public function testAddsMockResponseToRequestFromClient() + { + $p = new MockPlugin(); + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $p->addResponse($response); + + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertSame($response, $request->getResponse()); + $this->assertEquals(0, count($p)); + } + + /** + * @depends testAddsResponseFilesToQueue + * @expectedException \OutOfBoundsException + */ + public function testUpdateThrowsExceptionWhenEmpty() + { + $p = new MockPlugin(); + $p->onRequestBeforeSend(new Event()); + } + + /** + * @depends testAddsMockResponseToRequestFromClient + */ + public function testDetachesTemporaryWhenEmpty() + { + $p = new MockPlugin(null, true); + $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response')); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertFalse($this->hasSubscriber($client, $p)); + } + + public function testLoadsResponsesFromConstructor() + { + $p = new MockPlugin(array(new Response(200))); + $this->assertEquals(1, $p->count()); + } + + public function testStoresMockedRequests() + { + $p = new MockPlugin(array(new Response(200), new Response(200))); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + + $request1 = $client->get(); + $request1->send(); + $this->assertEquals(array($request1), $p->getReceivedRequests()); + + $request2 = $client->get(); + $request2->send(); + $this->assertEquals(array($request1, $request2), $p->getReceivedRequests()); + + $p->flush(); + $this->assertEquals(array(), $p->getReceivedRequests()); + } + + public function testReadsBodiesFromMockedRequests() + { + $p = new MockPlugin(array(new Response(200))); + $p->readBodies(true); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + + $body = EntityBody::factory('foo'); + $request = $client->put(); + $request->setBody($body); + $request->send(); + $this->assertEquals(3, $body->ftell()); + } + + public function testCanMockBadRequestExceptions() + { + $client = new Client('http://127.0.0.1:123/'); + $ex = new CurlException('Foo'); + $mock = new MockPlugin(array($ex)); + $client->addSubscriber($mock); + $request = $client->get('foo'); + + try { + $request->send(); + $this->fail('Did not dequeue an exception'); + } catch (CurlException $e) { + $this->assertSame($e, $ex); + $this->assertSame($request, $ex->getRequest()); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php new file mode 100755 index 000000000..3892fb62b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php @@ -0,0 +1,345 @@ + 'foo', + 'consumer_secret' => 'bar', + 'token' => 'count', + 'token_secret' => 'dracula' + ); + + protected function getRequest() + { + return RequestFactory::getInstance()->create('POST', 'http://www.test.com/path?a=b&c=d', null, array( + 'e' => 'f' + )); + } + + public function testSubscribesToEvents() + { + $events = OauthPlugin::getSubscribedEvents(); + $this->assertArrayHasKey('request.before_send', $events); + } + + public function testAcceptsConfigurationData() + { + $p = new OauthPlugin($this->config); + + // Access the config object + $class = new \ReflectionClass($p); + $property = $class->getProperty('config'); + $property->setAccessible(true); + $config = $property->getValue($p); + + $this->assertEquals('foo', $config['consumer_key']); + $this->assertEquals('bar', $config['consumer_secret']); + $this->assertEquals('count', $config['token']); + $this->assertEquals('dracula', $config['token_secret']); + $this->assertEquals('1.0', $config['version']); + $this->assertEquals('HMAC-SHA1', $config['signature_method']); + $this->assertEquals('header', $config['request_method']); + } + + public function testCreatesStringToSignFromPostRequest() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); + + $this->assertContains('&e=f', rawurldecode($signString)); + + $expectedSignString = + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0'; + + $this->assertEquals($expectedSignString, $signString); + } + + public function testCreatesStringToSignIgnoringPostFields() + { + $config = $this->config; + $config['disable_post_params'] = true; + $p = new OauthPlugin($config); + $request = $this->getRequest(); + $sts = rawurldecode($p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + $this->assertNotContains('&e=f', $sts); + } + + public function testCreatesStringToSignFromPostRequestWithCustomContentType() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->setHeader('Content-Type', 'Foo'); + $this->assertEquals( + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D'. self::NONCE .'%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0', + $p->getStringToSign($request, self::TIMESTAMP, self::NONCE) + ); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testConvertsBooleansToStrings() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->getQuery()->set('a', true); + $request->getQuery()->set('c', false); + $this->assertContains('&a%3Dtrue%26c%3Dfalse', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + public function testCreatesStringToSignFromPostRequestWithNullValues() + { + $config = array( + 'consumer_key' => 'foo', + 'consumer_secret' => 'bar', + 'token' => null, + 'token_secret' => 'dracula' + ); + + $p = new OauthPlugin($config); + $request = $this->getRequest(); + $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); + + $this->assertContains('&e=f', rawurldecode($signString)); + + $expectedSignString = // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_version%3D1.0'; + + $this->assertEquals($expectedSignString, $signString); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testMultiDimensionalArray() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->getQuery()->set('a', array('b' => array('e' => 'f', 'c' => 'd'))); + $this->assertContains('a%255Bb%255D%255Bc%255D%3Dd%26a%255Bb%255D%255Be%255D%3Df%26c%3Dd%26e%3Df%26', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + /** + * @depends testMultiDimensionalArray + */ + public function testMultiDimensionalArrayWithNonDefaultQueryAggregator() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $aggregator = new CommaAggregator(); + $query = $request->getQuery()->setAggregator($aggregator) + ->set('g', array('h', 'i', 'j')) + ->set('k', array('l')) + ->set('m', array('n', 'o')); + $this->assertContains('a%3Db%26c%3Dd%26e%3Df%26g%3Dh%2Ci%2Cj%26k%3Dl%26m%3Dn%2Co', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testSignsStrings() + { + $p = new OauthPlugin(array_merge($this->config, array( + 'signature_callback' => function($string, $key) { + return "_{$string}|{$key}_"; + } + ))); + $request = $this->getRequest(); + $sig = $p->getSignature($request, self::TIMESTAMP, self::NONCE); + $this->assertEquals( + '_POST&http%3A%2F%2Fwww.test.com%2Fpath&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D'. self::NONCE .'%26oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0|' . + 'bar&dracula_', + base64_decode($sig) + ); + } + + /** + * Test that the Oauth is signed correctly and that extra strings haven't been added + * to the authorization header. + */ + public function testSignsOauthRequests() + { + $p = new OauthPlugin($this->config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + $params = $p->onRequestBeforeSend($event); + + $this->assertTrue($event['request']->hasHeader('Authorization')); + + $authorizationHeader = (string)$event['request']->getHeader('Authorization'); + + $this->assertStringStartsWith('OAuth ', $authorizationHeader); + + $stringsToCheck = array( + 'oauth_consumer_key="foo"', + 'oauth_nonce="'.urlencode($params['oauth_nonce']).'"', + 'oauth_signature="'.urlencode($params['oauth_signature']).'"', + 'oauth_signature_method="HMAC-SHA1"', + 'oauth_timestamp="' . self::TIMESTAMP . '"', + 'oauth_token="count"', + 'oauth_version="1.0"', + ); + + $totalLength = strlen('OAuth '); + + //Separator is not used before first parameter. + $separator = ''; + + foreach ($stringsToCheck as $stringToCheck) { + $this->assertContains($stringToCheck, $authorizationHeader); + $totalLength += strlen($separator); + $totalLength += strlen($stringToCheck); + $separator = ', '; + } + + // Technically this test is not universally valid. It would be allowable to have extra \n characters + // in the Authorization header. However Guzzle does not do this, so we just perform a simple check + // on length to validate the Authorization header is composed of only the strings above. + $this->assertEquals($totalLength, strlen($authorizationHeader), 'Authorization has extra characters i.e. contains extra elements compared to stringsToCheck.'); + } + + public function testSignsOauthQueryStringRequest() + { + $config = array_merge( + $this->config, + array('request_method' => OauthPlugin::REQUEST_METHOD_QUERY) + ); + + $p = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + $params = $p->onRequestBeforeSend($event); + + $this->assertFalse($event['request']->hasHeader('Authorization')); + + $stringsToCheck = array( + 'a=b', + 'c=d', + 'oauth_consumer_key=foo', + 'oauth_nonce='.urlencode($params['oauth_nonce']), + 'oauth_signature='.urlencode($params['oauth_signature']), + 'oauth_signature_method=HMAC-SHA1', + 'oauth_timestamp='.self::TIMESTAMP, + 'oauth_token=count', + 'oauth_version=1.0', + ); + + $queryString = (string) $event['request']->getQuery(); + + $totalLength = strlen('?'); + + //Separator is not used before first parameter. + $separator = ''; + + foreach ($stringsToCheck as $stringToCheck) { + $this->assertContains($stringToCheck, $queryString); + $totalLength += strlen($separator); + $totalLength += strlen($stringToCheck); + $separator = '&'; + } + + // Removes the last query string separator '&' + $totalLength -= 1; + + $this->assertEquals($totalLength, strlen($queryString), 'Query string has extra characters i.e. contains extra elements compared to stringsToCheck.'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidArgumentExceptionOnMethodError() + { + $config = array_merge( + $this->config, + array('request_method' => 'FakeMethod') + ); + + $p = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + + $p->onRequestBeforeSend($event); + } + + public function testDoesNotAddFalseyValuesToAuthorization() + { + unset($this->config['token']); + $p = new OauthPlugin($this->config); + $event = new Event(array('request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP)); + $p->onRequestBeforeSend($event); + $this->assertTrue($event['request']->hasHeader('Authorization')); + $this->assertNotContains('oauth_token=', (string) $event['request']->getHeader('Authorization')); + } + + public function testOptionalOauthParametersAreNotAutomaticallyAdded() + { + // The only required Oauth parameters are the consumer key and secret. That is enough credentials + // for signing oauth requests. + $config = array( + 'consumer_key' => 'foo', + 'consumer_secret' => 'bar', + ); + + $plugin = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + + $timestamp = $plugin->getTimestamp($event); + $request = $event['request']; + $nonce = $plugin->generateNonce($request); + + $paramsToSign = $plugin->getParamsToSign($request, $timestamp, $nonce); + + $optionalParams = array( + 'callback' => 'oauth_callback', + 'token' => 'oauth_token', + 'verifier' => 'oauth_verifier', + 'token_secret' => 'token_secret' + ); + + foreach ($optionalParams as $optionName => $oauthName) { + $this->assertArrayNotHasKey($oauthName, $paramsToSign, "Optional Oauth param '$oauthName' was not set via config variable '$optionName', but it is listed in getParamsToSign()."); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php new file mode 100755 index 000000000..8b42fb839 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php @@ -0,0 +1,149 @@ +loader = $this->getMockBuilder('Guzzle\Service\AbstractConfigLoader') + ->setMethods(array('build')) + ->getMockForAbstractClass(); + } + + public function tearDown() + { + foreach ($this->cleanup as $file) { + unlink($file); + } + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnlyLoadsSupportedTypes() + { + $this->loader->load(new \stdClass()); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to open fooooooo.json + */ + public function testFileMustBeReadable() + { + $this->loader->load('fooooooo.json'); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unknown file extension + */ + public function testMustBeSupportedExtension() + { + $this->loader->load(dirname(__DIR__) . '/TestData/FileBody.txt'); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Error loading JSON data from + */ + public function testJsonMustBeValue() + { + $filename = tempnam(sys_get_temp_dir(), 'json') . '.json'; + file_put_contents($filename, '{/{./{}foo'); + $this->cleanup[] = $filename; + $this->loader->load($filename); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage PHP files must return an array + */ + public function testPhpFilesMustReturnAnArray() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, 'cleanup[] = $filename; + $this->loader->load($filename); + } + + public function testLoadsPhpFileIncludes() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, ' "bar");'); + $this->cleanup[] = $filename; + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $config = $this->loader->load($filename); + $this->assertEquals(array('foo' => 'bar'), $config); + } + + public function testCanCreateFromJson() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($file); + // Ensure that the config files were merged using the includes directives + $this->assertArrayHasKey('includes', $data); + $this->assertArrayHasKey('services', $data); + $this->assertInternalType('array', $data['services']['foo']); + $this->assertInternalType('array', $data['services']['abstract']); + $this->assertInternalType('array', $data['services']['mock']); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testUsesAliases() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $this->loader->addAlias('foo', $file); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load('foo'); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to open foo.json + */ + public function testCanRemoveAliases() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $this->loader->addAlias('foo.json', $file); + $this->loader->removeAlias('foo.json'); + $this->loader->load('foo.json'); + } + + public function testCanLoadArraysWithIncludes() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $config = array('includes' => array($file)); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($config); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testDoesNotEnterInfiniteLoop() + { + $prefix = $file = dirname(__DIR__) . '/TestData/description'; + $this->loader->load("{$prefix}/baz.json"); + $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); + // Ensure that the internal list of loaded files is reset + $this->loader->load("{$prefix}/../test_service2.json"); + $this->assertCount(1, $this->readAttribute($this->loader, 'loadedFiles')); + // Ensure that previously loaded files will be reloaded when starting fresh + $this->loader->load("{$prefix}/baz.json"); + $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php new file mode 100755 index 000000000..f63070e38 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php @@ -0,0 +1,177 @@ + array( + 'abstract' => array( + 'params' => array( + 'access_key' => 'xyz', + 'secret' => 'abc', + ), + ), + 'foo' => array( + 'extends' => 'abstract', + 'params' => array( + 'baz' => 'bar', + ), + ), + 'mock' => array( + 'extends' => 'abstract', + 'params' => array( + 'username' => 'foo', + 'password' => 'baz', + 'subdomain' => 'bar', + ) + ) + ) + ); + + $builder = $arrayFactory->load($data); + + // Ensure that services were parsed + $this->assertTrue(isset($builder['mock'])); + $this->assertTrue(isset($builder['abstract'])); + $this->assertTrue(isset($builder['foo'])); + $this->assertFalse(isset($builder['jimmy'])); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage foo is trying to extend a non-existent service: abstract + */ + public function testThrowsExceptionWhenExtendingNonExistentService() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'extends' => 'abstract' + ) + ) + ); + + $builder = $arrayFactory->load($data); + } + + public function testAllowsGlobalParameterOverrides() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'params' => array( + 'foo' => 'baz', + 'bar' => 'boo' + ) + ) + ) + ); + + $builder = $arrayFactory->load($data, array( + 'bar' => 'jar', + 'far' => 'car' + )); + + $compiled = json_decode($builder->serialize(), true); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'jar', + 'far' => 'car' + ), $compiled['foo']['params']); + } + + public function tstDoesNotErrorOnCircularReferences() + { + $arrayFactory = new ServiceBuilderLoader(); + $arrayFactory->load(array( + 'services' => array( + 'too' => array('extends' => 'ball'), + 'ball' => array('extends' => 'too'), + ) + )); + } + + public function configProvider() + { + $foo = array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '456') + ); + + return array( + array( + // Does not extend the existing `foo` service but overwrites it + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz') + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz'), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ), + array( + // Extends the existing `foo` service + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'foo', + 'params' => array('b' => '123', 'c' => 'def') + ) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '123', 'c' => 'def') + ), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ) + ); + } + + /** + * @dataProvider configProvider + */ + public function testCombinesConfigs($a, $b, $c) + { + $l = new ServiceBuilderLoader(); + $m = new \ReflectionMethod($l, 'mergeData'); + $m->setAccessible(true); + $this->assertEquals($c, $m->invoke($l, $a, $b)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php new file mode 100755 index 000000000..e1b3a1d49 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php @@ -0,0 +1,317 @@ + array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'michael', + 'password' => 'testing123', + 'subdomain' => 'michael', + ), + ), + 'billy.mock' => array( + 'alias' => 'Hello!', + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'billy', + 'password' => 'passw0rd', + 'subdomain' => 'billy', + ), + ), + 'billy.testing' => array( + 'extends' => 'billy.mock', + 'params' => array( + 'subdomain' => 'test.billy', + ), + ), + 'missing_params' => array( + 'extends' => 'billy.mock' + ) + ); + + public function testAllowsSerialization() + { + $builder = ServiceBuilder::factory($this->arrayData); + $cached = unserialize(serialize($builder)); + $this->assertEquals($cached, $builder); + } + + public function testDelegatesFactoryMethodToAbstractFactory() + { + $builder = ServiceBuilder::factory($this->arrayData); + $c = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage No service is registered as foobar + */ + public function testThrowsExceptionWhenGettingInvalidClient() + { + ServiceBuilder::factory($this->arrayData)->get('foobar'); + } + + public function testStoresClientCopy() + { + $builder = ServiceBuilder::factory($this->arrayData); + $client = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); + $this->assertEquals('http://127.0.0.1:8124/v1/michael', $client->getBaseUrl()); + $this->assertEquals($client, $builder->get('michael.mock')); + + // Get another client but throw this one away + $client2 = $builder->get('billy.mock', true); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client2); + $this->assertEquals('http://127.0.0.1:8124/v1/billy', $client2->getBaseUrl()); + + // Make sure the original client is still there and set + $this->assertTrue($client === $builder->get('michael.mock')); + + // Create a new billy.mock client that is stored + $client3 = $builder->get('billy.mock'); + + // Make sure that the stored billy.mock client is equal to the other stored client + $this->assertTrue($client3 === $builder->get('billy.mock')); + + // Make sure that this client is not equal to the previous throwaway client + $this->assertFalse($client2 === $builder->get('billy.mock')); + } + + public function testBuildersPassOptionsThroughToClients() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertEquals(8080, $c->getConfig('curl.curlopt_proxyport')); + } + + public function testUsesTheDefaultBuilderWhenNoBuilderIsSpecified() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c); + } + + public function testUsedAsArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $this->assertTrue($b->offsetExists('michael.mock')); + $this->assertFalse($b->offsetExists('not_there')); + $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']); + + unset($b['michael.mock']); + $this->assertFalse($b->offsetExists('michael.mock')); + + $b['michael.mock'] = new Client('http://www.test.com/'); + $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']); + } + + public function testFactoryCanCreateFromJson() + { + $tmp = sys_get_temp_dir() . '/test.js'; + file_put_contents($tmp, json_encode($this->arrayData)); + $b = ServiceBuilder::factory($tmp); + unlink($tmp); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testFactoryCanCreateFromArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testFactoryDoesNotRequireParams() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('missing_params'); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testBuilderAllowsReferencesBetweenClients() + { + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'other_client' => '{b}', + 'username' => 'x', + 'password' => 'y', + 'subdomain' => 'z' + ) + ), + 'b' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => '1', + 'password' => '2', + 'subdomain' => '3' + ) + ) + )); + + $client = $builder['a']; + $this->assertEquals('x', $client->getConfig('username')); + $this->assertSame($builder['b'], $client->getConfig('other_client')); + $this->assertEquals('1', $builder['b']->getConfig('username')); + } + + public function testEmitsEventsWhenClientsAreCreated() + { + // Ensure that the client signals that it emits an event + $this->assertEquals(array('service_builder.create_client'), ServiceBuilder::getAllEvents()); + + // Create a test service builder + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'test', + 'password' => '123', + 'subdomain' => 'z' + ) + ) + )); + + // Add an event listener to pick up client creation events + $emits = 0; + $builder->getEventDispatcher()->addListener('service_builder.create_client', function($event) use (&$emits) { + $emits++; + }); + + // Get the 'a' client by name + $client = $builder->get('a'); + + // Ensure that the event was emitted once, and that the client was present + $this->assertEquals(1, $emits); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); + } + + public function testCanAddGlobalParametersToServicesOnLoad() + { + $builder = ServiceBuilder::factory($this->arrayData, array( + 'username' => 'fred', + 'new_value' => 'test' + )); + + $data = json_decode($builder->serialize(), true); + + foreach ($data as $service) { + $this->assertEquals('fred', $service['params']['username']); + $this->assertEquals('test', $service['params']['new_value']); + } + } + + public function testAddsGlobalPlugins() + { + $b = new ServiceBuilder($this->arrayData); + $b->addGlobalPlugin(new HistoryPlugin()); + $s = $b->get('michael.mock'); + $this->assertTrue($s->getEventDispatcher()->hasListeners('request.sent')); + } + + public function testCanGetData() + { + $b = new ServiceBuilder($this->arrayData); + $this->assertEquals($this->arrayData['michael.mock'], $b->getData('michael.mock')); + $this->assertNull($b->getData('ewofweoweofe')); + } + + public function testCanGetByAlias() + { + $b = new ServiceBuilder($this->arrayData); + $this->assertSame($b->get('billy.mock'), $b->get('Hello!')); + } + + public function testCanOverwriteParametersForThrowawayClients() + { + $b = new ServiceBuilder($this->arrayData); + + $c1 = $b->get('michael.mock'); + $this->assertEquals('michael', $c1->getConfig('username')); + + $c2 = $b->get('michael.mock', array('username' => 'jeremy')); + $this->assertEquals('jeremy', $c2->getConfig('username')); + } + + public function testGettingAThrowawayClientWithParametersDoesNotAffectGettingOtherClients() + { + $b = new ServiceBuilder($this->arrayData); + + $c1 = $b->get('michael.mock', array('username' => 'jeremy')); + $this->assertEquals('jeremy', $c1->getConfig('username')); + + $c2 = $b->get('michael.mock'); + $this->assertEquals('michael', $c2->getConfig('username')); + } + + public function testCanUseArbitraryData() + { + $b = new ServiceBuilder(); + $b['a'] = 'foo'; + $this->assertTrue(isset($b['a'])); + $this->assertEquals('foo', $b['a']); + unset($b['a']); + $this->assertFalse(isset($b['a'])); + } + + public function testCanRegisterServiceData() + { + $b = new ServiceBuilder(); + $b['a'] = array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'billy', + 'password' => 'passw0rd', + 'subdomain' => 'billy', + ) + ); + $this->assertTrue(isset($b['a'])); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $b['a']); + $client = $b['a']; + unset($b['a']); + $this->assertFalse(isset($b['a'])); + // Ensure that instantiated clients can be registered + $b['mock'] = $client; + $this->assertSame($client, $b['mock']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php new file mode 100755 index 000000000..b8245ad58 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php @@ -0,0 +1,43 @@ +getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->once()) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load('foo')); + $this->assertEquals($data, $cache->load('foo')); + } + + public function testDoesNotCacheArrays() + { + $cache = new DoctrineCacheAdapter(new ArrayCache()); + $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->exactly(2)) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load(array())); + $this->assertEquals($data, $cache->load(array())); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php new file mode 100755 index 000000000..aee29ed8d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php @@ -0,0 +1,320 @@ +serviceTest = new ServiceDescription(array( + 'test_command' => new Operation(array( + 'doc' => 'documentationForCommand', + 'method' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'args' => array( + 'bucket' => array( + 'required' => true + ), + 'key' => array( + 'required' => true + ) + ) + )) + )); + + $this->service = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json'); + } + + public function testAllowsCustomClientParameters() + { + $client = new Mock\MockClient(null, array( + Client::COMMAND_PARAMS => array(AbstractCommand::RESPONSE_PROCESSING => 'foo') + )); + $command = $client->getCommand('mock_command'); + $this->assertEquals('foo', $command->get(AbstractCommand::RESPONSE_PROCESSING)); + } + + public function testFactoryCreatesClient() + { + $client = Client::factory(array( + 'base_url' => 'http://www.test.com/', + 'test' => '123' + )); + + $this->assertEquals('http://www.test.com/', $client->getBaseUrl()); + $this->assertEquals('123', $client->getConfig('test')); + } + + public function testFactoryDoesNotRequireBaseUrl() + { + $client = Client::factory(); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', Client::getAllEvents()); + } + + public function testExecutesCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $cmd = new MockCommand(); + $client->execute($cmd); + + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResult()); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + public function testExecutesCommandsWithArray() + { + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200), + new Response(200) + ))); + + // Create a command set and a command + $set = array(new MockCommand(), new MockCommand()); + $client->execute($set); + + // Make sure it sent + $this->assertTrue($set[0]->isExecuted()); + $this->assertTrue($set[1]->isExecuted()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidCommandIsExecuted() + { + $client = new Client(); + $client->execute(new \stdClass()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenMissingCommand() + { + $client = new Client(); + + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('test')) + ->will($this->returnValue(null)); + + $client->setCommandFactory($mock); + $client->getCommand('test'); + } + + public function testCreatesCommandsUsingCommandFactory() + { + $mockCommand = new MockCommand(); + + $client = new Mock\MockClient(); + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand)); + + $client->setCommandFactory($mock); + + $command = $client->getCommand('foo', array('acl' => '123')); + $this->assertSame($mockCommand, $command); + $command = $client->getCommand('foo', array('acl' => '123')); + $this->assertSame($mockCommand, $command); + $this->assertSame($client, $command->getClient()); + } + + public function testOwnsServiceDescription() + { + $client = new Mock\MockClient(); + $this->assertNull($client->getDescription()); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $this->assertSame($client, $client->setDescription($description)); + $this->assertSame($description, $client->getDescription()); + } + + public function testOwnsResourceIteratorFactory() + { + $client = new Mock\MockClient(); + + $method = new \ReflectionMethod($client, 'getResourceIteratorFactory'); + $method->setAccessible(TRUE); + $rf1 = $method->invoke($client); + + $rf = $this->readAttribute($client, 'resourceIteratorFactory'); + $this->assertInstanceOf('Guzzle\\Service\\Resource\\ResourceIteratorClassFactory', $rf); + $this->assertSame($rf1, $rf); + + $rf = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock'); + $client->setResourceIteratorFactory($rf); + $this->assertNotSame($rf1, $rf); + } + + public function testClientResetsRequestsBeforeExecutingCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHi", + "HTTP/1.1 200 OK\r\nContent-Length: 1\r\n\r\nI" + )); + + $client = new Mock\MockClient($this->getServer()->getUrl()); + + $command = $client->getCommand('mock_command'); + $client->execute($command); + $client->execute($command); + $this->assertEquals('I', $command->getResponse()->getBody(true)); + } + + public function testClientCreatesIterators() + { + $client = new Mock\MockClient(); + + $iterator = $client->getIterator('mock_command', array( + 'foo' => 'bar' + ), array( + 'limit' => 10 + )); + + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $this->assertEquals(10, $this->readAttribute($iterator, 'limit')); + + $command = $this->readAttribute($iterator, 'originalCommand'); + $this->assertEquals('bar', $command->get('foo')); + } + + public function testClientCreatesIteratorsWithNoOptions() + { + $client = new Mock\MockClient(); + $iterator = $client->getIterator('mock_command'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testClientCreatesIteratorsWithCommands() + { + $client = new Mock\MockClient(); + $command = new MockCommand(); + $iterator = $client->getIterator($command); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $iteratorCommand = $this->readAttribute($iterator, 'originalCommand'); + $this->assertSame($command, $iteratorCommand); + } + + public function testClientHoldsInflector() + { + $client = new Mock\MockClient(); + $this->assertInstanceOf('Guzzle\Inflection\MemoizingInflector', $client->getInflector()); + + $inflector = new Inflector(); + $client->setInflector($inflector); + $this->assertSame($inflector, $client->getInflector()); + } + + public function testClientAddsGlobalCommandOptions() + { + $client = new Mock\MockClient('http://www.foo.com', array( + Client::COMMAND_PARAMS => array( + 'mesa' => 'bar' + ) + )); + $command = $client->getCommand('mock_command'); + $this->assertEquals('bar', $command->get('mesa')); + } + + public function testSupportsServiceDescriptionBaseUrls() + { + $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); + $client = new Client(); + $client->setDescription($description); + $this->assertEquals('http://foo.com', $client->getBaseUrl()); + } + + public function testMergesDefaultCommandParamsCorrectly() + { + $client = new Mock\MockClient('http://www.foo.com', array( + Client::COMMAND_PARAMS => array( + 'mesa' => 'bar', + 'jar' => 'jar' + ) + )); + $command = $client->getCommand('mock_command', array('jar' => 'test')); + $this->assertEquals('bar', $command->get('mesa')); + $this->assertEquals('test', $command->get('jar')); + } + + /** + * @expectedException \Guzzle\Http\Exception\BadResponseException + */ + public function testWrapsSingleCommandExceptions() + { + $client = new Mock\MockClient('http://foobaz.com'); + $mock = new MockPlugin(array(new Response(401))); + $client->addSubscriber($mock); + $client->execute(new MockCommand()); + } + + public function testWrapsMultipleCommandExceptions() + { + $client = new Mock\MockClient('http://foobaz.com'); + $mock = new MockPlugin(array(new Response(200), new Response(200), new Response(404), new Response(500))); + $client->addSubscriber($mock); + + $cmds = array(new MockCommand(), new MockCommand(), new MockCommand(), new MockCommand()); + try { + $client->execute($cmds); + } catch (CommandTransferException $e) { + $this->assertEquals(2, count($e->getFailedRequests())); + $this->assertEquals(2, count($e->getSuccessfulRequests())); + $this->assertEquals(2, count($e->getFailedCommands())); + $this->assertEquals(2, count($e->getSuccessfulCommands())); + + foreach ($e->getSuccessfulCommands() as $c) { + $this->assertTrue($c->getResponse()->isSuccessful()); + } + + foreach ($e->getFailedCommands() as $c) { + $this->assertFalse($c->getRequest()->getResponse()->isSuccessful()); + } + } + } + + public function testGetCommandAfterTwoSetDescriptions() + { + $service1 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json'); + $service2 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service_3.json'); + + $client = new Mock\MockClient(); + + $client->setDescription($service1); + $client->getCommand('foo_bar'); + $client->setDescription($service2); + $client->getCommand('baz_qux'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php new file mode 100755 index 000000000..1004fae66 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php @@ -0,0 +1,16 @@ +setDescription(ServiceDescription::factory(__DIR__ . '/../../TestData/test_service.json')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php new file mode 100755 index 000000000..d76224697 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php @@ -0,0 +1,54 @@ + function($command, $api) { + $command->set('testing', '123'); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + return $request; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + $this->assertEquals('123', $c->get('testing')); + $this->assertEquals('http://www.test.com/', $c->getRequest()->getUrl()); + } + + /** + * @expectedException UnexpectedValueException + * @expectedExceptionMessage Closure command did not return a RequestInterface object + */ + public function testMustReturnRequest() + { + $c = new ClosureCommand(array( + 'closure' => function($command, $api) { + return false; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php new file mode 100755 index 000000000..b7173d486 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php @@ -0,0 +1,445 @@ +assertEquals('123', $command->get('test')); + $this->assertFalse($command->isPrepared()); + $this->assertFalse($command->isExecuted()); + } + + public function testDeterminesShortName() + { + $api = new Operation(array('name' => 'foobar')); + $command = new MockCommand(array(), $api); + $this->assertEquals('foobar', $command->getName()); + + $command = new MockCommand(); + $this->assertEquals('mock_command', $command->getName()); + + $command = new Sub(); + $this->assertEquals('sub.sub', $command->getName()); + } + + /** + * @expectedException RuntimeException + */ + public function testGetRequestThrowsExceptionBeforePreparation() + { + $command = new MockCommand(); + $command->getRequest(); + } + + public function testGetResponseExecutesCommandsWhenNeeded() + { + $response = new Response(200); + $client = $this->getClient(); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + $this->assertSame($response, $command->getResponse()); + $this->assertSame($response, $command->getResponse()); + } + + public function testGetResultExecutesCommandsWhenNeeded() + { + $response = new Response(200); + $client = $this->getClient(); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + $this->assertSame($response, $command->getResult()); + $this->assertSame($response, $command->getResult()); + } + + public function testSetClient() + { + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client); + $this->assertEquals($client, $command->getClient()); + + unset($client); + unset($command); + + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client)->prepare(); + $this->assertEquals($client, $command->getClient()); + $this->assertTrue($command->isPrepared()); + } + + public function testExecute() + { + $client = $this->getClient(); + $response = new Response(200, array( + 'Content-Type' => 'application/xml' + ), '123'); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $this->assertSame($command, $command->setClient($client)); + + // Returns the result of the command + $this->assertInstanceOf('SimpleXMLElement', $command->execute()); + + $this->assertTrue($command->isPrepared()); + $this->assertTrue($command->isExecuted()); + $this->assertSame($response, $command->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $command->getRequest()); + // Make sure that the result was automatically set to a SimpleXMLElement + $this->assertInstanceOf('SimpleXMLElement', $command->getResult()); + $this->assertEquals('123', (string) $command->getResult()->data); + } + + public function testConvertsJsonResponsesToArray() + { + $client = $this->getClient(); + $this->setMockResponse($client, array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), '{ "key": "Hi!" }' + ) + )); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + $this->assertEquals(array( + 'key' => 'Hi!' + ), $command->getResult()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testConvertsInvalidJsonResponsesToArray() + { + $json = '{ "key": "Hi!" }invalid'; + // Some implementations of php-json extension are not strict enough + // and allow to parse invalid json ignoring invalid parts + // See https://github.com/remicollet/pecl-json-c/issues/5 + if (json_decode($json) && JSON_ERROR_NONE === json_last_error()) { + $this->markTestSkipped('php-pecl-json library regression issues'); + } + + $client = $this->getClient(); + $this->setMockResponse($client, array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), $json + ) + )); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + } + + public function testProcessResponseIsNotXml() + { + $client = $this->getClient(); + $this->setMockResponse($client, array( + new Response(200, array( + 'Content-Type' => 'application/octet-stream' + ), 'abc,def,ghi') + )); + $command = new MockCommand(); + $client->execute($command); + + // Make sure that the result was not converted to XML + $this->assertFalse($command->getResult() instanceof \SimpleXMLElement); + } + + /** + * @expectedException RuntimeException + */ + public function testExecuteThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->execute(); + } + + /** + * @expectedException RuntimeException + */ + public function testPrepareThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->prepare(); + } + + public function testCommandsAllowsCustomRequestHeaders() + { + $command = new MockCommand(); + $command->getRequestHeaders()->set('test', '123'); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('123', $command->getRequestHeaders()->get('test')); + + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', (string) $command->getRequest()->getHeader('test')); + } + + public function testCommandsAllowsCustomRequestHeadersAsArray() + { + $command = new MockCommand(array(AbstractCommand::HEADERS_OPTION => array('Foo' => 'Bar'))); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('Bar', $command->getRequestHeaders()->get('Foo')); + } + + private function getOperation() + { + return new Operation(array( + 'name' => 'foobar', + 'httpMethod' => 'POST', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'parameters' => array( + 'test' => array( + 'default' => '123', + 'type' => 'string' + ) + ))); + } + + public function testCommandsUsesOperation() + { + $api = $this->getOperation(); + $command = new MockCommand(array(), $api); + $this->assertSame($api, $command->getOperation()); + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', $command->get('test')); + $this->assertSame($api, $command->getOperation($api)); + } + + public function testCloneMakesNewRequest() + { + $client = $this->getClient(); + $command = new MockCommand(array(), $this->getOperation()); + $command->setClient($client); + + $command->prepare(); + $this->assertTrue($command->isPrepared()); + + $command2 = clone $command; + $this->assertFalse($command2->isPrepared()); + } + + public function testHasOnCompleteMethod() + { + $that = $this; + $called = 0; + + $testFunction = function($command) use (&$called, $that) { + $called++; + $that->assertInstanceOf('Guzzle\Service\Command\CommandInterface', $command); + }; + + $client = $this->getClient(); + $command = new MockCommand(array( + 'command.on_complete' => $testFunction + ), $this->getOperation()); + $command->setClient($client); + + $command->prepare()->setResponse(new Response(200), true); + $command->execute(); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnCompleteMustBeCallable() + { + $client = $this->getClient(); + $command = new MockCommand(); + $command->setOnComplete('foo'); + } + + public function testCanSetResultManually() + { + $client = $this->getClient(); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200) + ))); + $command = new MockCommand(); + $client->execute($command); + $command->setResult('foo!'); + $this->assertEquals('foo!', $command->getResult()); + } + + public function testCanInitConfig() + { + $command = $this->getMockBuilder('Guzzle\\Service\\Command\\AbstractCommand') + ->setConstructorArgs(array(array( + 'foo' => 'bar' + ), new Operation(array( + 'parameters' => array( + 'baz' => new Parameter(array( + 'default' => 'baaar' + )) + ) + )))) + ->getMockForAbstractClass(); + + $this->assertEquals('bar', $command['foo']); + $this->assertEquals('baaar', $command['baz']); + } + + public function testAddsCurlOptionsToRequestsWhenPreparing() + { + $command = new MockCommand(array( + 'foo' => 'bar', + 'curl.options' => array('CURLOPT_PROXYPORT' => 8080) + )); + $client = new Client(); + $command->setClient($client); + $request = $command->prepare(); + $this->assertEquals(8080, $request->getCurlOptions()->get(CURLOPT_PROXYPORT)); + } + + public function testIsInvokable() + { + $client = $this->getClient(); + $response = new Response(200); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + // Returns the result of the command + $this->assertSame($response, $command()); + } + + public function testCreatesDefaultOperation() + { + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); + $this->assertInstanceOf('Guzzle\Service\Description\Operation', $command->getOperation()); + } + + public function testAllowsValidatorToBeInjected() + { + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); + $v = new SchemaValidator(); + $command->setValidator($v); + $this->assertSame($v, $this->readAttribute($command, 'validator')); + } + + public function testCanDisableValidation() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate')) + ->getMock(); + $v->expects($this->never())->method('validate'); + $command->setValidator($v); + $command->set(AbstractCommand::DISABLE_VALIDATION, true); + $command->prepare(); + } + + public function testValidatorDoesNotUpdateNonDefaultValues() + { + $command = new MockCommand(array('test' => 123, 'foo' => 'bar')); + $command->setClient(new \Guzzle\Service\Client()); + $command->prepare(); + $this->assertEquals(123, $command->get('test')); + $this->assertEquals('bar', $command->get('foo')); + } + + public function testValidatorUpdatesDefaultValues() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $command->prepare(); + $this->assertEquals(123, $command->get('test')); + $this->assertEquals('abc', $command->get('_internal')); + } + + /** + * @expectedException \Guzzle\Service\Exception\ValidationException + * @expectedExceptionMessage [Foo] Baz + */ + public function testValidatesCommandBeforeSending() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate', 'getErrors')) + ->getMock(); + $v->expects($this->any())->method('validate')->will($this->returnValue(false)); + $v->expects($this->any())->method('getErrors')->will($this->returnValue(array('[Foo] Baz', '[Bar] Boo'))); + $command->setValidator($v); + $command->prepare(); + } + + /** + * @expectedException \Guzzle\Service\Exception\ValidationException + * @expectedExceptionMessage Validation errors: [abc] must be of type string + */ + public function testValidatesAdditionalParameters() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array( + 'parameters' => array( + 'baz' => array('type' => 'integer') + ), + 'additionalParameters' => array( + 'type' => 'string' + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('foo', array( + 'abc' => false, + 'command.headers' => array('foo' => 'bar') + )); + $command->prepare(); + } + + public function testCanAccessValidationErrorsFromCommand() + { + $validationErrors = array('[Foo] Baz', '[Bar] Boo'); + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + + $this->assertFalse($command->getValidationErrors()); + + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate', 'getErrors')) + ->getMock(); + $v->expects($this->any())->method('getErrors')->will($this->returnValue($validationErrors)); + $command->setValidator($v); + + $this->assertEquals($validationErrors, $command->getValidationErrors()); + } + + public function testCanChangeResponseBody() + { + $body = EntityBody::factory(); + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $command->set(AbstractCommand::RESPONSE_BODY, $body); + $request = $command->prepare(); + $this->assertSame($body, $this->readAttribute($request, 'responseBody')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php new file mode 100755 index 000000000..b7a4682fc --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php @@ -0,0 +1,122 @@ +serializer = DefaultRequestSerializer::getInstance(); + $this->client = new Client('http://foo.com/baz'); + $this->operation = new Operation(array('httpMethod' => 'POST')); + $this->command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setConstructorArgs(array(array(), $this->operation)) + ->getMockForAbstractClass(); + $this->command->setClient($this->client); + } + + public function testAllowsCustomVisitor() + { + $this->serializer->addVisitor('custom', new HeaderVisitor()); + $this->command['test'] = '123'; + $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'custom'))); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('123', (string) $request->getHeader('test')); + } + + public function testUsesRelativePath() + { + $this->operation->setUri('bar'); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar', (string) $request->getUrl()); + } + + public function testUsesRelativePathWithUriLocations() + { + $this->command['test'] = '123'; + $this->operation->setUri('bar/{test}'); + $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'uri'))); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar/123', (string) $request->getUrl()); + } + + public function testAllowsCustomFactory() + { + $f = new VisitorFlyweight(); + $serializer = new DefaultRequestSerializer($f); + $this->assertSame($f, $this->readAttribute($serializer, 'factory')); + } + + public function testMixedParams() + { + $this->operation->setUri('bar{?limit,fields}'); + $this->operation->addParam(new Parameter(array( + 'name' => 'limit', + 'location' => 'uri', + 'required' => false, + ))); + $this->operation->addParam(new Parameter(array( + 'name' => 'fields', + 'location' => 'uri', + 'required' => true, + ))); + + $this->command['fields'] = array('id', 'name'); + + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar?fields='.urlencode('id,name'), (string) $request->getUrl()); + } + + public function testValidatesAdditionalParameters() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'bar' => array('location' => 'header') + ), + 'additionalParameters' => array( + 'location' => 'json' + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('foo'); + $command['bar'] = 'test'; + $command['hello'] = 'abc'; + $request = $command->prepare(); + $this->assertEquals('test', (string) $request->getHeader('bar')); + $this->assertEquals('{"hello":"abc"}', (string) $request->getBody()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php new file mode 100755 index 000000000..a6a02f951 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php @@ -0,0 +1,59 @@ +setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'), true); + $this->assertInstanceOf('SimpleXMLElement', $op->execute()); + } + + public function testParsesJsonResponses() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"Baz":"Bar"}'), true); + $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testThrowsExceptionWhenParsingJsonFails() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array('Content-Type' => 'application/json'), '{"Baz":ddw}'), true); + $op->execute(); + } + + public function testAddsContentTypeWhenExpectsIsSetOnCommand() + { + $op = new OperationCommand(array(), new Operation()); + $op['command.expects'] = 'application/json'; + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, null, '{"Baz":"Bar"}'), true); + $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php new file mode 100755 index 000000000..ab1041ad3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php @@ -0,0 +1,76 @@ +client = new Client(); + + $map = new MapFactory(array( + 'test' => 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + $this->factory = new AliasFactory($this->client, array( + 'foo' => 'test', + 'bar' => 'sub', + 'sub' => 'test1', + 'krull' => 'test3', + 'krull_2' => 'krull', + 'sub_2' => 'bar', + 'bad_link' => 'jarjar' + )); + + $map2 = new MapFactory(array( + 'test3' => 'Guzzle\Tests\Service\Mock\Command\Sub\Sub' + )); + + $this->client->setCommandFactory(new CompositeFactory(array($map, $this->factory, $map2))); + } + + public function aliasProvider() + { + return array( + array('foo', 'Guzzle\Tests\Service\Mock\Command\MockCommand', false), + array('bar', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub_2', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('krull', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('krull_2', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('missing', null, true), + array('bad_link', null, true) + ); + } + + /** + * @dataProvider aliasProvider + */ + public function testAliasesCommands($key, $result, $exception) + { + try { + $command = $this->client->getCommand($key); + if (is_null($result)) { + $this->assertNull($command); + } else { + $this->assertInstanceof($result, $command); + } + } catch (\Exception $e) { + if (!$exception) { + $this->fail('Got exception when it was not expected'); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php new file mode 100755 index 000000000..b896dcfd6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php @@ -0,0 +1,124 @@ +getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testIsIterable() + { + $factory = new CompositeFactory(array($this->getFactory(), $this->getFactory())); + $this->assertEquals(2, count($factory)); + $this->assertEquals(2, count(iterator_to_array($factory->getIterator()))); + } + + public function testFindsFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factory = new CompositeFactory(array($f1, $f2)); + $this->assertNull($factory->find('foo')); + $this->assertNull($factory->find($this->getFactory())); + $this->assertSame($f1, $factory->find('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertSame($f2, $factory->find('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + $this->assertSame($f1, $factory->find($f1)); + $this->assertSame($f2, $factory->find($f2)); + + $this->assertFalse($factory->has('foo')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + } + + public function testCreatesCommands() + { + $factory = new CompositeFactory(); + $this->assertNull($factory->factory('foo')); + + $f1 = $this->getFactory(); + $mockCommand1 = $this->getMockForAbstractClass('Guzzle\\Service\\Command\\AbstractCommand'); + + $f1->expects($this->once()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand1)); + + $factory = new CompositeFactory(array($f1)); + $this->assertSame($mockCommand1, $factory->factory('foo')); + } + + public function testAllowsRemovalOfFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factories = array($f1, $f2, $f3); + $factory = new CompositeFactory($factories); + + $factory->remove('foo'); + $this->assertEquals($factories, $factory->getIterator()->getArrayCopy()); + + $factory->remove($f1); + $this->assertEquals(array($f2, $f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\MapFactory'); + $this->assertEquals(array($f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + + $factory->remove('foo'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + } + + public function testAddsFactoriesBeforeAndAtEnd() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $f4 = $this->getFactory(); + + $factory = new CompositeFactory(); + + $factory->add($f1); + $this->assertEquals(array($f1), $factory->getIterator()->getArrayCopy()); + + $factory->add($f2); + $this->assertEquals(array($f1, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f3, $f2); + $this->assertEquals(array($f1, $f3, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f4, 'Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array($f1, $f4, $f3, $f2), $factory->getIterator()->getArrayCopy()); + } + + public function testProvidesDefaultChainForClients() + { + $client = $this->getMock('Guzzle\\Service\\Client'); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(1, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[0]); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $client->expects($this->once()) + ->method('getDescription') + ->will($this->returnValue($description)); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(2, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory', $a[0]); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[1]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php new file mode 100755 index 000000000..766471822 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php @@ -0,0 +1,49 @@ + $prefix + )); + } + + $factory = new ConcreteClassFactory($client); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php new file mode 100755 index 000000000..ee720d1e7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php @@ -0,0 +1,37 @@ + 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php new file mode 100755 index 000000000..337263481 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php @@ -0,0 +1,68 @@ +getDescription(); + + $factory = new ServiceDescriptionFactory($d); + $this->assertSame($d, $factory->getServiceDescription()); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } + + public function testUsesUcFirstIfNoExactMatch() + { + $d = $this->getDescription(); + $factory = new ServiceDescriptionFactory($d, new Inflector()); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Test')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('test')); + } + + public function testUsesInflectionIfNoExactMatch() + { + $d = $this->getDescription(); + $factory = new ServiceDescriptionFactory($d, new Inflector()); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('JarJar')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('jar_jar')); + } + + protected function getDescription() + { + return ServiceDescription::factory(array( + 'operations' => array( + 'jar_jar' => array('class' => 'Guzzle\Tests\Service\Mock\Command\MockCommand'), + 'binks' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand'), + 'Test' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand') + ) + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php new file mode 100755 index 000000000..46b472eb5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php @@ -0,0 +1,110 @@ +command = new MockCommand(); + $this->request = new EntityEnclosingRequest('POST', 'http://www.test.com/some/path.php'); + $this->validator = new SchemaValidator(); + } + + protected function getCommand($location) + { + $command = new OperationCommand(array(), $this->getNestedCommand($location)); + $command->setClient(new MockClient()); + + return $command; + } + + protected function getNestedCommand($location) + { + return new Operation(array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => new Parameter(array( + 'type' => 'object', + 'location' => $location, + 'sentAs' => 'Foo', + 'required' => true, + 'properties' => array( + 'test' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'baz' => array( + 'type' => 'boolean', + 'default' => true + ), + 'jenga' => array( + 'type' => 'string', + 'default' => 'hello', + 'sentAs' => 'Jenga_Yall!', + 'filters' => array('strtoupper') + ) + ) + ), + 'bar' => array('default' => 123) + ), + 'additionalProperties' => array( + 'type' => 'string', + 'filters' => array('strtoupper'), + 'location' => $location + ) + )), + 'arr' => new Parameter(array( + 'type' => 'array', + 'location' => $location, + 'items' => array( + 'type' => 'string', + 'filters' => array('strtoupper') + ) + )), + ) + )); + } + + protected function getCommandWithArrayParamAndFilters() + { + $operation = new Operation(array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => new Parameter(array( + 'type' => 'string', + 'location' => 'query', + 'sentAs' => 'Foo', + 'required' => true, + 'default' => 'bar', + 'filters' => array('strtoupper') + )), + 'arr' => new Parameter(array( + 'type' => 'array', + 'location' => 'query', + 'sentAs' => 'Arr', + 'required' => true, + 'default' => array(123, 456, 789), + 'filters' => array(array('method' => 'implode', 'args' => array(',', '@value'))) + )) + ) + )); + $command = new OperationCommand(array(), $operation); + $command->setClient(new MockClient()); + + return $command; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php new file mode 100755 index 000000000..2a95c452a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php @@ -0,0 +1,63 @@ +getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getBody()); + $this->assertNull($this->request->getHeader('Expect')); + } + + public function testAddsExpectHeaderWhenSetToTrue() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $param->setData('expect_header', true); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getBody()); + } + + public function testCanDisableExpectHeader() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $param->setData('expect_header', false); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertNull($this->request->getHeader('Expect')); + } + + public function testCanSetExpectHeaderBasedOnSize() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + // The body is less than the cutoff + $param->setData('expect_header', 5); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertNull($this->request->getHeader('Expect')); + // Now check when the body is greater than the cutoff + $param->setData('expect_header', 2); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('100-Continue', (string) $this->request->getHeader('Expect')); + } + + public function testAddsContentEncodingWhenSetOnBody() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $body = EntityBody::factory('foo'); + $body->compress(); + $visitor->visit($this->command, $this->request, $param, $body); + $this->assertEquals('gzip', (string) $this->request->getHeader('Content-Encoding')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php new file mode 100755 index 000000000..7ea1ae913 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php @@ -0,0 +1,48 @@ +getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setAdditionalProperties(new Parameter(array())); + $visitor->visit($this->command, $this->request, $param, 'test'); + } + + public function testVisitsLocation() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setAdditionalProperties(false); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getHeader('test')); + } + + public function testVisitsMappedPrefixHeaders() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setSentAs('x-foo-'); + $param->setAdditionalProperties(new Parameter(array( + 'type' => 'string' + ))); + $visitor->visit($this->command, $this->request, $param, array( + 'bar' => 'test', + 'baz' => '123' + )); + $this->assertEquals('test', (string) $this->request->getHeader('x-foo-bar')); + $this->assertEquals('123', (string) $this->request->getHeader('x-foo-baz')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php new file mode 100755 index 000000000..ea6782f53 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php @@ -0,0 +1,60 @@ +after($this->command, $this->request); + + $param = $this->getNestedCommand('json')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test2'), 'abc'); + $visitor->after($this->command, $this->request); + $this->assertEquals('{"test":"123","test2":"abc"}', (string) $this->request->getBody()); + } + + public function testAddsJsonHeader() + { + $visitor = new Visitor(); + $visitor->setContentTypeHeader('application/json-foo'); + $param = $this->getNestedCommand('json')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $visitor->after($this->command, $this->request); + $this->assertEquals('application/json-foo', (string) $this->request->getHeader('Content-Type')); + } + + public function testRecursivelyBuildsJsonBodies() + { + $command = $this->getCommand('json'); + $request = $command->prepare(); + $this->assertEquals('{"Foo":{"test":{"baz":true,"Jenga_Yall!":"HELLO"},"bar":123}}', (string) $request->getBody()); + } + + public function testAppliesFiltersToAdditionalProperties() + { + $command = $this->getCommand('json'); + $command->set('foo', array('not_set' => 'abc')); + $request = $command->prepare(); + $result = json_decode($request->getBody(), true); + $this->assertEquals('ABC', $result['Foo']['not_set']); + } + + public function testAppliesFiltersToArrayItemValues() + { + $command = $this->getCommand('json'); + $command->set('arr', array('a', 'b')); + $request = $command->prepare(); + $result = json_decode($request->getBody(), true); + $this->assertEquals(array('A', 'B'), $result['arr']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php new file mode 100755 index 000000000..540b41087 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php @@ -0,0 +1,33 @@ +getNestedCommand('postField')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $this->assertEquals('123', (string) $this->request->getPostField('test')); + } + + public function testRecursivelyBuildsPostFields() + { + $command = $this->getCommand('postField'); + $request = $command->prepare(); + $visitor = new Visitor(); + $param = $command->getOperation()->getParam('foo'); + $visitor->visit($command, $request, $param, $command['foo']); + $visitor->after($command, $request); + $this->assertEquals( + 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', + rawurldecode((string) $request->getPostFields()) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php new file mode 100755 index 000000000..21e3cec33 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php @@ -0,0 +1,54 @@ +getNestedCommand('postFile')->getParam('foo'); + + // Test using a path to a file + $visitor->visit($this->command, $this->request, $param->setSentAs('test_3'), __FILE__); + $this->assertInternalType('array', $this->request->getPostFile('test_3')); + + // Test with a PostFile + $visitor->visit($this->command, $this->request, $param->setSentAs(null), new PostFile('baz', __FILE__)); + $this->assertInternalType('array', $this->request->getPostFile('baz')); + } + + public function testVisitsLocationWithMultipleFiles() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'DoPost' => array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => array( + 'location' => 'postFile', + 'type' => array('string', 'array') + ) + ) + ) + ) + )); + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length:0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $client->setDescription($description); + $command = $client->getCommand('DoPost', array('foo' => array(__FILE__, __FILE__))); + $command->execute(); + $received = $this->getServer()->getReceivedRequests(); + $this->assertContains('name="foo[0]";', $received[0]); + $this->assertContains('name="foo[1]";', $received[0]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php new file mode 100755 index 000000000..607af7603 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php @@ -0,0 +1,48 @@ +getNestedCommand('query')->getParam('foo')->setSentAs('test'); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', $this->request->getQuery()->get('test')); + } + + /** + * @covers Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor + * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively + */ + public function testRecursivelyBuildsQueryStrings() + { + $command = $this->getCommand('query'); + $command->getOperation()->getParam('foo')->setSentAs('Foo'); + $request = $command->prepare(); + $this->assertEquals( + 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', + rawurldecode($request->getQuery()) + ); + } + + /** + * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively + */ + public function testFiltersAreAppliedToArrayParamType() + { + $command = $this->getCommandWithArrayParamAndFilters(); + $request = $command->prepare(); + $query = $request->getQuery(); + // param type 'string' + $this->assertEquals('BAR', $query->get('Foo')); + // param type 'array' + $this->assertEquals('123,456,789', $query->get('Arr')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php new file mode 100755 index 000000000..ff8cec599 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php @@ -0,0 +1,20 @@ +getNestedCommand('response_body')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param, sys_get_temp_dir() . '/foo.txt'); + $body = $this->readAttribute($this->request, 'responseBody'); + $this->assertContains('/foo.txt', $body->getUri()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php new file mode 100755 index 000000000..beb58b00a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php @@ -0,0 +1,558 @@ + array( + 'xmlRoot' => array( + 'name' => 'test', + 'namespaces' => 'http://foo.com' + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array('location' => 'xml', 'type' => 'string') + ) + ), + array('Foo' => 'test', 'Baz' => 'bar'), + 'testbar' + ), + // Ensure that the content-type is not added + array(array('parameters' => array('Foo' => array('location' => 'xml', 'type' => 'string'))), array(), ''), + // Test with adding attributes and no namespace + array( + array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'test' + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string', 'data' => array('xmlAttribute' => true)) + ) + ), + array('Foo' => 'test', 'Baz' => 'bar'), + '' + ), + // Test adding with an array + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'numeric', + 'sentAs' => 'Bar' + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array(1, 2)), + 'test12' + ), + // Test adding an object + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), + 'testabcfoo' + ), + // Add an array that contains an object + array( + array( + 'parameters' => array( + 'Baz' => array( + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Bar', + 'properties' => array('A' => array(), 'B' => array()) + ) + ) + ) + ), + array('Baz' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + )), + '1234' + ), + // Add an object of attributes + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), + 'testfoo' + ), + // Check order doesn't matter + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bam' => 'foo', 'Bar' => 'abc')), + 'testfoo' + ), + // Add values with custom namespaces + array( + array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'data' => array( + 'xmlNamespace' => 'http://foo.com' + ) + ) + ) + ), + array('Foo' => 'test'), + 'test' + ), + // Add attributes with custom namespace prefix + array( + array( + 'parameters' => array( + 'Wrap' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Foo' => array( + 'type' => 'string', + 'sentAs' => 'xsi:baz', + 'data' => array( + 'xmlNamespace' => 'http://foo.com', + 'xmlAttribute' => true + ) + ) + ) + ), + ) + ), + array('Wrap' => array( + 'Foo' => 'test' + )), + '' + ), + // Add nodes with custom namespace prefix + array( + array( + 'parameters' => array( + 'Wrap' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Foo' => array( + 'type' => 'string', + 'sentAs' => 'xsi:Foo', + 'data' => array( + 'xmlNamespace' => 'http://foobar.com' + ) + ) + ) + ), + ) + ), + array('Wrap' => array( + 'Foo' => 'test' + )), + 'test' + ), + array( + array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'data' => array( + 'xmlNamespace' => 'http://foo.com' + ) + ) + ) + ), + array('Foo' => '

    This is a title

    '), + 'This is a title]]>' + ), + // Flat array at top level + array( + array( + 'parameters' => array( + 'Bars' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Bar', + 'properties' => array( + 'A' => array(), + 'B' => array() + ) + ) + ), + 'Boos' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'location' => 'xml', + 'items' => array( + 'sentAs' => 'Boo', + 'type' => 'string' + ) + ) + ) + ), + array( + 'Bars' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + ), + 'Boos' => array('test', '123') + ), + '1234test123' + ), + // Nested flat arrays + array( + array( + 'parameters' => array( + 'Delete' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Items' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Item', + 'properties' => array( + 'A' => array(), + 'B' => array() + ) + ) + ) + ) + ) + ) + ), + array( + 'Delete' => array( + 'Items' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + ) + ) + ), + '1234' + ) + ); + } + + /** + * @dataProvider xmlProvider + */ + public function testSerializesXml(array $operation, array $input, $xml) + { + $operation = new Operation($operation); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array($input, $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client('http://www.test.com/some/path.php')); + $request = $command->prepare(); + if (!empty($input)) { + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + } else { + $this->assertNull($request->getHeader('Content-Type')); + } + $body = str_replace(array("\n", ""), '', (string) $request->getBody()); + $this->assertEquals($xml, $body); + } + + public function testAddsContentTypeAndTopLevelValues() + { + $operation = new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'test', + 'namespaces' => array( + 'xsi' => 'http://foo.com' + ) + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array('location' => 'xml', 'type' => 'string') + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test', + 'Baz' => 'bar' + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + $this->assertEquals( + '' . "\n" + . 'testbar' . "\n", + (string) $request->getBody() + ); + } + + public function testCanChangeContentType() + { + $visitor = new XmlVisitor(); + $visitor->setContentTypeHeader('application/foo'); + $this->assertEquals('application/foo', $this->readAttribute($visitor, 'contentType')); + } + + public function testCanAddArrayOfSimpleTypes() + { + $request = new EntityEnclosingRequest('POST', 'http://foo.com'); + $visitor = new XmlVisitor(); + $param = new Parameter(array( + 'type' => 'object', + 'location' => 'xml', + 'name' => 'Out', + 'properties' => array( + 'Nodes' => array( + 'required' => true, + 'type' => 'array', + 'min' => 1, + 'items' => array('type' => 'string', 'sentAs' => 'Node') + ) + ) + )); + + $param->setParent(new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'Test', + 'namespaces' => array( + 'https://foo/' + ) + ) + ) + ))); + + $value = array('Nodes' => array('foo', 'baz')); + $this->assertTrue($this->validator->validate($param, $value)); + $visitor->visit($this->command, $request, $param, $value); + $visitor->after($this->command, $request); + + $this->assertEquals( + "\n" + . "foobaz\n", + (string) $request->getBody() + ); + } + + public function testCanAddMultipleNamespacesToRoot() + { + $operation = new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'Hi', + 'namespaces' => array( + 'xsi' => 'http://foo.com', + 'foo' => 'http://foobar.com' + ) + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string') + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test' + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testValuesAreFiltered() + { + $operation = new Operation(array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'filters' => array('strtoupper') + ), + 'Bar' => array( + 'location' => 'xml', + 'type' => 'object', + 'properties' => array( + 'Baz' => array( + 'filters' => array('strtoupper') + ) + ) + ) + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test', + 'Bar' => array( + 'Baz' => 'abc' + ) + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'TESTABC' . "\n", + (string) $request->getBody() + ); + } + + public function testSkipsNullValues() + { + $operation = new Operation(array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string' + ), + 'Bar' => array( + 'location' => 'xml', + 'type' => 'object', + 'properties' => array( + 'Baz' => array(), + 'Bam' => array(), + ) + ), + 'Arr' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string' + ) + ) + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => null, + 'Bar' => array( + 'Bar' => null, + 'Bam' => 'test' + ), + 'Arr' => array(null) + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testAllowsXmlEncoding() + { + $operation = new Operation(array( + 'data' => array( + 'xmlEncoding' => 'UTF-8' + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml') + ) + )); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array('Foo' => 'test'), $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testAllowsSendingXmlPayloadIfNoXmlParamsWereSet() + { + $operation = new Operation(array( + 'httpMethod' => 'POST', + 'data' => array('xmlAllowEmpty' => true), + 'parameters' => array('Foo' => array('location' => 'xml')) + )); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array(), $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client('http://foo.com')); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . '' . "\n", + (string) $request->getBody() + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php new file mode 100755 index 000000000..7b8600342 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php @@ -0,0 +1,29 @@ +value = array(); + $this->command = new MockCommand(); + $this->response = new Response(200, array( + 'X-Foo' => 'bar', + 'Content-Length' => 3, + 'Content-Type' => 'text/plain' + ), 'Foo'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php new file mode 100755 index 000000000..932e39bff --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php @@ -0,0 +1,21 @@ + 'body', 'name' => 'foo')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('Foo', (string) $this->value['foo']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php new file mode 100755 index 000000000..db54b1abb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php @@ -0,0 +1,98 @@ + 'header', + 'name' => 'ContentType', + 'sentAs' => 'Content-Type' + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['ContentType']); + } + + public function testVisitsLocationWithFilters() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Content-Type', + 'filters' => array('strtoupper') + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('TEXT/PLAIN', $this->value['Content-Type']); + } + + public function testVisitsMappedPrefixHeaders() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Metadata', + 'sentAs' => 'X-Baz-', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'string' + ) + )); + $response = new Response(200, array( + 'X-Baz-Test' => 'ABC', + 'X-Baz-Bar' => array('123', '456'), + 'Content-Length' => 3 + ), 'Foo'); + $visitor->visit($this->command, $response, $param, $this->value); + $this->assertEquals(array( + 'Metadata' => array( + 'Test' => 'ABC', + 'Bar' => array('123', '456') + ) + ), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownHeaders() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Content-Type', + 'additionalParameters' => false + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['Content-Type']); + $this->assertArrayNotHasKey('X-Foo', $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'ContentType', + 'sentAs' => 'Content-Type', + 'additionalParameters' => false + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['ContentType']); + $this->assertArrayNotHasKey('X-Foo', $this->value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php new file mode 100755 index 000000000..4f8d30b1e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php @@ -0,0 +1,157 @@ +getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, '{"foo":"bar"}'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('foo' => 'bar'), $result); + } + + public function testVisitsLocation() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'array', + 'items' => array( + 'filters' => 'strtoupper', + 'type' => 'string' + ) + )); + $this->value = array('foo' => array('a', 'b', 'c')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('A', 'B', 'C'), $this->value['foo']); + } + + public function testRenamesTopLevelValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'sentAs' => 'Baz', + 'type' => 'string', + )); + $this->value = array('Baz' => 'test'); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => 'test'), $this->value); + } + + public function testRenamesDoesNotFailForNonExistentKey() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('unknown' => 'Unknown')), $this->value); + } + + public function testTraversesObjectsAndAppliesFilters() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'foo' => array('filters' => 'strtoupper'), + 'bar' => array('filters' => 'strtolower') + ) + )); + $this->value = array('foo' => array('foo' => 'hello', 'bar' => 'THERE')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => 'HELLO', 'bar' => 'there'), $this->value['foo']); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'name' => 'bar', + ), + ), + )); + $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + public function testWalksAdditionalProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'object', + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'filters' => array('base64_decode') + ) + ), + ), + )); + $this->value = array('foo' => array('baz' => array('bar' => 'Zm9v'))); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('foo', $this->value['foo']['baz']['bar']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php new file mode 100755 index 000000000..23cd40fed --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php @@ -0,0 +1,21 @@ + 'reasonPhrase', 'name' => 'phrase')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('OK', $this->value['phrase']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php new file mode 100755 index 000000000..7211a5801 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php @@ -0,0 +1,21 @@ + 'statusCode', 'name' => 'code')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(200, $this->value['code']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php new file mode 100755 index 000000000..f87cec7cd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php @@ -0,0 +1,431 @@ +getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Bar' => 'test'), $result); + } + + public function testBeforeMethodParsesXmlWithNamespace() + { + $this->markTestSkipped("Response/XmlVisitor cannot accept 'xmlns' in response, see #368 (http://git.io/USa1mA)."); + + $visitor = new Visitor(); + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Bar' => 'test'), $result); + } + + public function testBeforeMethodParsesNestedXml() + { + $visitor = new Visitor(); + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Items' => array('Bar' => 'test')), $result); + } + + public function testCanExtractAndRenameTopLevelXmlValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'sentAs' => 'Bar' + )); + $value = array('Bar' => 'test'); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertArrayHasKey('foo', $value); + $this->assertEquals('test', $value['foo']); + } + + public function testEnsuresRepeatedArraysAreInCorrectLocations() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'sentAs' => 'Foo', + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Baz' => array('type' => 'string'), + 'Bam' => array('type' => 'string') + ) + ) + )); + + $xml = new \SimpleXMLElement('12'); + $value = json_decode(json_encode($xml), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array( + 'foo' => array( + array ( + 'Bar' => '1', + 'Baz' => '2' + ) + ) + ), $value); + } + + public function testEnsuresFlatArraysAreFlat() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'type' => 'array', + 'items' => array('type' => 'string') + )); + + $value = array('foo' => array('bar', 'baz')); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar', 'baz')), $value); + + $value = array('foo' => 'bar'); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar')), $value); + } + + public function xmlDataProvider() + { + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'Items', + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'name' => 'Item', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Baz' => array('type' => 'string') + ) + ) + )); + + return array( + array($param, '12', array( + 'Items' => array( + array('Bar' => 1), + array('Bar' => 2) + ) + )), + array($param, '1', array( + 'Items' => array( + array('Bar' => 1) + ) + )), + array($param, '', array( + 'Items' => array() + )) + ); + } + + /** + * @dataProvider xmlDataProvider + */ + public function testEnsuresWrappedArraysAreInCorrectLocations($param, $xml, $result) + { + $visitor = new Visitor(); + $xml = new \SimpleXMLElement($xml); + $value = json_decode(json_encode($xml), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals($result, $value); + } + + public function testCanRenameValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'TerminatingInstances', + 'type' => 'array', + 'location' => 'xml', + 'sentAs' => 'instancesSet', + 'items' => array( + 'name' => 'item', + 'type' => 'object', + 'sentAs' => 'item', + 'properties' => array( + 'InstanceId' => array( + 'type' => 'string', + 'sentAs' => 'instanceId', + ), + 'CurrentState' => array( + 'type' => 'object', + 'sentAs' => 'currentState', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + ), + 'Name' => array( + 'type' => 'string', + 'sentAs' => 'name', + ), + ), + ), + 'PreviousState' => array( + 'type' => 'object', + 'sentAs' => 'previousState', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + ), + 'Name' => array( + 'type' => 'string', + 'sentAs' => 'name', + ), + ), + ), + ), + ) + )); + + $value = array( + 'instancesSet' => array ( + 'item' => array ( + 'instanceId' => 'i-3ea74257', + 'currentState' => array( + 'code' => '32', + 'name' => 'shutting-down', + ), + 'previousState' => array( + 'code' => '16', + 'name' => 'running', + ), + ), + ) + ); + + $visitor->visit($this->command, $this->response, $param, $value); + + $this->assertEquals(array( + 'TerminatingInstances' => array( + array( + 'InstanceId' => 'i-3ea74257', + 'CurrentState' => array( + 'Code' => '32', + 'Name' => 'shutting-down', + ), + 'PreviousState' => array( + 'Code' => '16', + 'Name' => 'running', + ) + ) + ) + ), $value); + } + + public function testCanRenameAttributes() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'RunningQueues', + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'item', + 'properties' => array( + 'QueueId' => array( + 'type' => 'string', + 'sentAs' => 'queue_id', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'CurrentState' => array( + 'type' => 'object', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'Name' => array( + 'sentAs' => 'name', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + ), + ), + 'PreviousState' => array( + 'type' => 'object', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'Name' => array( + 'sentAs' => 'name', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + ), + ), + ), + ) + )); + + $xml = ''; + $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); + $visitor->visit($this->command, $this->response, $param, $value); + + $this->assertEquals(array( + 'RunningQueues' => array( + array( + 'QueueId' => 'q-3ea74257', + 'CurrentState' => array( + 'Code' => '32', + 'Name' => 'processing', + ), + 'PreviousState' => array( + 'Code' => '16', + 'Name' => 'wait', + ), + ), + ) + ), $value); + } + + public function testAddsEmptyArraysWhenValueIsMissing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'Foo', + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'array'), + 'Bar' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'array'), + ) + ) + ) + ) + )); + + $value = array(); + $visitor->visit($this->command, $this->response, $param, $value); + + $value = array( + 'Foo' => array( + 'Bar' => array() + ) + ); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array( + 'Foo' => array( + array( + 'Bar' => array() + ) + ) + ), $value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'name' => 'bar', + ), + ), + )); + $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + public function testProperlyHandlesEmptyStringValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array('type' => 'string') + ), + )); + $xml = ''; + $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar' => '')), $value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php new file mode 100755 index 000000000..a252ffe60 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php @@ -0,0 +1,53 @@ +assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', $f->getRequestVisitor('json')); + $this->assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', $f->getResponseVisitor('json')); + } + + public function testCanUseCustomMappings() + { + $f = new VisitorFlyweight(array()); + $this->assertEquals(array(), $this->readAttribute($f, 'mappings')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage No request visitor has been mapped for foo + */ + public function testThrowsExceptionWhenRetrievingUnknownVisitor() + { + VisitorFlyweight::getInstance()->getRequestVisitor('foo'); + } + + public function testCachesVisitors() + { + $f = new VisitorFlyweight(); + $v1 = $f->getRequestVisitor('json'); + $this->assertSame($v1, $f->getRequestVisitor('json')); + } + + public function testAllowsAddingVisitors() + { + $f = new VisitorFlyweight(); + $j1 = new JsonRequestVisitor(); + $j2 = new JsonResponseVisitor(); + $f->addRequestVisitor('json', $j1); + $f->addResponseVisitor('json', $j2); + $this->assertSame($j1, $f->getRequestVisitor('json')); + $this->assertSame($j2, $f->getResponseVisitor('json')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php new file mode 100755 index 000000000..95fb533af --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php @@ -0,0 +1,102 @@ +getRequestSerializer(); + $b = new DefaultRequestSerializer(VisitorFlyweight::getInstance()); + $operation->setRequestSerializer($b); + $this->assertNotSame($a, $operation->getRequestSerializer()); + } + + public function testPreparesRequestUsingSerializer() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $s = $this->getMockBuilder('Guzzle\Service\Command\RequestSerializerInterface') + ->setMethods(array('prepare')) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('prepare') + ->will($this->returnValue(new EntityEnclosingRequest('POST', 'http://foo.com'))); + $op->setRequestSerializer($s); + $op->prepare(); + } + + public function testParsesResponsesWithResponseParser() + { + $op = new OperationCommand(array(), new Operation()); + $p = $this->getMockBuilder('Guzzle\Service\Command\ResponseParserInterface') + ->setMethods(array('parse')) + ->getMockForAbstractClass(); + $p->expects($this->once()) + ->method('parse') + ->will($this->returnValue(array('foo' => 'bar'))); + $op->setResponseParser($p); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200), true); + $this->assertEquals(array('foo' => 'bar'), $op->execute()); + } + + public function testParsesResponsesUsingModelParserWhenMatchingModelIsFound() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array('responseClass' => 'bar', 'responseType' => 'model') + ), + 'models' => array( + 'bar' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'string', 'location' => 'xml') + ) + ) + ) + )); + + $op = new OperationCommand(array(), $description->getOperation('foo')); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'), true); + $result = $op->execute(); + $this->assertEquals(new Model(array('Baz' => 'Bar')), $result); + } + + public function testAllowsRawResponses() + { + $description = new ServiceDescription(array( + 'operations' => array('foo' => array('responseClass' => 'bar', 'responseType' => 'model')), + 'models' => array('bar' => array()) + )); + $op = new OperationCommand(array( + OperationCommand::RESPONSE_PROCESSING => OperationCommand::TYPE_RAW + ), $description->getOperation('foo')); + $op->setClient(new Client()); + $request = $op->prepare(); + $response = new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'); + $request->setResponse($response, true); + $this->assertSame($response, $op->execute()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php new file mode 100755 index 000000000..69ba1fcea --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php @@ -0,0 +1,335 @@ +addVisitor('foo', $visitor); + $this->assertSame($visitor, $this->readAttribute($p, 'factory')->getResponseVisitor('foo')); + } + + public function testUsesParentParser() + { + $p = new OperationResponseParser(new VisitorFlyweight()); + $operation = new Operation(); + $operation->setServiceDescription(new ServiceDescription()); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($p)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/xml'), 'C'), true); + $this->assertInstanceOf('SimpleXMLElement', $op->execute()); + } + + public function testVisitsLocations() + { + $parser = new OperationResponseParser(new VisitorFlyweight(array())); + $parser->addVisitor('statusCode', new StatusCodeVisitor()); + $parser->addVisitor('reasonPhrase', new ReasonPhraseVisitor()); + $parser->addVisitor('json', new JsonVisitor()); + $op = new OperationCommand(array(), $this->getDescription()->getOperation('test')); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(201), true); + $result = $op->execute(); + $this->assertEquals(201, $result['code']); + $this->assertEquals('Created', $result['phrase']); + } + + public function testVisitsLocationsForJsonResponse() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertEquals(array( + 'baz' => 'bar', + 'enigma' => '123', + 'code' => 200, + 'phrase' => 'OK' + ), $result->toArray()); + } + + public function testSkipsUnkownModels() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $operation->setResponseClass('Baz')->setResponseType('model'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(201), true); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $op->execute()); + } + + public function testAllowsModelProcessingToBeDisabled() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $op = new OperationCommand(array('command.response_processing' => 'native'), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertInstanceOf('Guzzle\Service\Resource\Model', $result); + $this->assertEquals(array( + 'baz' => 'bar', + 'enigma' => '123' + ), $result->toArray()); + } + + public function testCanInjectModelSchemaIntoModels() + { + $parser = new OperationResponseParser(VisitorFlyweight::getInstance(), true); + $desc = $this->getDescription(); + $operation = $desc->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertSame($result->getStructure(), $desc->getModel('Foo')); + } + + public function testDoesNotParseXmlWhenNotUsingXmlVisitor() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array('baz' => array('location' => 'body')) + ) + ) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $brokenXml = '<><><>>>>'; + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), $brokenXml), true); + $result = $op->execute(); + $this->assertEquals(array('baz'), $result->getKeys()); + $this->assertEquals($brokenXml, (string) $result['baz']); + } + + public function testVisitsAdditionalProperties() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array( + 'code' => array('location' => 'statusCode') + ), + 'additionalProperties' => array( + 'location' => 'json', + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'string', + 'filters' => 'strtoupper' + ) + ) + ) + ) + ) + )); + + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '[{"a":"test"},{"a":"baz"}]'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array( + 'code' => 200, + array('a' => 'TEST'), + array('a' => 'BAZ') + ), $result); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testAdditionalPropertiesDisabledDiscardsData() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'name' => array( + 'location' => 'json', + 'type' => 'string', + ), + 'nested' => array( + 'location' => 'json', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'width' => array( + 'type' => 'integer' + ) + ), + ), + 'code' => array('location' => 'statusCode') + ), + + ) + ) + )); + + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '{"name":"test", "volume":2.0, "nested":{"width":10,"bogus":1}}'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array( + 'name' => 'test', + 'nested' => array( + 'width' => 10, + ), + 'code' => 200 + ), $result); + } + + public function testCreatesCustomResponseClassInterface() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Guzzle\Tests\Mock\CustomResponseModel')) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $result = $op->execute(); + $this->assertInstanceOf('Guzzle\Tests\Mock\CustomResponseModel', $result); + $this->assertSame($op, $result->command); + } + + /** + * @expectedException \Guzzle\Service\Exception\ResponseClassException + * @expectedExceptionMessage must exist + */ + public function testEnsuresResponseClassExists() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo\Baz\Bar')) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $op->execute(); + } + + /** + * @expectedException \Guzzle\Service\Exception\ResponseClassException + * @expectedExceptionMessage and implement + */ + public function testEnsuresResponseClassImplementsResponseClassInterface() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => __CLASS__)) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $op->execute(); + } + + protected function getDescription() + { + return ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array( + 'baz' => array('type' => 'string', 'location' => 'json'), + 'code' => array('location' => 'statusCode'), + 'phrase' => array('location' => 'reasonPhrase'), + ) + ) + ) + )); + } + + public function testCanAddListenerToParseDomainObjects() + { + $client = new Client(); + $client->setDescription(ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'FooBazBar')) + ))); + $foo = new \stdClass(); + $client->getEventDispatcher()->addListener('command.parse_response', function ($e) use ($foo) { + $e['result'] = $foo; + }); + $command = $client->getCommand('test'); + $command->prepare()->setResponse(new Response(200), true); + $result = $command->execute(); + $this->assertSame($result, $foo); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/501 + */ + public function testAdditionalPropertiesWithRefAreResolved() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Baz' => array('type' => 'string'), + 'Foo' => array( + 'type' => 'object', + 'additionalProperties' => array('$ref' => 'Baz', 'location' => 'json') + ) + ) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '{"a":"a","b":"b","c":"c"}'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array('a' => 'a', 'b' => 'b', 'c' => 'c'), $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php new file mode 100755 index 000000000..ae33b6925 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php @@ -0,0 +1,308 @@ + 'test', + 'summary' => 'doc', + 'notes' => 'notes', + 'documentationUrl' => 'http://www.example.com', + 'httpMethod' => 'POST', + 'uri' => '/api/v1', + 'responseClass' => 'array', + 'responseNotes' => 'returns the json_decoded response', + 'deprecated' => true, + 'parameters' => array( + 'key' => array( + 'required' => true, + 'type' => 'string', + 'maxLength' => 10 + ), + 'key_2' => array( + 'required' => true, + 'type' => 'integer', + 'default' => 10 + ) + ) + )); + + $this->assertEquals('test', $c->getName()); + $this->assertEquals('doc', $c->getSummary()); + $this->assertEquals('http://www.example.com', $c->getDocumentationUrl()); + $this->assertEquals('POST', $c->getHttpMethod()); + $this->assertEquals('/api/v1', $c->getUri()); + $this->assertEquals('array', $c->getResponseClass()); + $this->assertEquals('returns the json_decoded response', $c->getResponseNotes()); + $this->assertTrue($c->getDeprecated()); + $this->assertEquals('Guzzle\\Service\\Command\\OperationCommand', $c->getClass()); + $this->assertEquals(array( + 'key' => new Parameter(array( + 'name' => 'key', + 'required' => true, + 'type' => 'string', + 'maxLength' => 10, + 'parent' => $c + )), + 'key_2' => new Parameter(array( + 'name' => 'key_2', + 'required' => true, + 'type' => 'integer', + 'default' => 10, + 'parent' => $c + )) + ), $c->getParams()); + + $this->assertEquals(new Parameter(array( + 'name' => 'key_2', + 'required' => true, + 'type' => 'integer', + 'default' => 10, + 'parent' => $c + )), $c->getParam('key_2')); + + $this->assertNull($c->getParam('afefwef')); + $this->assertArrayNotHasKey('parent', $c->getParam('key_2')->toArray()); + } + + public function testAllowsConcreteCommands() + { + $c = new Operation(array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'parameters' => array( + 'p' => new Parameter(array( + 'name' => 'foo' + )) + ) + )); + $this->assertEquals('Guzzle\\Service\\Command\ClosureCommand', $c->getClass()); + } + + public function testConvertsToArray() + { + $data = array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'summary' => 'test', + 'documentationUrl' => 'http://www.example.com', + 'httpMethod' => 'PUT', + 'uri' => '/', + 'parameters' => array('p' => array('name' => 'foo')) + ); + $c = new Operation($data); + $toArray = $c->toArray(); + unset($data['name']); + $this->assertArrayHasKey('parameters', $toArray); + $this->assertInternalType('array', $toArray['parameters']); + + // Normalize the array + unset($data['parameters']); + unset($toArray['parameters']); + + $data['responseType'] = 'primitive'; + $data['responseClass'] = 'array'; + $this->assertEquals($data, $toArray); + } + + public function testDeterminesIfHasParam() + { + $command = $this->getTestCommand(); + $this->assertTrue($command->hasParam('data')); + $this->assertFalse($command->hasParam('baz')); + } + + public function testReturnsParamNames() + { + $command = $this->getTestCommand(); + $this->assertEquals(array('data'), $command->getParamNames()); + } + + protected function getTestCommand() + { + return new Operation(array( + 'parameters' => array( + 'data' => new Parameter(array( + 'type' => 'string' + )) + ) + )); + } + + public function testCanBuildUpCommands() + { + $c = new Operation(array()); + $c->setName('foo') + ->setClass('Baz') + ->setDeprecated(false) + ->setSummary('summary') + ->setDocumentationUrl('http://www.foo.com') + ->setHttpMethod('PUT') + ->setResponseNotes('oh') + ->setResponseClass('string') + ->setUri('/foo/bar') + ->addParam(new Parameter(array( + 'name' => 'test' + ))); + + $this->assertEquals('foo', $c->getName()); + $this->assertEquals('Baz', $c->getClass()); + $this->assertEquals(false, $c->getDeprecated()); + $this->assertEquals('summary', $c->getSummary()); + $this->assertEquals('http://www.foo.com', $c->getDocumentationUrl()); + $this->assertEquals('PUT', $c->getHttpMethod()); + $this->assertEquals('oh', $c->getResponseNotes()); + $this->assertEquals('string', $c->getResponseClass()); + $this->assertEquals('/foo/bar', $c->getUri()); + $this->assertEquals(array('test'), $c->getParamNames()); + } + + public function testCanRemoveParams() + { + $c = new Operation(array()); + $c->addParam(new Parameter(array('name' => 'foo'))); + $this->assertTrue($c->hasParam('foo')); + $c->removeParam('foo'); + $this->assertFalse($c->hasParam('foo')); + } + + public function testAddsNameToParametersIfNeeded() + { + $command = new Operation(array('parameters' => array('foo' => new Parameter(array())))); + $this->assertEquals('foo', $command->getParam('foo')->getName()); + } + + public function testContainsApiErrorInformation() + { + $command = $this->getOperation(); + $this->assertEquals(1, count($command->getErrorResponses())); + $arr = $command->toArray(); + $this->assertEquals(1, count($arr['errorResponses'])); + $command->addErrorResponse(400, 'Foo', 'Baz\\Bar'); + $this->assertEquals(2, count($command->getErrorResponses())); + $command->setErrorResponses(array()); + $this->assertEquals(0, count($command->getErrorResponses())); + } + + public function testHasNotes() + { + $o = new Operation(array('notes' => 'foo')); + $this->assertEquals('foo', $o->getNotes()); + $o->setNotes('bar'); + $this->assertEquals('bar', $o->getNotes()); + } + + public function testHasData() + { + $o = new Operation(array('data' => array('foo' => 'baz', 'bar' => 123))); + $o->setData('test', false); + $this->assertEquals('baz', $o->getData('foo')); + $this->assertEquals(123, $o->getData('bar')); + $this->assertNull($o->getData('wfefwe')); + $this->assertEquals(array( + 'parameters' => array(), + 'class' => 'Guzzle\Service\Command\OperationCommand', + 'data' => array('foo' => 'baz', 'bar' => 123, 'test' => false), + 'responseClass' => 'array', + 'responseType' => 'primitive' + ), $o->toArray()); + } + + public function testHasServiceDescription() + { + $s = new ServiceDescription(); + $o = new Operation(array(), $s); + $this->assertSame($s, $o->getServiceDescription()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesResponseType() + { + $o = new Operation(array('responseClass' => 'array', 'responseType' => 'foo')); + } + + public function testInfersResponseType() + { + $o = $this->getOperation(); + $o->setServiceDescription(new ServiceDescription(array('models' => array('Foo' => array())))); + $this->assertEquals('primitive', $o->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('boolean')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('array')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('integer')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('string')->getResponseType()); + $this->assertEquals('class', $o->setResponseClass('foo')->getResponseType()); + $this->assertEquals('class', $o->setResponseClass(__CLASS__)->getResponseType()); + $this->assertEquals('model', $o->setResponseClass('Foo')->getResponseType()); + } + + public function testHasResponseType() + { + // infers in the constructor + $o = new Operation(array('responseClass' => 'array')); + $this->assertEquals('primitive', $o->getResponseType()); + // Infers when set + $o = new Operation(); + $this->assertEquals('primitive', $o->getResponseType()); + $this->assertEquals('model', $o->setResponseType('model')->getResponseType()); + } + + public function testHasAdditionalParameters() + { + $o = new Operation(array( + 'additionalParameters' => array( + 'type' => 'string', 'name' => 'binks' + ), + 'parameters' => array( + 'foo' => array('type' => 'integer') + ) + )); + $this->assertEquals('string', $o->getAdditionalParameters()->getType()); + $arr = $o->toArray(); + $this->assertEquals(array( + 'type' => 'string' + ), $arr['additionalParameters']); + } + + /** + * @return Operation + */ + protected function getOperation() + { + return new Operation(array( + 'name' => 'OperationTest', + 'class' => get_class($this), + 'parameters' => array( + 'test' => array('type' => 'object'), + 'bool_1' => array('default' => true, 'type' => 'boolean'), + 'bool_2' => array('default' => false), + 'float' => array('type' => 'numeric'), + 'int' => array('type' => 'integer'), + 'date' => array('type' => 'string'), + 'timestamp' => array('type' => 'string'), + 'string' => array('type' => 'string'), + 'username' => array('type' => 'string', 'required' => true, 'filters' => 'strtolower'), + 'test_function' => array('type' => 'string', 'filters' => __CLASS__ . '::strtoupper') + ), + 'errorResponses' => array( + array('code' => 503, 'reason' => 'InsufficientCapacity', 'class' => 'Guzzle\\Exception\\RuntimeException') + ) + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php new file mode 100755 index 000000000..b9c162aae --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php @@ -0,0 +1,411 @@ + 'foo', + 'type' => 'bar', + 'required' => true, + 'default' => '123', + 'description' => '456', + 'minLength' => 2, + 'maxLength' => 5, + 'location' => 'body', + 'static' => 'static!', + 'filters' => array('trim', 'json_encode') + ); + + public function testCreatesParamFromArray() + { + $p = new Parameter($this->data); + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('bar', $p->getType()); + $this->assertEquals(true, $p->getRequired()); + $this->assertEquals('123', $p->getDefault()); + $this->assertEquals('456', $p->getDescription()); + $this->assertEquals(2, $p->getMinLength()); + $this->assertEquals(5, $p->getMaxLength()); + $this->assertEquals('body', $p->getLocation()); + $this->assertEquals('static!', $p->getStatic()); + $this->assertEquals(array('trim', 'json_encode'), $p->getFilters()); + } + + public function testCanConvertToArray() + { + $p = new Parameter($this->data); + unset($this->data['name']); + $this->assertEquals($this->data, $p->toArray()); + } + + public function testUsesStatic() + { + $d = $this->data; + $d['default'] = 'booboo'; + $d['static'] = true; + $p = new Parameter($d); + $this->assertEquals('booboo', $p->getValue('bar')); + } + + public function testUsesDefault() + { + $d = $this->data; + $d['default'] = 'foo'; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('foo', $p->getValue(null)); + } + + public function testReturnsYourValue() + { + $d = $this->data; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('foo', $p->getValue('foo')); + } + + public function testZeroValueDoesNotCauseDefaultToBeReturned() + { + $d = $this->data; + $d['default'] = '1'; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('0', $p->getValue('0')); + } + + public function testFiltersValues() + { + $d = $this->data; + $d['static'] = null; + $d['filters'] = 'strtoupper'; + $p = new Parameter($d); + $this->assertEquals('FOO', $p->filter('foo')); + } + + public function testConvertsBooleans() + { + $p = new Parameter(array('type' => 'boolean')); + $this->assertEquals(true, $p->filter('true')); + $this->assertEquals(false, $p->filter('false')); + } + + public function testUsesArrayByDefaultForFilters() + { + $d = $this->data; + $d['filters'] = null; + $p = new Parameter($d); + $this->assertEquals(array(), $p->getFilters()); + } + + public function testAllowsSimpleLocationValue() + { + $p = new Parameter(array('name' => 'myname', 'location' => 'foo', 'sentAs' => 'Hello')); + $this->assertEquals('foo', $p->getLocation()); + $this->assertEquals('Hello', $p->getSentAs()); + } + + public function testParsesTypeValues() + { + $p = new Parameter(array('type' => 'foo')); + $this->assertEquals('foo', $p->getType()); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage A [method] value must be specified for each complex filter + */ + public function testValidatesComplexFilters() + { + $p = new Parameter(array('filters' => array(array('args' => 'foo')))); + } + + public function testCanBuildUpParams() + { + $p = new Parameter(array()); + $p->setName('foo') + ->setDescription('c') + ->setFilters(array('d')) + ->setLocation('e') + ->setSentAs('f') + ->setMaxLength(1) + ->setMinLength(1) + ->setMinimum(2) + ->setMaximum(2) + ->setMinItems(3) + ->setMaxItems(3) + ->setRequired(true) + ->setStatic(true) + ->setDefault('h') + ->setType('i'); + + $p->addFilter('foo'); + + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('h', $p->getDefault()); + $this->assertEquals('c', $p->getDescription()); + $this->assertEquals(array('d', 'foo'), $p->getFilters()); + $this->assertEquals('e', $p->getLocation()); + $this->assertEquals('f', $p->getSentAs()); + $this->assertEquals(1, $p->getMaxLength()); + $this->assertEquals(1, $p->getMinLength()); + $this->assertEquals(2, $p->getMaximum()); + $this->assertEquals(2, $p->getMinimum()); + $this->assertEquals(3, $p->getMaxItems()); + $this->assertEquals(3, $p->getMinItems()); + $this->assertEquals(true, $p->getRequired()); + $this->assertEquals(true, $p->getStatic()); + $this->assertEquals('i', $p->getType()); + } + + public function testAllowsNestedShape() + { + $command = $this->getServiceBuilder()->get('mock')->getCommand('mock_command')->getOperation(); + $param = new Parameter(array( + 'parent' => $command, + 'name' => 'foo', + 'type' => 'object', + 'location' => 'query', + 'properties' => array( + 'foo' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'baz' => array( + 'name' => 'baz', + 'type' => 'bool', + ) + ) + ), + 'bar' => array( + 'name' => 'bar', + 'default' => '123' + ) + ) + )); + + $this->assertSame($command, $param->getParent()); + $this->assertNotEmpty($param->getProperties()); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('foo')); + $this->assertSame($param, $param->getProperty('foo')->getParent()); + $this->assertSame($param->getProperty('foo'), $param->getProperty('foo')->getProperty('baz')->getParent()); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('bar')); + $this->assertSame($param, $param->getProperty('bar')->getParent()); + + $array = $param->toArray(); + $this->assertInternalType('array', $array['properties']); + $this->assertArrayHasKey('foo', $array['properties']); + $this->assertArrayHasKey('bar', $array['properties']); + } + + public function testAllowsComplexFilters() + { + $that = $this; + $param = new Parameter(array()); + $param->setFilters(array(array('method' => function ($a, $b, $c, $d) use ($that, $param) { + $that->assertEquals('test', $a); + $that->assertEquals('my_value!', $b); + $that->assertEquals('bar', $c); + $that->assertSame($param, $d); + return 'abc' . $b; + }, 'args' => array('test', '@value', 'bar', '@api')))); + $this->assertEquals('abcmy_value!', $param->filter('my_value!')); + } + + public function testCanChangeParentOfNestedParameter() + { + $param1 = new Parameter(array('name' => 'parent')); + $param2 = new Parameter(array('name' => 'child')); + $param2->setParent($param1); + $this->assertSame($param1, $param2->getParent()); + } + + public function testCanRemoveFromNestedStructure() + { + $param1 = new Parameter(array('name' => 'parent')); + $param2 = new Parameter(array('name' => 'child')); + $param1->addProperty($param2); + $this->assertSame($param1, $param2->getParent()); + $this->assertSame($param2, $param1->getProperty('child')); + + // Remove a single child from the structure + $param1->removeProperty('child'); + $this->assertNull($param1->getProperty('child')); + // Remove the entire structure + $param1->addProperty($param2); + $param1->removeProperty('child'); + $this->assertNull($param1->getProperty('child')); + } + + public function testAddsAdditionalProperties() + { + $p = new Parameter(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + )); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getAdditionalProperties()); + $this->assertNull($p->getAdditionalProperties()->getAdditionalProperties()); + $p = new Parameter(array('type' => 'object')); + $this->assertTrue($p->getAdditionalProperties()); + } + + public function testAddsItems() + { + $p = new Parameter(array( + 'type' => 'array', + 'items' => array('type' => 'string') + )); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getItems()); + $out = $p->toArray(); + $this->assertEquals('array', $out['type']); + $this->assertInternalType('array', $out['items']); + } + + public function testHasExtraProperties() + { + $p = new Parameter(); + $this->assertEquals(array(), $p->getData()); + $p->setData(array('foo' => 'bar')); + $this->assertEquals('bar', $p->getData('foo')); + $p->setData('baz', 'boo'); + $this->assertEquals(array('foo' => 'bar', 'baz' => 'boo'), $p->getData()); + } + + public function testCanRetrieveKnownPropertiesUsingDataMethod() + { + $p = new Parameter(); + $this->assertEquals(null, $p->getData('foo')); + $p->setName('test'); + $this->assertEquals('test', $p->getData('name')); + } + + public function testHasInstanceOf() + { + $p = new Parameter(); + $this->assertNull($p->getInstanceOf()); + $p->setInstanceOf('Foo'); + $this->assertEquals('Foo', $p->getInstanceOf()); + } + + public function testHasPattern() + { + $p = new Parameter(); + $this->assertNull($p->getPattern()); + $p->setPattern('/[0-9]+/'); + $this->assertEquals('/[0-9]+/', $p->getPattern()); + } + + public function testHasEnum() + { + $p = new Parameter(); + $this->assertNull($p->getEnum()); + $p->setEnum(array('foo', 'bar')); + $this->assertEquals(array('foo', 'bar'), $p->getEnum()); + } + + public function testSerializesItems() + { + $p = new Parameter(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + )); + $this->assertEquals(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + ), $p->toArray()); + } + + public function testResolvesRefKeysRecursively() + { + $description = new ServiceDescription(array( + 'models' => array( + 'JarJar' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), + 'Anakin' => array('type' => 'array', 'items' => array('$ref' => 'JarJar')) + ) + )); + $p = new Parameter(array('$ref' => 'Anakin', 'description' => 'added'), $description); + $this->assertEquals(array( + 'type' => 'array', + 'items' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), + 'description' => 'added' + ), $p->toArray()); + } + + public function testResolvesExtendsRecursively() + { + $jarJar = array('type' => 'string', 'default' => 'Mesa address tha senate!', 'description' => 'a'); + $anakin = array('type' => 'array', 'items' => array('extends' => 'JarJar', 'description' => 'b')); + $description = new ServiceDescription(array( + 'models' => array('JarJar' => $jarJar, 'Anakin' => $anakin) + )); + // Description attribute will be updated, and format added + $p = new Parameter(array('extends' => 'Anakin', 'format' => 'date'), $description); + $this->assertEquals(array( + 'type' => 'array', + 'format' => 'date', + 'items' => array( + 'type' => 'string', + 'default' => 'Mesa address tha senate!', + 'description' => 'b' + ) + ), $p->toArray()); + } + + public function testHasKeyMethod() + { + $p = new Parameter(array('name' => 'foo', 'sentAs' => 'bar')); + $this->assertEquals('bar', $p->getWireName()); + $p->setSentAs(null); + $this->assertEquals('foo', $p->getWireName()); + } + + public function testIncludesNameInToArrayWhenItemsAttributeHasName() + { + $p = new Parameter(array( + 'type' => 'array', + 'name' => 'Abc', + 'items' => array( + 'name' => 'Foo', + 'type' => 'object' + ) + )); + $result = $p->toArray(); + $this->assertEquals(array( + 'type' => 'array', + 'items' => array( + 'name' => 'Foo', + 'type' => 'object', + 'additionalProperties' => true + ) + ), $result); + } + + public function dateTimeProvider() + { + $d = 'October 13, 2012 16:15:46 UTC'; + + return array( + array($d, 'date-time', '2012-10-13T16:15:46Z'), + array($d, 'date', '2012-10-13'), + array($d, 'timestamp', strtotime($d)), + array(new \DateTime($d), 'timestamp', strtotime($d)) + ); + } + + /** + * @dataProvider dateTimeProvider + */ + public function testAppliesFormat($d, $format, $result) + { + $p = new Parameter(); + $p->setFormat($format); + $this->assertEquals($format, $p->getFormat()); + $this->assertEquals($result, $p->filter($d)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php new file mode 100755 index 000000000..eb3619bea --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php @@ -0,0 +1,61 @@ +assertEquals($result, SchemaFormatter::format($format, $value)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesDateTimeInput() + { + SchemaFormatter::format('date-time', false); + } + + public function testEnsuresTimestampsAreIntegers() + { + $t = time(); + $result = SchemaFormatter::format('timestamp', $t); + $this->assertSame($t, $result); + $this->assertInternalType('int', $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php new file mode 100755 index 000000000..4d6cc8728 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php @@ -0,0 +1,326 @@ +validator = new SchemaValidator(); + } + + public function testValidatesArrayListsAreNumericallyIndexed() + { + $value = array(array(1)); + $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals( + array('[Foo][0] must be an array of properties. Got a numerically indexed array.'), + $this->validator->getErrors() + ); + } + + public function testValidatesArrayListsContainProperItems() + { + $value = array(true); + $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals( + array('[Foo][0] must be of type object'), + $this->validator->getErrors() + ); + } + + public function testAddsDefaultValuesInLists() + { + $value = array(array()); + $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals(array(array('Bar' => true)), $value); + } + + public function testMergesDefaultValuesInLists() + { + $value = array( + array('Baz' => 'hello!'), + array('Bar' => false) + ); + $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals(array( + array( + 'Baz' => 'hello!', + 'Bar' => true + ), + array('Bar' => false) + ), $value); + } + + public function testCorrectlyConvertsParametersToArrayWhenArraysArePresent() + { + $param = $this->getComplexParam(); + $result = $param->toArray(); + $this->assertInternalType('array', $result['items']); + $this->assertEquals('array', $result['type']); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getItems()); + } + + public function testAllowsInstanceOf() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'instanceOf' => get_class($this) + )); + $this->assertTrue($this->validator->validate($p, $this)); + $this->assertFalse($this->validator->validate($p, $p)); + $this->assertEquals(array('[foo] must be an instance of ' . __CLASS__), $this->validator->getErrors()); + } + + public function testEnforcesInstanceOfOnlyWhenObject() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => array('object', 'string'), + 'instanceOf' => get_class($this) + )); + $this->assertTrue($this->validator->validate($p, $this)); + $s = 'test'; + $this->assertTrue($this->validator->validate($p, $s)); + } + + public function testConvertsObjectsToArraysWhenToArrayInterface() + { + $o = $this->getMockBuilder('Guzzle\Common\ToArrayInterface') + ->setMethods(array('toArray')) + ->getMockForAbstractClass(); + $o->expects($this->once()) + ->method('toArray') + ->will($this->returnValue(array( + 'foo' => 'bar' + ))); + $p = new Parameter(array( + 'name' => 'test', + 'type' => 'object', + 'properties' => array( + 'foo' => array('required' => 'true') + ) + )); + $this->assertTrue($this->validator->validate($p, $o)); + } + + public function testMergesValidationErrorsInPropertiesWithParent() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array('type' => 'string', 'required' => true, 'description' => 'This is what it does'), + 'test' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 5), + 'test2' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 2), + 'test3' => array('type' => 'integer', 'minimum' => 100), + 'test4' => array('type' => 'integer', 'maximum' => 10), + 'test5' => array('type' => 'array', 'maxItems' => 2), + 'test6' => array('type' => 'string', 'enum' => array('a', 'bc')), + 'test7' => array('type' => 'string', 'pattern' => '/[0-9]+/'), + 'test8' => array('type' => 'number'), + 'baz' => array( + 'type' => 'array', + 'minItems' => 2, + 'required' => true, + "items" => array("type" => "string") + ) + ) + )); + + $value = array( + 'test' => 'a', + 'test2' => 'abc', + 'baz' => array(false), + 'test3' => 10, + 'test4' => 100, + 'test5' => array(1, 3, 4), + 'test6' => 'Foo', + 'test7' => 'abc', + 'test8' => 'abc' + ); + + $this->assertFalse($this->validator->validate($p, $value)); + $this->assertEquals(array ( + '[foo][bar] is a required string: This is what it does', + '[foo][baz] must contain 2 or more elements', + '[foo][baz][0] must be of type string', + '[foo][test2] length must be less than or equal to 2', + '[foo][test3] must be greater than or equal to 100', + '[foo][test4] must be less than or equal to 10', + '[foo][test5] must contain 2 or fewer elements', + '[foo][test6] must be one of "a" or "bc"', + '[foo][test7] must match the following regular expression: /[0-9]+/', + '[foo][test8] must be of type number', + '[foo][test] length must be greater than or equal to 2', + ), $this->validator->getErrors()); + } + + public function testHandlesNullValuesInArraysWithDefaults() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'bar' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'foo' => array('default' => 'hi') + ) + ) + ) + )); + $value = array(); + $this->assertTrue($this->validator->validate($p, $value)); + $this->assertEquals(array('bar' => array('foo' => 'hi')), $value); + } + + public function testFailsWhenNullValuesInArraysWithNoDefaults() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'bar' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array('foo' => array('type' => 'string')) + ) + ) + )); + $value = array(); + $this->assertFalse($this->validator->validate($p, $value)); + $this->assertEquals(array('[foo][bar] is a required object'), $this->validator->getErrors()); + } + + public function testChecksTypes() + { + $p = new SchemaValidator(); + $r = new \ReflectionMethod($p, 'determineType'); + $r->setAccessible(true); + $this->assertEquals('any', $r->invoke($p, 'any', 'hello')); + $this->assertEquals(false, $r->invoke($p, 'foo', 'foo')); + $this->assertEquals('string', $r->invoke($p, 'string', 'hello')); + $this->assertEquals(false, $r->invoke($p, 'string', false)); + $this->assertEquals('integer', $r->invoke($p, 'integer', 1)); + $this->assertEquals(false, $r->invoke($p, 'integer', 'abc')); + $this->assertEquals('numeric', $r->invoke($p, 'numeric', 1)); + $this->assertEquals('numeric', $r->invoke($p, 'numeric', '1')); + $this->assertEquals('number', $r->invoke($p, 'number', 1)); + $this->assertEquals('number', $r->invoke($p, 'number', '1')); + $this->assertEquals(false, $r->invoke($p, 'numeric', 'a')); + $this->assertEquals('boolean', $r->invoke($p, 'boolean', true)); + $this->assertEquals('boolean', $r->invoke($p, 'boolean', false)); + $this->assertEquals(false, $r->invoke($p, 'boolean', 'false')); + $this->assertEquals('null', $r->invoke($p, 'null', null)); + $this->assertEquals(false, $r->invoke($p, 'null', 'abc')); + $this->assertEquals('array', $r->invoke($p, 'array', array())); + $this->assertEquals(false, $r->invoke($p, 'array', 'foo')); + } + + public function testValidatesFalseAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')), + 'additionalProperties' => false + )); + $value = array('test' => '123'); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test] is not an allowed property'), $this->validator->getErrors()); + $value = array('bar' => '123'); + $this->assertTrue($this->validator->validate($param, $value)); + } + + public function testAllowsUndefinedAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')) + )); + $value = array('test' => '123'); + $this->assertTrue($this->validator->validate($param, $value)); + } + + public function testValidatesAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')), + 'additionalProperties' => array('type' => 'integer') + )); + $value = array('test' => 'foo'); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test] must be of type integer'), $this->validator->getErrors()); + } + + public function testValidatesAdditionalPropertiesThatArrayArrays() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'array', + 'items' => array('type' => 'string') + ) + )); + $value = array('test' => array(true)); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test][0] must be of type string'), $this->validator->getErrors()); + } + + public function testIntegersCastToStringWhenTypeMismatch() + { + $param = new Parameter(array('name' => 'test', 'type' => 'string')); + $value = 12; + $this->assertTrue($this->validator->validate($param, $value)); + $this->assertEquals('12', $value); + } + + public function testRequiredMessageIncludesType() + { + $param = new Parameter(array('name' => 'test', 'type' => array('string', 'boolean'), 'required' => true)); + $value = null; + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[test] is a required string or boolean'), $this->validator->getErrors()); + } + + protected function getComplexParam() + { + return new Parameter(array( + 'name' => 'Foo', + 'type' => 'array', + 'required' => true, + 'min' => 1, + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array( + 'type' => 'string', + ), + 'Bar' => array( + 'required' => true, + 'type' => 'boolean', + 'default' => true + ) + ) + ) + )); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php new file mode 100755 index 000000000..bbfd1d6df --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php @@ -0,0 +1,177 @@ + true, + 'baz' => array('bar'), + 'apiVersion' => '123', + 'operations' => array() + )); + + $this->assertEquals(true, $d->getData('foo')); + $this->assertEquals(array('bar'), $d->getData('baz')); + $this->assertEquals('123', $d->getApiVersion()); + } + + public function testAllowsDeepNestedInheritance() + { + $d = ServiceDescription::factory(array( + 'operations' => array( + 'abstract' => array( + 'httpMethod' => 'HEAD', + 'parameters' => array( + 'test' => array('type' => 'string', 'required' => true) + ) + ), + 'abstract2' => array('uri' => '/test', 'extends' => 'abstract'), + 'concrete' => array('extends' => 'abstract2'), + 'override' => array('extends' => 'abstract', 'httpMethod' => 'PUT'), + 'override2' => array('extends' => 'override', 'httpMethod' => 'POST', 'uri' => '/') + ) + )); + + $c = $d->getOperation('concrete'); + $this->assertEquals('/test', $c->getUri()); + $this->assertEquals('HEAD', $c->getHttpMethod()); + $params = $c->getParams(); + $param = $params['test']; + $this->assertEquals('string', $param->getType()); + $this->assertTrue($param->getRequired()); + + // Ensure that merging HTTP method does not make an array + $this->assertEquals('PUT', $d->getOperation('override')->getHttpMethod()); + $this->assertEquals('POST', $d->getOperation('override2')->getHttpMethod()); + $this->assertEquals('/', $d->getOperation('override2')->getUri()); + } + + /** + * @expectedException RuntimeException + */ + public function testThrowsExceptionWhenExtendingMissingCommand() + { + ServiceDescription::factory(array( + 'operations' => array( + 'concrete' => array( + 'extends' => 'missing' + ) + ) + )); + } + + public function testAllowsMultipleInheritance() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'a' => array( + 'httpMethod' => 'GET', + 'parameters' => array( + 'a1' => array( + 'default' => 'foo', + 'required' => true, + 'prepend' => 'hi' + ) + ) + ), + 'b' => array( + 'extends' => 'a', + 'parameters' => array( + 'b2' => array() + ) + ), + 'c' => array( + 'parameters' => array( + 'a1' => array( + 'default' => 'bar', + 'required' => true, + 'description' => 'test' + ), + 'c3' => array() + ) + ), + 'd' => array( + 'httpMethod' => 'DELETE', + 'extends' => array('b', 'c'), + 'parameters' => array( + 'test' => array() + ) + ) + ) + )); + + $command = $description->getOperation('d'); + $this->assertEquals('DELETE', $command->getHttpMethod()); + $this->assertContains('a1', $command->getParamNames()); + $this->assertContains('b2', $command->getParamNames()); + $this->assertContains('c3', $command->getParamNames()); + $this->assertContains('test', $command->getParamNames()); + + $this->assertTrue($command->getParam('a1')->getRequired()); + $this->assertEquals('bar', $command->getParam('a1')->getDefault()); + $this->assertEquals('test', $command->getParam('a1')->getDescription()); + } + + public function testAddsOtherFields() + { + $description = ServiceDescription::factory(array( + 'operations' => array(), + 'description' => 'Foo', + 'apiVersion' => 'bar' + )); + $this->assertEquals('Foo', $description->getDescription()); + $this->assertEquals('bar', $description->getApiVersion()); + } + + public function testCanLoadNestedExtends() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'root' => array( + 'class' => 'foo' + ), + 'foo' => array( + 'extends' => 'root', + 'parameters' => array( + 'baz' => array('type' => 'string') + ) + ), + 'foo_2' => array( + 'extends' => 'foo', + 'parameters' => array( + 'bar' => array('type' => 'string') + ) + ), + 'foo_3' => array( + 'class' => 'bar', + 'parameters' => array( + 'bar2' => array('type' => 'string') + ) + ), + 'foo_4' => array( + 'extends' => array('foo_2', 'foo_3'), + 'parameters' => array( + 'bar3' => array('type' => 'string') + ) + ) + ) + )); + + $this->assertTrue($description->hasOperation('foo_4')); + $foo4 = $description->getOperation('foo_4'); + $this->assertTrue($foo4->hasParam('baz')); + $this->assertTrue($foo4->hasParam('bar')); + $this->assertTrue($foo4->hasParam('bar2')); + $this->assertTrue($foo4->hasParam('bar3')); + $this->assertEquals('bar', $foo4->getClass()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php new file mode 100755 index 000000000..ff2545235 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php @@ -0,0 +1,240 @@ +serviceData = array( + 'test_command' => new Operation(array( + 'name' => 'test_command', + 'description' => 'documentationForCommand', + 'httpMethod' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'parameters' => array( + 'bucket' => array('required' => true), + 'key' => array('required' => true) + ) + )) + ); + } + + /** + * @covers Guzzle\Service\Description\ServiceDescription::factory + * @covers Guzzle\Service\Description\ServiceDescriptionLoader::build + */ + public function testFactoryDelegatesToConcreteFactories() + { + $jsonFile = __DIR__ . '/../../TestData/test_service.json'; + $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($jsonFile)); + } + + public function testConstructor() + { + $service = new ServiceDescription(array('operations' => $this->serviceData)); + $this->assertEquals(1, count($service->getOperations())); + $this->assertFalse($service->hasOperation('foobar')); + $this->assertTrue($service->hasOperation('test_command')); + } + + public function testIsSerializable() + { + $service = new ServiceDescription(array('operations' => $this->serviceData)); + $data = serialize($service); + $d2 = unserialize($data); + $this->assertEquals(serialize($service), serialize($d2)); + } + + public function testSerializesParameters() + { + $service = new ServiceDescription(array( + 'operations' => array( + 'foo' => new Operation(array('parameters' => array('foo' => array('type' => 'string')))) + ) + )); + $serialized = serialize($service); + $this->assertContains('"parameters":{"foo":', $serialized); + $service = unserialize($serialized); + $this->assertTrue($service->getOperation('foo')->hasParam('foo')); + } + + public function testAllowsForJsonBasedArrayParamsFunctionalTest() + { + $description = new ServiceDescription(array( + 'operations' => array( + 'test' => new Operation(array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'data' => array( + 'required' => true, + 'filters' => 'json_encode', + 'location' => 'body' + ) + ) + )) + ) + )); + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('test', array( + 'data' => array( + 'foo' => 'bar' + ) + )); + + $request = $command->prepare(); + $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); + } + + public function testContainsModels() + { + $d = new ServiceDescription(array( + 'operations' => array('foo' => array()), + 'models' => array( + 'Tag' => array('type' => 'object'), + 'Person' => array('type' => 'object') + ) + )); + $this->assertTrue($d->hasModel('Tag')); + $this->assertTrue($d->hasModel('Person')); + $this->assertFalse($d->hasModel('Foo')); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $d->getModel('Tag')); + $this->assertNull($d->getModel('Foo')); + $this->assertContains('"models":{', serialize($d)); + $this->assertEquals(array('Tag', 'Person'), array_keys($d->getModels())); + } + + public function testCanAddModels() + { + $d = new ServiceDescription(array()); + $this->assertFalse($d->hasModel('Foo')); + $d->addModel(new Parameter(array('name' => 'Foo'))); + $this->assertTrue($d->hasModel('Foo')); + } + + public function testHasAttributes() + { + $d = new ServiceDescription(array( + 'operations' => array(), + 'name' => 'Name', + 'description' => 'Description', + 'apiVersion' => '1.24' + )); + + $this->assertEquals('Name', $d->getName()); + $this->assertEquals('Description', $d->getDescription()); + $this->assertEquals('1.24', $d->getApiVersion()); + + $s = serialize($d); + $this->assertContains('"name":"Name"', $s); + $this->assertContains('"description":"Description"', $s); + $this->assertContains('"apiVersion":"1.24"', $s); + + $d = unserialize($s); + $this->assertEquals('Name', $d->getName()); + $this->assertEquals('Description', $d->getDescription()); + $this->assertEquals('1.24', $d->getApiVersion()); + } + + public function testPersistsCustomAttributes() + { + $data = array( + 'operations' => array('foo' => array('class' => 'foo', 'parameters' => array())), + 'name' => 'Name', + 'description' => 'Test', + 'apiVersion' => '1.24', + 'auth' => 'foo', + 'keyParam' => 'bar' + ); + $d = new ServiceDescription($data); + $d->setData('hello', 'baz'); + $this->assertEquals('foo', $d->getData('auth')); + $this->assertEquals('baz', $d->getData('hello')); + $this->assertEquals('bar', $d->getData('keyParam')); + // responseClass and responseType are added by default + $data['operations']['foo']['responseClass'] = 'array'; + $data['operations']['foo']['responseType'] = 'primitive'; + $this->assertEquals($data + array('hello' => 'baz'), json_decode($d->serialize(), true)); + } + + public function testHasToArray() + { + $data = array( + 'operations' => array(), + 'name' => 'Name', + 'description' => 'Test' + ); + $d = new ServiceDescription($data); + $arr = $d->toArray(); + $this->assertEquals('Name', $arr['name']); + $this->assertEquals('Test', $arr['description']); + } + + public function testReturnsNullWhenRetrievingMissingOperation() + { + $s = new ServiceDescription(array()); + $this->assertNull($s->getOperation('foo')); + } + + public function testCanAddOperations() + { + $s = new ServiceDescription(array()); + $this->assertFalse($s->hasOperation('Foo')); + $s->addOperation(new Operation(array('name' => 'Foo'))); + $this->assertTrue($s->hasOperation('Foo')); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesOperationTypes() + { + $s = new ServiceDescription(array( + 'operations' => array('foo' => new \stdClass()) + )); + } + + public function testHasBaseUrl() + { + $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); + $this->assertEquals('http://foo.com', $description->getBaseUrl()); + $description->setBaseUrl('http://foobar.com'); + $this->assertEquals('http://foobar.com', $description->getBaseUrl()); + } + + public function testCanUseBasePath() + { + $description = new ServiceDescription(array('basePath' => 'http://foo.com')); + $this->assertEquals('http://foo.com', $description->getBaseUrl()); + } + + public function testModelsHaveNames() + { + $desc = array( + 'models' => array( + 'date' => array('type' => 'string'), + 'user'=> array( + 'type' => 'object', + 'properties' => array( + 'dob' => array('$ref' => 'date') + ) + ) + ) + ); + + $s = ServiceDescription::factory($desc); + $this->assertEquals('date', $s->getModel('date')->getName()); + $this->assertEquals('dob', $s->getModel('user')->getProperty('dob')->getName()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php new file mode 100755 index 000000000..be0d4ac8b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php @@ -0,0 +1,66 @@ +addSuccessfulCommand($c1)->addFailedCommand($c2); + $this->assertSame(array($c1), $e->getSuccessfulCommands()); + $this->assertSame(array($c2), $e->getFailedCommands()); + $this->assertSame(array($c1, $c2), $e->getAllCommands()); + } + + public function testConvertsMultiExceptionIntoCommandTransfer() + { + $r1 = new Request('GET', 'http://foo.com'); + $r2 = new Request('GET', 'http://foobaz.com'); + $e = new MultiTransferException('Test', 123); + $e->addSuccessfulRequest($r1)->addFailedRequest($r2); + $ce = CommandTransferException::fromMultiTransferException($e); + + $this->assertInstanceOf('Guzzle\Service\Exception\CommandTransferException', $ce); + $this->assertEquals('Test', $ce->getMessage()); + $this->assertEquals(123, $ce->getCode()); + $this->assertSame(array($r1), $ce->getSuccessfulRequests()); + $this->assertSame(array($r2), $ce->getFailedRequests()); + } + + public function testCanRetrieveExceptionForCommand() + { + $r1 = new Request('GET', 'http://foo.com'); + $e1 = new \Exception('foo'); + $c1 = $this->getMockBuilder('Guzzle\Tests\Service\Mock\Command\MockCommand') + ->setMethods(array('getRequest')) + ->getMock(); + $c1->expects($this->once())->method('getRequest')->will($this->returnValue($r1)); + + $e = new MultiTransferException('Test', 123); + $e->addFailedRequestWithException($r1, $e1); + $ce = CommandTransferException::fromMultiTransferException($e); + $ce->addFailedCommand($c1); + + $this->assertSame($e1, $ce->getExceptionForFailedCommand($c1)); + } + + public function testAddsNonRequestExceptions() + { + $e = new MultiTransferException(); + $e->add(new \Exception('bar')); + $e->addFailedRequestWithException(new Request('GET', 'http://www.foo.com'), new \Exception('foo')); + $ce = CommandTransferException::fromMultiTransferException($e); + $this->assertEquals(2, count($ce)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php new file mode 100755 index 000000000..6455295ad --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php @@ -0,0 +1,15 @@ +assertEquals($items, $e->getCommands()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php new file mode 100755 index 000000000..ef789d8a9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php @@ -0,0 +1,17 @@ +setErrors($errors); + $this->assertEquals($errors, $e->getErrors()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php new file mode 100755 index 000000000..4ab423e85 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php @@ -0,0 +1,31 @@ + 'iterable_command', + 'parameters' => array( + 'page_size' => array('type' => 'integer'), + 'next_token' => array('type' => 'string') + ) + )); + } + + protected function build() + { + $this->request = $this->client->createRequest('GET'); + + // Add the next token and page size query string values + $this->request->getQuery()->set('next_token', $this->get('next_token')); + + if ($this->get('page_size')) { + $this->request->getQuery()->set('page_size', $this->get('page_size')); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php new file mode 100755 index 000000000..831a7e795 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php @@ -0,0 +1,32 @@ + get_called_class() == __CLASS__ ? 'mock_command' : 'sub.sub', + 'httpMethod' => 'POST', + 'parameters' => array( + 'test' => array( + 'default' => 123, + 'required' => true, + 'doc' => 'Test argument' + ), + '_internal' => array( + 'default' => 'abc' + ), + 'foo' => array('filters' => array('strtoupper')) + ) + )); + } + + protected function build() + { + $this->request = $this->client->createRequest(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php new file mode 100755 index 000000000..72ae1f6a7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php @@ -0,0 +1,30 @@ + 'other_command', + 'parameters' => array( + 'test' => array( + 'default' => '123', + 'required' => true, + 'doc' => 'Test argument' + ), + 'other' => array(), + 'arg' => array('type' => 'string'), + 'static' => array('static' => true, 'default' => 'this is static') + ) + )); + } + + protected function build() + { + $this->request = $this->client->getRequest('HEAD'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php new file mode 100755 index 000000000..d348480cf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php @@ -0,0 +1,7 @@ + '{scheme}://127.0.0.1:8124/{api_version}/{subdomain}', + 'scheme' => 'http', + 'api_version' => 'v1' + ), array('username', 'password', 'subdomain')); + + return new self($config->get('base_url'), $config); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php new file mode 100755 index 000000000..8faf4123a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php @@ -0,0 +1,42 @@ +nextToken) { + $this->command->set('next_token', $this->nextToken); + } + + $this->command->set('page_size', (int) $this->calculatePageSize()); + $this->command->execute(); + + $data = json_decode($this->command->getResponse()->getBody(true), true); + + $this->nextToken = $data['next_token']; + + return $data['resources']; + } + + public function next() + { + $this->calledNext++; + parent::next(); + } + + public function getResources() + { + return $this->resources; + } + + public function getIteratedCount() + { + return $this->iteratedCount; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php new file mode 100755 index 000000000..41c207372 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php @@ -0,0 +1,37 @@ +assertFalse($factory->canBuild($cmd)); + $factory->build($cmd); + } + + public function testBuildsResourceIterators() + { + $f1 = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $factory = new CompositeResourceIteratorFactory(array()); + $factory->addFactory($f1); + $command = new MockCommand(); + $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock')); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php new file mode 100755 index 000000000..d166e9261 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php @@ -0,0 +1,40 @@ +build(new MockCommand()); + } + + public function testBuildsResourceIterators() + { + $factory = new MapResourceIteratorFactory(array( + 'mock_command' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' + )); + $iterator = $factory->build(new MockCommand()); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testUsesWildcardMappings() + { + $factory = new MapResourceIteratorFactory(array( + '*' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' + )); + $iterator = $factory->build(new MockCommand()); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php new file mode 100755 index 000000000..7214133e5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php @@ -0,0 +1,65 @@ + 'object')); + $model = new Model(array('foo' => 'bar'), $param); + $this->assertSame($param, $model->getStructure()); + $this->assertEquals('bar', $model->get('foo')); + $this->assertEquals('bar', $model['foo']); + } + + public function testCanBeUsedWithoutStructure() + { + $model = new Model(array( + 'Foo' => 'baz', + 'Bar' => array( + 'Boo' => 'Bam' + ) + )); + $transform = function ($key, $value) { + return ($value && is_array($value)) ? new Collection($value) : $value; + }; + $model = $model->map($transform); + $this->assertInstanceOf('Guzzle\Common\Collection', $model->getPath('Bar')); + } + + public function testAllowsFiltering() + { + $model = new Model(array( + 'Foo' => 'baz', + 'Bar' => 'a' + )); + $model = $model->filter(function ($i, $v) { + return $v[0] == 'a'; + }); + $this->assertEquals(array('Bar' => 'a'), $model->toArray()); + } + + public function testDoesNotIncludeEmptyStructureInString() + { + $model = new Model(array('Foo' => 'baz')); + $str = (string) $model; + $this->assertContains('Debug output of model', $str); + $this->assertNotContains('Model structure', $str); + } + + public function testDoesIncludeModelStructureInString() + { + $model = new Model(array('Foo' => 'baz'), new Parameter(array('name' => 'Foo'))); + $str = (string) $model; + $this->assertContains('Debug output of Foo model', $str); + $this->assertContains('Model structure', $str); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php new file mode 100755 index 000000000..7b061b537 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php @@ -0,0 +1,41 @@ +registerNamespace('Baz'); + $command = new MockCommand(); + $factory->build($command); + } + + public function testBuildsResourceIterators() + { + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $command = new MockCommand(); + $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock')); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testChecksIfCanBuild() + { + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service'); + $this->assertFalse($factory->canBuild(new MockCommand())); + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $this->assertTrue($factory->canBuild(new MockCommand())); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php new file mode 100755 index 000000000..573fb6d6e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php @@ -0,0 +1,184 @@ +assertInternalType('array', ResourceIterator::getAllEvents()); + } + + public function testConstructorConfiguresDefaults() + { + $ri = $this->getMockForAbstractClass('Guzzle\\Service\\Resource\\ResourceIterator', array( + $this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), + array( + 'limit' => 10, + 'page_size' => 3 + ) + ), 'MockIterator'); + + $this->assertEquals(false, $ri->getNextToken()); + $this->assertEquals(false, $ri->current()); + } + + public function testSendsRequestsForNextSetOfResources() + { + // Queue up an array of responses for iterating + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }" + )); + + // Create a new resource iterator using the IterableCommand mock + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3 + )); + + // Ensure that no requests have been sent yet + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + + //$this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $ri->toArray(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[2]->getQuery()->get('page_size')); + + // Reset and resend + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", + )); + + $d = array(); + foreach ($ri as $data) { + $d[] = $data; + } + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $d); + } + + public function testCalculatesPageSize() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"j\", \"k\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3, + 'limit' => 7 + )); + + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(1, $requests[2]->getQuery()->get('page_size')); + } + + public function testUseAsArray() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"g\", \"h\", \"i\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the key is never < 0 + $this->assertEquals(0, $ri->key()); + $this->assertEquals(0, count($ri)); + + // Ensure that the iterator can be used as KVP array + $data = array(); + foreach ($ri as $key => $value) { + $data[$key] = $value; + } + + // Ensure that the iterate is countable + $this->assertEquals(6, count($ri)); + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i'), $data); + } + + public function testBailsWhenSendReturnsNoResults() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the iterator can be used as KVP array + $data = $ri->toArray(); + + // Ensure that the iterate is countable + $this->assertEquals(3, count($ri)); + $this->assertEquals(array('d', 'e', 'f'), $data); + + $this->assertEquals(2, $ri->getRequestCount()); + } + + public function testHoldsDataOptions() + { + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $this->assertNull($ri->get('foo')); + $this->assertSame($ri, $ri->set('foo', 'bar')); + $this->assertEquals('bar', $ri->get('foo')); + } + + public function testSettingLimitOrPageSizeClearsData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + + $ri->setLimit(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + $ri->setPageSize(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + } + + public function testWorksWithCustomAppendIterator() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }" + )); + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $a = new \Guzzle\Iterator\AppendIterator(); + $a->append($ri); + $results = iterator_to_array($a, false); + $this->assertEquals(4, $ri->calledNext); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php new file mode 100755 index 000000000..083aaa0d9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php @@ -0,0 +1,172 @@ +client = new Client($this->getServer()->getUrl()); + $this->factory = new PhpStreamRequestFactory(); + } + + public function testOpensValidStreamByCreatingContext() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->get('/'); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + $this->assertEquals(2, $stream->getSize()); + } + + public function testOpensValidStreamByPassingContextAndMerging() + { + $request = $this->client->get('/'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createContext', 'createStream')) + ->getMock(); + $this->factory->expects($this->never()) + ->method('createContext'); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + + $context = array('http' => array('method' => 'HEAD', 'ignore_errors' => false)); + $this->factory->fromRequest($request, stream_context_create($context)); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertEquals('HEAD', $options['http']['method']); + $this->assertFalse($options['http']['ignore_errors']); + $this->assertEquals('1.1', $options['http']['protocol_version']); + } + + public function testAppliesProxySettings() + { + $request = $this->client->get('/'); + $request->getCurlOptions()->set(CURLOPT_PROXY, 'tcp://foo.com'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertEquals('tcp://foo.com', $options['http']['proxy']); + } + + public function testAddsPostFields() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->post('/', array('Foo' => 'Bar'), array('foo' => 'baz bar')); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + + $received = $this->getServer()->getReceivedRequests(); + $this->assertEquals(1, count($received)); + $this->assertContains('POST / HTTP/1.1', $received[0]); + $this->assertContains('host: ', $received[0]); + $this->assertContains('user-agent: Guzzle/', $received[0]); + $this->assertContains('foo: Bar', $received[0]); + $this->assertContains('content-length: 13', $received[0]); + $this->assertContains('foo=baz%20bar', $received[0]); + } + + public function testAddsBody() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->put('/', array('Foo' => 'Bar'), 'Testing...123'); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + + $received = $this->getServer()->getReceivedRequests(); + $this->assertEquals(1, count($received)); + $this->assertContains('PUT / HTTP/1.1', $received[0]); + $this->assertContains('host: ', $received[0]); + $this->assertContains('user-agent: Guzzle/', $received[0]); + $this->assertContains('foo: Bar', $received[0]); + $this->assertContains('content-length: 13', $received[0]); + $this->assertContains('Testing...123', $received[0]); + } + + public function testCanDisableSslValidation() + { + $request = $this->client->get('/'); + $request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertFalse($options['ssl']['verify_peer']); + } + + public function testUsesSslValidationByDefault() + { + $request = $this->client->get('/'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertTrue($options['ssl']['verify_peer']); + $this->assertSame($request->getCurlOptions()->get(CURLOPT_CAINFO), $options['ssl']['cafile']); + } + + public function testBasicAuthAddsUserAndPassToUrl() + { + $request = $this->client->get('/'); + $request->setAuth('Foo', 'Bar'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $this->assertContains('Foo:Bar@', (string) $this->readAttribute($this->factory, 'url')); + } + + public function testCanCreateCustomStreamClass() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->get('/'); + $stream = $this->factory->fromRequest($request, array(), array('stream_class' => 'Guzzle\Http\EntityBody')); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $stream); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php new file mode 100755 index 000000000..4973f252e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php @@ -0,0 +1,189 @@ +assertEquals($handle, $stream->getStream()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isLocal()); + $this->assertTrue($stream->isSeekable()); + $this->assertEquals('PHP', $stream->getWrapper()); + $this->assertEquals('TEMP', $stream->getStreamType()); + $this->assertEquals(4, $stream->getSize()); + $this->assertEquals('php://temp', $stream->getUri()); + $this->assertEquals(array(), $stream->getWrapperData()); + $this->assertFalse($stream->isConsumed()); + unset($stream); + } + + public function testCanModifyStream() + { + $handle1 = fopen('php://temp', 'r+'); + $handle2 = fopen('php://temp', 'r+'); + $stream = new Stream($handle1); + $this->assertSame($handle1, $stream->getStream()); + $stream->setStream($handle2, 10); + $this->assertEquals(10, $stream->getSize()); + $this->assertSame($handle2, $stream->getStream()); + } + + public function testStreamClosesHandleOnDestruct() + { + $handle = fopen('php://temp', 'r'); + $stream = new Stream($handle); + unset($stream); + $this->assertFalse(is_resource($handle)); + } + + public function testConvertsToString() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('data', (string) $stream); + unset($stream); + + $handle = fopen(__DIR__ . '/../TestData/FileBody.txt', 'r'); + $stream = new Stream($handle); + $this->assertEquals('', (string) $stream); + unset($stream); + } + + public function testConvertsToStringAndRestoresCursorPos() + { + $handle = fopen('php://temp', 'w+'); + $stream = new Stream($handle); + $stream->write('foobazbar'); + $stream->seek(3); + $this->assertEquals('foobazbar', (string) $stream); + $this->assertEquals(3, $stream->ftell()); + } + + public function testIsConsumed() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertFalse($stream->isConsumed()); + $stream->read(4); + $this->assertTrue($stream->isConsumed()); + } + + public function testAllowsSettingManualSize() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $stream->setSize(10); + $this->assertEquals(10, $stream->getSize()); + unset($stream); + } + + public function testWrapsStream() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertTrue($stream->isSeekable()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('da', $stream->read(2)); + $this->assertEquals('ta', $stream->read(2)); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('data', $stream->read(4)); + $stream->write('_appended'); + $stream->seek(0); + $this->assertEquals('data_appended', $stream->read(13)); + } + + public function testGetSize() + { + $size = filesize(__DIR__ . '/../../../bootstrap.php'); + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals($handle, $stream->getStream()); + $this->assertEquals($size, $stream->getSize()); + $this->assertEquals($size, $stream->getSize()); + unset($stream); + + // Make sure that false is returned when the size cannot be determined + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $handle = fopen('http://127.0.0.1:' . $this->getServer()->getPort(), 'r'); + $stream = new Stream($handle); + $this->assertEquals(false, $stream->getSize()); + unset($stream); + } + + public function testEnsuresSizeIsConsistent() + { + $h = fopen('php://temp', 'r+'); + fwrite($h, 'foo'); + $stream = new Stream($h); + $this->assertEquals(3, $stream->getSize()); + $stream->write('test'); + $this->assertEquals(7, $stream->getSize()); + fclose($h); + } + + public function testAbstractsMetaData() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals('plainfile', $stream->getMetaData('wrapper_type')); + $this->assertEquals(null, $stream->getMetaData('wrapper_data')); + $this->assertInternalType('array', $stream->getMetaData()); + } + + public function testDoesNotAttemptToWriteToReadonlyStream() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals(0, $stream->write('foo')); + } + + public function testProvidesStreamPosition() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $stream->read(2); + $this->assertSame(ftell($handle), $stream->ftell()); + $this->assertEquals(2, $stream->ftell()); + } + + public function testRewindIsSeekZero() + { + $stream = new Stream(fopen('php://temp', 'w+')); + $stream->write('foobazbar'); + $this->assertTrue($stream->rewind()); + $this->assertEquals('foobazbar', $stream->read(9)); + } + + public function testCanDetachStream() + { + $r = fopen('php://temp', 'w+'); + $stream = new Stream($r); + $stream->detachStream(); + $this->assertNull($stream->getStream()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt new file mode 100755 index 000000000..e69de29bb diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json new file mode 100755 index 000000000..c354ed788 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json"] +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json new file mode 100755 index 000000000..765237b3e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json", "bar.json"] +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json new file mode 100755 index 000000000..cee5005b8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json @@ -0,0 +1,8 @@ +{ + "includes": ["recursive.json"], + "operations": { + "abstract": { + "httpMethod": "POST" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json new file mode 100755 index 000000000..c354ed788 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json"] +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response new file mode 100755 index 000000000..b6938a28c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Length: 0 + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json new file mode 100755 index 000000000..7b2a9dad4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json @@ -0,0 +1,18 @@ +{ + "includes": [ "json2.json" ], + "services": { + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json new file mode 100755 index 000000000..08e5566f0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json @@ -0,0 +1,11 @@ +{ + "services": { + "foo": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "baz": "bar" + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json new file mode 100755 index 000000000..25452e4a4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json @@ -0,0 +1,71 @@ +{ + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + }, + + "test.abstract.aws": { + "params": { + "access_key": "12345", + "secret_key": "abcd" + } + }, + + "test.s3": { + "class": "Guzzle\\Service\\Aws\\S3Client", + "extends": "test.abstract.aws", + "params": { + "devpay_product_token": "", + "devpay_user_token": "" + } + }, + + "test.simple_db": { + "class": "Guzzle\\Service\\Aws\\SimpleDb\\SimpleDbClient", + "extends": "test.abstract.aws" + }, + + "test.sqs": { + "class": "Guzzle\\Service\\Aws\\Sqs\\SqsClient", + "extends": "test.abstract.aws" + }, + + "test.centinel": { + "class": "Guzzle\\Service\\CardinalCommerce\\Centinel.CentinelClient", + "params": { + "password": "test", + "processor_id": "123", + "merchant_id": "456" + } + }, + + "test.mws": { + "class": "Guzzle\\Service\\Mws\\MwsClient", + "extends": "test.abstract.aws", + "params": { + "merchant_id": "ABCDE", + "marketplace_id": "FGHIJ", + "application_name": "GuzzleTest", + "application_version": "0.1", + "base_url": "https://mws.amazonservices.com" + } + }, + + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "params": { + "username": "test_user", + "password": "****", + "subdomain": "test" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json new file mode 100755 index 000000000..01557ca11 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json @@ -0,0 +1,40 @@ +{ + "includes": [ "test_service2.json" ], + "operations": { + "test": { + "uri": "/path" + }, + "concrete": { + "extends": "abstract" + }, + "foo_bar": { + "uri": "/testing", + "parameters": { + "other": { + "location": "json", + "location_key": "Other" + }, + "test": { + "type": "object", + "location": "json", + "properties": { + "baz": { + "type": "boolean", + "default": true + }, + "bar": { + "type": "string", + "filters": [ + { + "method": "strtolower", + "args": ["test", "@value"] + }, + "strtoupper" + ] + } + } + } + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json new file mode 100755 index 000000000..66dd9ef7e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json @@ -0,0 +1,7 @@ +{ + "operations": { + "abstract": { + "uri": "/abstract" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json new file mode 100755 index 000000000..ae2ae0bd4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json @@ -0,0 +1,40 @@ +{ + "includes": [ "test_service2.json" ], + "operations": { + "test": { + "uri": "/path" + }, + "concrete": { + "extends": "abstract" + }, + "baz_qux": { + "uri": "/testing", + "parameters": { + "other": { + "location": "json", + "location_key": "Other" + }, + "test": { + "type": "object", + "location": "json", + "properties": { + "baz": { + "type": "boolean", + "default": true + }, + "bar": { + "type": "string", + "filters": [ + { + "method": "strtolower", + "args": ["test", "@value"] + }, + "strtoupper" + ] + } + } + } + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/bootstrap.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/bootstrap.php new file mode 100755 index 000000000..28908d310 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzle/guzzle/tests/bootstrap.php @@ -0,0 +1,10 @@ +` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +## 3.7.0 - 2013-06-10 + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc.). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc.). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +## 3.6.0 - 2013-05-29 + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +## 3.5.0 - 2013-05-13 + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +## 3.4.3 - 2013-04-30 + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +## 3.4.2 - 2013-04-29 + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +## 3.4.1 - 2013-04-16 + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +## 3.4.0 - 2013-04-11 + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +## 3.3.1 - 2013-03-10 + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +## 3.3.0 - 2013-03-03 + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +## 3.2.0 - 2013-02-14 + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +## 3.1.2 - 2013-01-27 + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +## 3.1.1 - 2013-01-20 + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +## 3.1.0 - 2013-01-12 + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +## 3.0.7 - 2012-12-19 + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +## 3.0.6 - 2012-12-09 + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +## 3.0.5 - 2012-11-18 + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +## 3.0.4 - 2012-11-11 + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +## 3.0.3 - 2012-11-04 + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +## 3.0.2 - 2012-10-25 + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +## 3.0.1 - 2012-10-22 + +* Models can now be used like regular collection objects by calling filter, map, etc. +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +## 3.0.0 - 2012-10-15 + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +## 2.8.8 - 2012-10-15 + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +## 2.8.7 - 2012-09-30 + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +## 2.8.6 - 2012-09-05 + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +## 2.8.5 - 2012-08-29 + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +## 2.8.4 - 2012-08-15 + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +## 2.8.3 - 2012-07-30 + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +## 2.8.2 - 2012-07-24 + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()` +* Bug: `+` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +## 2.8.1 - 2012-07-16 + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +## 2.8.0 - 2012-07-15 + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +## 2.7.2 - 2012-07-02 + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +## 2.7.1 - 2012-06-26 + +* Minor patch to update version number in UA string +* Updating build process + +## 2.7.0 - 2012-06-25 + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +## 2.6.6 - 2012-06-10 + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +## 2.6.5 - 2012-06-03 + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +## 2.6.4 - 2012-05-30 + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +## 2.6.3 - 2012-05-23 + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +## 2.6.2 - 2012-05-19 + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +## 2.6.1 - 2012-05-19 + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +## 2.6.0 - 2012-05-15 + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +## 2.5.0 - 2012-05-08 + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/LICENSE new file mode 100755 index 000000000..71d3b783c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/Makefile b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/Makefile new file mode 100755 index 000000000..69bf3273e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/Makefile @@ -0,0 +1,50 @@ +all: clean coverage docs + +start-server: + cd vendor/guzzlehttp/ringphp && make start-server + +stop-server: + cd vendor/guzzlehttp/ringphp && make stop-server + +test: start-server + vendor/bin/phpunit + $(MAKE) stop-server + +coverage: start-server + vendor/bin/phpunit --coverage-html=artifacts/coverage + $(MAKE) stop-server + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* + +docs: + cd docs && make html && cd .. + +view-docs: + open docs/_build/html/index.html + +tag: + $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) + @echo Tagging $(TAG) + chag update $(TAG) + sed -i '' -e "s/VERSION = '.*'/VERSION = '$(TAG)'/" src/ClientInterface.php + php -l src/ClientInterface.php + git add -A + git commit -m '$(TAG) release' + chag tag + +perf: start-server + php tests/perf.php + $(MAKE) stop-server + +package: burgomaster + php build/packager.php + +burgomaster: + mkdir -p build/artifacts + curl -s https://raw.githubusercontent.com/mtdowling/Burgomaster/0.0.2/src/Burgomaster.php > build/artifacts/Burgomaster.php + +.PHONY: docs burgomaster diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/README.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/README.md new file mode 100755 index 000000000..d41e7e758 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/README.md @@ -0,0 +1,70 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +[![Build Status](https://secure.travis-ci.org/guzzle/guzzle.svg?branch=master)](http://travis-ci.org/guzzle/guzzle) + +Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and +trivial to integrate with web services. + +- Manages things like persistent connections, represents query strings as + collections, simplifies sending streaming POST requests with fields and + files, and abstracts away the underlying HTTP transport layer. +- Can send both synchronous and asynchronous requests using the same interface + without requiring a dependency on a specific event loop. +- Pluggable HTTP adapters allows Guzzle to integrate with any method you choose + for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream + wrapper, non-blocking event loops like ReactPHP. +- Guzzle makes it so that you no longer need to fool around with cURL options, + stream contexts, or sockets. + +```php +$client = new GuzzleHttp\Client(); +$response = $client->get('http://guzzlephp.org'); +$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); +echo $res->getStatusCode(); +// "200" +echo $res->getHeader('content-type'); +// 'application/json; charset=utf8' +echo $res->getBody(); +// {"type":"User"...' +var_export($res->json()); +// Outputs the JSON decoded data + +// Send an asynchronous request. +$req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]); +$client->send($req)->then(function ($response) { + echo 'I completed! ' . $response; +}); +``` + +Get more information and answers with the +[Documentation](http://guzzlephp.org/), +[Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), +and [Gitter](https://gitter.im/guzzle/guzzle). + +### Installing via Composer + +The recommended way to install Guzzle is through +[Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php +``` + +Next, run the Composer command to install the latest stable version of Guzzle: + +```bash +composer.phar require guzzlehttp/guzzle +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` + +### Documentation + +More information can be found in the online documentation at +http://guzzlephp.org/. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/UPGRADING.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/UPGRADING.md new file mode 100755 index 000000000..2b3877fa8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -0,0 +1,1050 @@ +Guzzle Upgrade Guide +==================== + +4.x to 5.0 +---------- + +## Rewritten Adapter Layer + +Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send +HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor +is still supported, but it has now been renamed to `handler`. Instead of +passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP +`callable` that follows the RingPHP specification. + +## Removed Fluent Interfaces + +[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil) +from the following classes: + +- `GuzzleHttp\Collection` +- `GuzzleHttp\Url` +- `GuzzleHttp\Query` +- `GuzzleHttp\Post\PostBody` +- `GuzzleHttp\Cookie\SetCookie` + +## Removed functions.php + +Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following +functions can be used as replacements. + +- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode` +- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath` +- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path` +- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however, + deprecated in favor of using `GuzzleHttp\Pool::batch()`. + +The "procedural" global client has been removed with no replacement (e.g., +`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client` +object as a replacement. + +## `throwImmediately` has been removed + +The concept of "throwImmediately" has been removed from exceptions and error +events. This control mechanism was used to stop a transfer of concurrent +requests from completing. This can now be handled by throwing the exception or +by cancelling a pool of requests or each outstanding future request +individually. + +## headers event has been removed + +Removed the "headers" event. This event was only useful for changing the +body a response once the headers of the response were known. You can implement +a similar behavior in a number of ways. One example might be to use a +FnStream that has access to the transaction being sent. For example, when the +first byte is written, you could check if the response headers match your +expectations, and if so, change the actual stream body that is being +written to. + +## Updates to HTTP Messages + +Removed the `asArray` parameter from +`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header +value as an array, then use the newly added `getHeaderAsArray()` method of +`MessageInterface`. This change makes the Guzzle interfaces compatible with +the PSR-7 interfaces. + +3.x to 4.0 +---------- + +## Overarching changes: + +- Now requires PHP 5.4 or greater. +- No longer requires cURL to send requests. +- Guzzle no longer wraps every exception it throws. Only exceptions that are + recoverable are now wrapped by Guzzle. +- Various namespaces have been removed or renamed. +- No longer requiring the Symfony EventDispatcher. A custom event dispatcher + based on the Symfony EventDispatcher is + now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant + speed and functionality improvements). + +Changes per Guzzle 3.x namespace are described below. + +## Batch + +The `Guzzle\Batch` namespace has been removed. This is best left to +third-parties to implement on top of Guzzle's core HTTP library. + +## Cache + +The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement +has been implemented yet, but hoping to utilize a PSR cache interface). + +## Common + +- Removed all of the wrapped exceptions. It's better to use the standard PHP + library for unrecoverable exceptions. +- `FromConfigInterface` has been removed. +- `Guzzle\Common\Version` has been removed. The VERSION constant can be found + at `GuzzleHttp\ClientInterface::VERSION`. + +### Collection + +- `getAll` has been removed. Use `toArray` to convert a collection to an array. +- `inject` has been removed. +- `keySearch` has been removed. +- `getPath` no longer supports wildcard expressions. Use something better like + JMESPath for this. +- `setPath` now supports appending to an existing array via the `[]` notation. + +### Events + +Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses +`GuzzleHttp\Event\Emitter`. + +- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by + `GuzzleHttp\Event\EmitterInterface`. +- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by + `GuzzleHttp\Event\Emitter`. +- `Symfony\Component\EventDispatcher\Event` is replaced by + `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in + `GuzzleHttp\Event\EventInterface`. +- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and + `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the + event emitter of a request, client, etc. now uses the `getEmitter` method + rather than the `getDispatcher` method. + +#### Emitter + +- Use the `once()` method to add a listener that automatically removes itself + the first time it is invoked. +- Use the `listeners()` method to retrieve a list of event listeners rather than + the `getListeners()` method. +- Use `emit()` instead of `dispatch()` to emit an event from an emitter. +- Use `attach()` instead of `addSubscriber()` and `detach()` instead of + `removeSubscriber()`. + +```php +$mock = new Mock(); +// 3.x +$request->getEventDispatcher()->addSubscriber($mock); +$request->getEventDispatcher()->removeSubscriber($mock); +// 4.x +$request->getEmitter()->attach($mock); +$request->getEmitter()->detach($mock); +``` + +Use the `on()` method to add a listener rather than the `addListener()` method. + +```php +// 3.x +$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } ); +// 4.x +$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } ); +``` + +## Http + +### General changes + +- The cacert.pem certificate has been moved to `src/cacert.pem`. +- Added the concept of adapters that are used to transfer requests over the + wire. +- Simplified the event system. +- Sending requests in parallel is still possible, but batching is no longer a + concept of the HTTP layer. Instead, you must use the `complete` and `error` + events to asynchronously manage parallel request transfers. +- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`. +- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`. +- QueryAggregators have been rewritten so that they are simply callable + functions. +- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in + `functions.php` for an easy to use static client instance. +- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from + `GuzzleHttp\Exception\TransferException`. + +### Client + +Calling methods like `get()`, `post()`, `head()`, etc. no longer create and +return a request, but rather creates a request, sends the request, and returns +the response. + +```php +// 3.0 +$request = $client->get('/'); +$response = $request->send(); + +// 4.0 +$response = $client->get('/'); + +// or, to mirror the previous behavior +$request = $client->createRequest('GET', '/'); +$response = $client->send($request); +``` + +`GuzzleHttp\ClientInterface` has changed. + +- The `send` method no longer accepts more than one request. Use `sendAll` to + send multiple requests in parallel. +- `setUserAgent()` has been removed. Use a default request option instead. You + could, for example, do something like: + `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`. +- `setSslVerification()` has been removed. Use default request options instead, + like `$client->setConfig('defaults/verify', true)`. + +`GuzzleHttp\Client` has changed. + +- The constructor now accepts only an associative array. You can include a + `base_url` string or array to use a URI template as the base URL of a client. + You can also specify a `defaults` key that is an associative array of default + request options. You can pass an `adapter` to use a custom adapter, + `batch_adapter` to use a custom adapter for sending requests in parallel, or + a `message_factory` to change the factory used to create HTTP requests and + responses. +- The client no longer emits a `client.create_request` event. +- Creating requests with a client no longer automatically utilize a URI + template. You must pass an array into a creational method (e.g., + `createRequest`, `get`, `put`, etc.) in order to expand a URI template. + +### Messages + +Messages no longer have references to their counterparts (i.e., a request no +longer has a reference to it's response, and a response no loger has a +reference to its request). This association is now managed through a +`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to +these transaction objects using request events that are emitted over the +lifecycle of a request. + +#### Requests with a body + +- `GuzzleHttp\Message\EntityEnclosingRequest` and + `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The + separation between requests that contain a body and requests that do not + contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface` + handles both use cases. +- Any method that previously accepts a `GuzzleHttp\Response` object now accept a + `GuzzleHttp\Message\ResponseInterface`. +- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to + `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create + both requests and responses and is implemented in + `GuzzleHttp\Message\MessageFactory`. +- POST field and file methods have been removed from the request object. You + must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface` + to control the format of a POST body. Requests that are created using a + standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use + a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if + the method is POST and no body is provided. + +```php +$request = $client->createRequest('POST', '/'); +$request->getBody()->setField('foo', 'bar'); +$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r'))); +``` + +#### Headers + +- `GuzzleHttp\Message\Header` has been removed. Header values are now simply + represented by an array of values or as a string. Header values are returned + as a string by default when retrieving a header value from a message. You can + pass an optional argument of `true` to retrieve a header value as an array + of strings instead of a single concatenated string. +- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to + `GuzzleHttp\Post`. This interface has been simplified and now allows the + addition of arbitrary headers. +- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most + of the custom headers are now handled separately in specific + subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has + been updated to properly handle headers that contain parameters (like the + `Link` header). + +#### Responses + +- `GuzzleHttp\Message\Response::getInfo()` and + `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event + system to retrieve this type of information. +- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed. +- `GuzzleHttp\Message\Response::getMessage()` has been removed. +- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific + methods have moved to the CacheSubscriber. +- Header specific helper functions like `getContentMd5()` have been removed. + Just use `getHeader('Content-MD5')` instead. +- `GuzzleHttp\Message\Response::setRequest()` and + `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event + system to work with request and response objects as a transaction. +- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the + Redirect subscriber instead. +- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have + been removed. Use `getStatusCode()` instead. + +#### Streaming responses + +Streaming requests can now be created by a client directly, returning a +`GuzzleHttp\Message\ResponseInterface` object that contains a body stream +referencing an open PHP HTTP stream. + +```php +// 3.0 +use Guzzle\Stream\PhpStreamRequestFactory; +$request = $client->get('/'); +$factory = new PhpStreamRequestFactory(); +$stream = $factory->fromRequest($request); +$data = $stream->read(1024); + +// 4.0 +$response = $client->get('/', ['stream' => true]); +// Read some data off of the stream in the response body +$data = $response->getBody()->read(1024); +``` + +#### Redirects + +The `configureRedirects()` method has been removed in favor of a +`allow_redirects` request option. + +```php +// Standard redirects with a default of a max of 5 redirects +$request = $client->createRequest('GET', '/', ['allow_redirects' => true]); + +// Strict redirects with a custom number of redirects +$request = $client->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 5, 'strict' => true] +]); +``` + +#### EntityBody + +EntityBody interfaces and classes have been removed or moved to +`GuzzleHttp\Stream`. All classes and interfaces that once required +`GuzzleHttp\EntityBodyInterface` now require +`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no +longer uses `GuzzleHttp\EntityBody::factory` but now uses +`GuzzleHttp\Stream\Stream::factory` or even better: +`GuzzleHttp\Stream\create()`. + +- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface` +- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream` +- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream` +- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream` +- `Guzzle\Http\IoEmittyinEntityBody` has been removed. + +#### Request lifecycle events + +Requests previously submitted a large number of requests. The number of events +emitted over the lifecycle of a request has been significantly reduced to make +it easier to understand how to extend the behavior of a request. All events +emitted during the lifecycle of a request now emit a custom +`GuzzleHttp\Event\EventInterface` object that contains context providing +methods and a way in which to modify the transaction at that specific point in +time (e.g., intercept the request and set a response on the transaction). + +- `request.before_send` has been renamed to `before` and now emits a + `GuzzleHttp\Event\BeforeEvent` +- `request.complete` has been renamed to `complete` and now emits a + `GuzzleHttp\Event\CompleteEvent`. +- `request.sent` has been removed. Use `complete`. +- `request.success` has been removed. Use `complete`. +- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`. +- `request.exception` has been removed. Use `error`. +- `request.receive.status_line` has been removed. +- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to + maintain a status update. +- `curl.callback.write` has been removed. Use a custom `StreamInterface` to + intercept writes. +- `curl.callback.read` has been removed. Use a custom `StreamInterface` to + intercept reads. + +`headers` is a new event that is emitted after the response headers of a +request have been received before the body of the response is downloaded. This +event emits a `GuzzleHttp\Event\HeadersEvent`. + +You can intercept a request and inject a response using the `intercept()` event +of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and +`GuzzleHttp\Event\ErrorEvent` event. + +See: http://docs.guzzlephp.org/en/latest/events.html + +## Inflection + +The `Guzzle\Inflection` namespace has been removed. This is not a core concern +of Guzzle. + +## Iterator + +The `Guzzle\Iterator` namespace has been removed. + +- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and + `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of + Guzzle itself. +- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent + class is shipped with PHP 5.4. +- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because + it's easier to just wrap an iterator in a generator that maps values. + +For a replacement of these iterators, see https://github.com/nikic/iter + +## Log + +The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The +`Guzzle\Log` namespace has been removed. Guzzle now relies on +`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been +moved to `GuzzleHttp\Subscriber\Log\Formatter`. + +## Parser + +The `Guzzle\Parser` namespace has been removed. This was previously used to +make it possible to plug in custom parsers for cookies, messages, URI +templates, and URLs; however, this level of complexity is not needed in Guzzle +so it has been removed. + +- Cookie: Cookie parsing logic has been moved to + `GuzzleHttp\Cookie\SetCookie::fromString`. +- Message: Message parsing logic for both requests and responses has been moved + to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only + used in debugging or deserializing messages, so it doesn't make sense for + Guzzle as a library to add this level of complexity to parsing messages. +- UriTemplate: URI template parsing has been moved to + `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL + URI template library if it is installed. +- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously + it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary, + then developers are free to subclass `GuzzleHttp\Url`. + +## Plugin + +The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`. +Several plugins are shipping with the core Guzzle library under this namespace. + +- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar + code has moved to `GuzzleHttp\Cookie`. +- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin. +- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is + received. +- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin. +- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before + sending. This subscriber is attached to all requests by default. +- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin. + +The following plugins have been removed (third-parties are free to re-implement +these if needed): + +- `GuzzleHttp\Plugin\Async` has been removed. +- `GuzzleHttp\Plugin\CurlAuth` has been removed. +- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This + functionality should instead be implemented with event listeners that occur + after normal response parsing occurs in the guzzle/command package. + +The following plugins are not part of the core Guzzle package, but are provided +in separate repositories: + +- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be muchs simpler + to build custom retry policies using simple functions rather than various + chained classes. See: https://github.com/guzzle/retry-subscriber +- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to + https://github.com/guzzle/cache-subscriber +- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to + https://github.com/guzzle/log-subscriber +- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to + https://github.com/guzzle/message-integrity-subscriber +- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to + `GuzzleHttp\Subscriber\MockSubscriber`. +- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to + https://github.com/guzzle/oauth-subscriber + +## Service + +The service description layer of Guzzle has moved into two separate packages: + +- http://github.com/guzzle/command Provides a high level abstraction over web + services by representing web service operations using commands. +- http://github.com/guzzle/guzzle-services Provides an implementation of + guzzle/command that provides request serialization and response parsing using + Guzzle service descriptions. + +## Stream + +Stream have moved to a separate package available at +https://github.com/guzzle/streams. + +`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take +on the responsibilities of `Guzzle\Http\EntityBody` and +`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number +of methods implemented by the `StreamInterface` has been drastically reduced to +allow developers to more easily extend and decorate stream behavior. + +## Removed methods from StreamInterface + +- `getStream` and `setStream` have been removed to better encapsulate streams. +- `getMetadata` and `setMetadata` have been removed in favor of + `GuzzleHttp\Stream\MetadataStreamInterface`. +- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been + removed. This data is accessible when + using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`. +- `rewind` has been removed. Use `seek(0)` for a similar behavior. + +## Renamed methods + +- `detachStream` has been renamed to `detach`. +- `feof` has been renamed to `eof`. +- `ftell` has been renamed to `tell`. +- `readLine` has moved from an instance method to a static class method of + `GuzzleHttp\Stream\Stream`. + +## Metadata streams + +`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams +that contain additional metadata accessible via `getMetadata()`. +`GuzzleHttp\Stream\StreamInterface::getMetadata` and +`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed. + +## StreamRequestFactory + +The entire concept of the StreamRequestFactory has been removed. The way this +was used in Guzzle 3 broke the actual interface of sending streaming requests +(instead of getting back a Response, you got a StreamInterface). Streeaming +PHP requests are now implemented throught the `GuzzleHttp\Adapter\StreamAdapter`. + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc.). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml + + + + + + Get a list of groups + + + Uses a search query to get a list of groups + + + + Create a group + + + + + Delete a group by ID + + + + + + + Update a group + + + + + + +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/build/packager.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/build/packager.php new file mode 100755 index 000000000..724bf6349 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/build/packager.php @@ -0,0 +1,21 @@ +deepCopy($file, $file); +} + +// Copy each dependency to the staging directory. Copy *.php and *.pem files. +$packager->recursiveCopy('src', 'GuzzleHttp', ['php']); +$packager->recursiveCopy('vendor/react/promise/src', 'React/Promise'); +$packager->recursiveCopy('vendor/guzzlehttp/ringphp/src', 'GuzzleHttp/Ring'); +$packager->recursiveCopy('vendor/guzzlehttp/streams/src', 'GuzzleHttp/Stream'); +$packager->createAutoloader(['React/Promise/functions.php']); +$packager->createPhar(__DIR__ . '/artifacts/guzzle.phar'); +$packager->createZip(__DIR__ . '/artifacts/guzzle.zip'); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/composer.json new file mode 100755 index 000000000..6ec7dad6a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/composer.json @@ -0,0 +1,39 @@ +{ + "name": "guzzlehttp/guzzle", + "type": "library", + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0", + "guzzlehttp/ringphp": "^1.1" + }, + "require-dev": { + "ext-curl": "*", + "psr/log": "^1.0", + "phpunit/phpunit": "^4.0" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/Makefile b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/Makefile new file mode 100755 index 000000000..d92e03f95 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png new file mode 100755 index 0000000000000000000000000000000000000000..f1017f7e6028c14a9e0694c66a6cfbb2d546adf5 GIT binary patch literal 803 zcmV+;1Kj+HP)@ zosv>1reIXPlo1y){RjSw2_NWGX5O-#W+NK3tBQ_IN%bh7xZ1Yehws80|4azI;aWIJ z_%xhlcTubTO7Dbx z)F-R8gg5MzGv|t4=e_El4GCwW0m6?C;0bG4DRC^TH6-pa>y8_h*QBud6Ms>Qf{oN> z=Q($Dn|DINB{`Ea{)h&^x;i{)XQ{?Z&id71eOkRPqgl17&E%|dJIC&SCwnd zY4oUR`OVorB8A||QZjLWS~cr&OD?HEtM@^bIovNUC5An6?z`xDgJL2H2Mya@dku<1YUfi&QvS8KS8=~uOs!oaF z8OMF7-5yyh}yDkaCp7Ob8b;wv(27WLL#lglguF0fh3d(d@ zP%vrDIA~G}dL)X;YnCMSE4ZM-gfVsYTLItd3J`~_vw^k=W%C_MlG002ovPDHLkV1oLqbt3=( literal 0 HcmV?d00001 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_static/logo.png b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_static/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..965a4ef4139180ea7d33594feba2394dfce6774a GIT binary patch literal 247678 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%0ARzX0ax0O>7%athq>_G8o_) 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 Q^3R@b2)1Zk0&5+MNyUSR-V=Sej)GD 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�{XQapLL3s@DiJOewb9vk!Lb} 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

    kl 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

    2-cn6gsGIE2zEilUdg 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

    Z5%j-C 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%0A=e`l9AQljxQ1J>rUJsDX<`+@ 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 literal 0 HcmV?d00001 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html new file mode 100755 index 000000000..7950a0f8e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html @@ -0,0 +1,3 @@ +

  • GitHub
  • +
  • Forum
  • +
  • IRC
  • diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/clients.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/clients.rst new file mode 100755 index 000000000..35a9806f9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/clients.rst @@ -0,0 +1,1326 @@ +======= +Clients +======= + +Clients are used to create requests, create transactions, send requests +through an HTTP handler, and return a response. You can add default request +options to a client that are applied to every request (e.g., default headers, +default query string parameters, etc.), and you can add event listeners and +subscribers to every request created by a client. + +Creating a client +================= + +The constructor of a client accepts an associative array of configuration +options. + +base_url + Configures a base URL for the client so that requests created + using a relative URL are combined with the ``base_url`` of the client + according to section `5.2 of RFC 3986 `_. + + .. code-block:: php + + // Create a client with a base URL + $client = new GuzzleHttp\Client(['base_url' => 'https://github.com']); + // Send a request to https://github.com/notifications + $response = $client->get('/notifications'); + + Don't feel like reading RFC 3986? Here are some quick examples on how a + ``base_url`` is resolved with another URI. + + ======================= ================== =============================== + base_url URI Result + ======================= ================== =============================== + ``http://foo.com`` ``/bar`` ``http://foo.com/bar`` + ``http://foo.com/foo`` ``/bar`` ``http://foo.com/bar`` + ``http://foo.com/foo`` ``bar`` ``http://foo.com/bar`` + ``http://foo.com/foo/`` ``bar`` ``http://foo.com/foo/bar`` + ``http://foo.com`` ``http://baz.com`` ``http://baz.com`` + ``http://foo.com/?bar`` ``bar`` ``http://foo.com/bar`` + ======================= ================== =============================== + +handler + Configures the `RingPHP handler `_ + used to transfer the HTTP requests of a client. Guzzle will, by default, + utilize a stacked handlers that chooses the best handler to use based on the + provided request options and based on the extensions available in the + environment. + +message_factory + Specifies the factory used to create HTTP requests and responses + (``GuzzleHttp\Message\MessageFactoryInterface``). + +defaults + Associative array of :ref:`request-options` that are applied to every + request created by the client. This allows you to specify things like + default headers (e.g., User-Agent), default query string parameters, SSL + configurations, and any other supported request options. + +emitter + Specifies an event emitter (``GuzzleHttp\Event\EmitterInterface``) instance + to be used by the client to emit request events. This option is useful if + you need to inject an emitter with listeners/subscribers already attached. + +Here's an example of creating a client with various options. + +.. code-block:: php + + use GuzzleHttp\Client; + + $client = new Client([ + 'base_url' => ['https://api.twitter.com/{version}/', ['version' => 'v1.1']], + 'defaults' => [ + 'headers' => ['Foo' => 'Bar'], + 'query' => ['testing' => '123'], + 'auth' => ['username', 'password'], + 'proxy' => 'tcp://localhost:80' + ] + ]); + +Sending Requests +================ + +Requests can be created using various methods of a client. You can create +**and** send requests using one of the following methods: + +- ``GuzzleHttp\Client::get``: Sends a GET request. +- ``GuzzleHttp\Client::head``: Sends a HEAD request +- ``GuzzleHttp\Client::post``: Sends a POST request +- ``GuzzleHttp\Client::put``: Sends a PUT request +- ``GuzzleHttp\Client::delete``: Sends a DELETE request +- ``GuzzleHttp\Client::options``: Sends an OPTIONS request + +Each of the above methods accepts a URL as the first argument and an optional +associative array of :ref:`request-options` as the second argument. + +Synchronous Requests +-------------------- + +Guzzle sends synchronous (blocking) requests when the ``future`` request option +is not specified. This means that the request will complete immediately, and if +an error is encountered, a ``GuzzleHttp\Exception\RequestException`` will be +thrown. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + + $client->put('http://httpbin.org', [ + 'headers' => ['X-Foo' => 'Bar'], + 'body' => 'this is the body!', + 'save_to' => '/path/to/local/file', + 'allow_redirects' => false, + 'timeout' => 5 + ]); + +Synchronous Error Handling +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a recoverable error is encountered while calling the ``send()`` method of +a client, a ``GuzzleHttp\Exception\RequestException`` is thrown. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Exception\RequestException; + + $client = new Client(); + + try { + $client->get('http://httpbin.org'); + } catch (RequestException $e) { + echo $e->getRequest() . "\n"; + if ($e->hasResponse()) { + echo $e->getResponse() . "\n"; + } + } + +``GuzzleHttp\Exception\RequestException`` always contains a +``GuzzleHttp\Message\RequestInterface`` object that can be accessed using the +exception's ``getRequest()`` method. + +A response might be present in the exception. In the event of a networking +error, no response will be received. You can check if a ``RequestException`` +has a response using the ``hasResponse()`` method. If the exception has a +response, then you can access the associated +``GuzzleHttp\Message\ResponseInterface`` using the ``getResponse()`` method of +the exception. + +Asynchronous Requests +--------------------- + +You can send asynchronous requests by setting the ``future`` request option +to ``true`` (or a string that your handler understands). This creates a +``GuzzleHttp\Message\FutureResponse`` object that has not yet completed. Once +you have a future response, you can use a promise object obtained by calling +the ``then`` method of the response to take an action when the response has +completed or encounters an error. + +.. code-block:: php + + $response = $client->put('http://httpbin.org/get', ['future' => true]); + + // Call the function when the response completes + $response->then(function ($response) { + echo $response->getStatusCode(); + }); + +You can call the ``wait()`` method of a future response to block until it has +completed. You also use a future response object just like a normal response +object by accessing the methods of the response. Using a future response like a +normal response object, also known as *dereferencing*, will block until the +response has completed. + +.. code-block:: php + + $response = $client->put('http://httpbin.org/get', ['future' => true]); + + // Block until the response has completed + echo $response->getStatusCode(); + +.. important:: + + If an exception occurred while transferring the future response, then the + exception encountered will be thrown when dereferencing. + +.. note:: + + It depends on the RingPHP handler used by a client, but you typically need + to use the same RingPHP handler in order to utilize asynchronous requests + across multiple clients. + +Asynchronous Error Handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Handling errors with future response object promises is a bit different. When +using a promise, exceptions are forwarded to the ``$onError`` function provided +to the second argument of the ``then()`` function. + +.. code-block:: php + + $response = $client->put('http://httpbin.org/get', ['future' => true]); + + $response + ->then( + function ($response) { + // This is called when the request succeeded + echo 'Success: ' . $response->getStatusCode(); + // Returning a value will forward the value to the next promise + // in the chain. + return $response; + }, + function ($error) { + // This is called when the exception failed. + echo 'Exception: ' . $error->getMessage(); + // Throwing will "forward" the exception to the next promise + // in the chain. + throw $error; + } + ) + ->then( + function($response) { + // This is called after the first promise in the chain. It + // receives the value returned from the first promise. + echo $response->getReasonPhrase(); + }, + function ($error) { + // This is called if the first promise error handler in the + // chain rethrows the exception. + echo 'Error: ' . $error->getMessage(); + } + ); + +Please see the `React/Promises project documentation `_ +for more information on how promise resolution and rejection forwarding works. + +HTTP Errors +----------- + +If the ``exceptions`` request option is not set to ``false``, then exceptions +are thrown for HTTP protocol errors as well: +``GuzzleHttp\Exception\ClientErrorResponseException`` for 4xx level HTTP +responses and ``GuzzleHttp\Exception\ServerException`` for 5xx level responses, +both of which extend from ``GuzzleHttp\Exception\BadResponseException``. + +Creating Requests +----------------- + +You can create a request without sending it. This is useful for building up +requests over time or sending requests in concurrently. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org', [ + 'headers' => ['X-Foo' => 'Bar'] + ]); + + // Modify the request as needed + $request->setHeader('Baz', 'bar'); + +After creating a request, you can send it with the client's ``send()`` method. + +.. code-block:: php + + $response = $client->send($request); + +Sending Requests With a Pool +============================ + +You can send requests concurrently using a fixed size pool via the +``GuzzleHttp\Pool`` class. The Pool class is an implementation of +``GuzzleHttp\Ring\Future\FutureInterface``, meaning it can be dereferenced at a +later time or cancelled before sending. The Pool constructor accepts a client +object, iterator or array that yields ``GuzzleHttp\Message\RequestInterface`` +objects, and an optional associative array of options that can be used to +affect the transfer. + +.. code-block:: php + + use GuzzleHttp\Pool; + + $requests = [ + $client->createRequest('GET', 'http://httpbin.org'), + $client->createRequest('DELETE', 'http://httpbin.org/delete'), + $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'test']) + ]; + + $options = []; + + // Create a pool. Note: the options array is optional. + $pool = new Pool($client, $requests, $options); + + // Send the requests + $pool->wait(); + +The Pool constructor accepts the following associative array of options: + +- **pool_size**: Integer representing the maximum number of requests that are + allowed to be sent concurrently. +- **before**: Callable or array representing the event listeners to add to + each request's :ref:`before_event` event. +- **complete**: Callable or array representing the event listeners to add to + each request's :ref:`complete_event` event. +- **error**: Callable or array representing the event listeners to add to + each request's :ref:`error_event` event. +- **end**: Callable or array representing the event listeners to add to + each request's :ref:`end_event` event. + +The "before", "complete", "error", and "end" event options accept a callable or +an array of associative arrays where each associative array contains a "fn" key +with a callable value, an optional "priority" key representing the event +priority (with a default value of 0), and an optional "once" key that can be +set to true so that the event listener will be removed from the request after +it is first triggered. + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Event\CompleteEvent; + + // Add a single event listener using a callable. + Pool::send($client, $requests, [ + 'complete' => function (CompleteEvent $event) { + echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; + echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; + } + ]); + + // The above is equivalent to the following, but the following structure + // allows you to add multiple event listeners to the same event name. + Pool::send($client, $requests, [ + 'complete' => [ + [ + 'fn' => function (CompleteEvent $event) { /* ... */ }, + 'priority' => 0, // Optional + 'once' => false // Optional + ] + ] + ]); + +Asynchronous Response Handling +------------------------------ + +When sending requests concurrently using a pool, the request/response/error +lifecycle must be handled asynchronously. This means that you give the Pool +multiple requests and handle the response or errors that is associated with the +request using event callbacks. + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Event\ErrorEvent; + + Pool::send($client, $requests, [ + 'complete' => function (CompleteEvent $event) { + echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; + echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; + // Do something with the completion of the request... + }, + 'error' => function (ErrorEvent $event) { + echo 'Request failed: ' . $event->getRequest()->getUrl() . "\n"; + echo $event->getException(); + // Do something to handle the error... + } + ]); + +The ``GuzzleHttp\Event\ErrorEvent`` event object is emitted when an error +occurs during a transfer. With this event, you have access to the request that +was sent, the response that was received (if one was received), access to +transfer statistics, and the ability to intercept the exception with a +different ``GuzzleHttp\Message\ResponseInterface`` object. See :doc:`events` +for more information. + +Handling Errors After Transferring +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It sometimes might be easier to handle all of the errors that occurred during a +transfer after all of the requests have been sent. Here we are adding each +failed request to an array that we can use to process errors later. + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Event\ErrorEvent; + + $errors = []; + Pool::send($client, $requests, [ + 'error' => function (ErrorEvent $event) use (&$errors) { + $errors[] = $event; + } + ]); + + foreach ($errors as $error) { + // Handle the error... + } + +.. _batch-requests: + +Batching Requests +----------------- + +Sometimes you just want to send a few requests concurrently and then process +the results all at once after they've been sent. Guzzle provides a convenience +function ``GuzzleHttp\Pool::batch()`` that makes this very simple: + +.. code-block:: php + + use GuzzleHttp\Pool; + use GuzzleHttp\Client; + + $client = new Client(); + + $requests = [ + $client->createRequest('GET', 'http://httpbin.org/get'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + // Results is a GuzzleHttp\BatchResults object. + $results = Pool::batch($client, $requests); + + // Can be accessed by index. + echo $results[0]->getStatusCode(); + + // Can be accessed by request. + echo $results->getResult($requests[0])->getStatusCode(); + + // Retrieve all successful responses + foreach ($results->getSuccessful() as $response) { + echo $response->getStatusCode() . "\n"; + } + + // Retrieve all failures. + foreach ($results->getFailures() as $requestException) { + echo $requestException->getMessage() . "\n"; + } + +``GuzzleHttp\Pool::batch()`` accepts an optional associative array of options +in the third argument that allows you to specify the 'before', 'complete', +'error', and 'end' events as well as specify the maximum number of requests to +send concurrently using the 'pool_size' option key. + +.. _request-options: + +Request Options +=============== + +You can customize requests created by a client using **request options**. +Request options control various aspects of a request including, headers, +query string parameters, timeout settings, the body of a request, and much +more. + +All of the following examples use the following client: + +.. code-block:: php + + $client = new GuzzleHttp\Client(['base_url' => 'http://httpbin.org']); + +headers +------- + +:Summary: Associative array of headers to add to the request. Each key is the + name of a header, and each value is a string or array of strings + representing the header field values. +:Types: array +:Defaults: None + +.. code-block:: php + + // Set various headers on a request + $client->get('/get', [ + 'headers' => [ + 'User-Agent' => 'testing/1.0', + 'Accept' => 'application/json', + 'X-Foo' => ['Bar', 'Baz'] + ] + ]); + +body +---- + +:Summary: The ``body`` option is used to control the body of an entity + enclosing request (e.g., PUT, POST, PATCH). +:Types: + - string + - ``fopen()`` resource + - ``GuzzleHttp\Stream\StreamInterface`` + - ``GuzzleHttp\Post\PostBodyInterface`` +:Default: None + +This setting can be set to any of the following types: + +- string + + .. code-block:: php + + // You can send requests that use a string as the message body. + $client->put('/put', ['body' => 'foo']); + +- resource returned from ``fopen()`` + + .. code-block:: php + + // You can send requests that use a stream resource as the body. + $resource = fopen('http://httpbin.org', 'r'); + $client->put('/put', ['body' => $resource]); + +- Array + + Use an array to send POST style requests that use a + ``GuzzleHttp\Post\PostBodyInterface`` object as the body. + + .. code-block:: php + + // You can send requests that use a POST body containing fields & files. + $client->post('/post', [ + 'body' => [ + 'field' => 'abc', + 'other_field' => '123', + 'file_name' => fopen('/path/to/file', 'r') + ] + ]); + +- ``GuzzleHttp\Stream\StreamInterface`` + + .. code-block:: php + + // You can send requests that use a Guzzle stream object as the body + $stream = GuzzleHttp\Stream\Stream::factory('contents...'); + $client->post('/post', ['body' => $stream]); + +json +---- + +:Summary: The ``json`` option is used to easily upload JSON encoded data as the + body of a request. A Content-Type header of ``application/json`` will be + added if no Content-Type header is already present on the message. +:Types: + Any PHP type that can be operated on by PHP's ``json_encode()`` function. +:Default: None + +.. code-block:: php + + $request = $client->createRequest('PUT', '/put', ['json' => ['foo' => 'bar']]); + echo $request->getHeader('Content-Type'); + // application/json + echo $request->getBody(); + // {"foo":"bar"} + +.. note:: + + This request option does not support customizing the Content-Type header + or any of the options from PHP's `json_encode() `_ + function. If you need to customize these settings, then you must pass the + JSON encoded data into the request yourself using the ``body`` request + option and you must specify the correct Content-Type header using the + ``headers`` request option. + +query +----- + +:Summary: Associative array of query string values to add to the request. +:Types: + - array + - ``GuzzleHttp\Query`` +:Default: None + +.. code-block:: php + + // Send a GET request to /get?foo=bar + $client->get('/get', ['query' => ['foo' => 'bar']]); + +Query strings specified in the ``query`` option are combined with any query +string values that are parsed from the URL. + +.. code-block:: php + + // Send a GET request to /get?abc=123&foo=bar + $client->get('/get?abc=123', ['query' => ['foo' => 'bar']]); + +auth +---- + +:Summary: Pass an array of HTTP authentication parameters to use with the + request. The array must contain the username in index [0], the password in + index [1], and you can optionally provide a built-in authentication type in + index [2]. Pass ``null`` to disable authentication for a request. +:Types: + - array + - string + - null +:Default: None + +The built-in authentication types are as follows: + +basic + Use `basic HTTP authentication `_ in + the ``Authorization`` header (the default setting used if none is + specified). + + .. code-block:: php + + $client->get('/get', ['auth' => ['username', 'password']]); + +digest + Use `digest authentication `_ (must be + supported by the HTTP handler). + + .. code-block:: php + + $client->get('/get', ['auth' => ['username', 'password', 'digest']]); + + *This is currently only supported when using the cURL handler, but creating + a replacement that can be used with any HTTP handler is planned.* + +.. important:: + + The authentication type (whether it's provided as a string or as the third + option in an array) is always converted to a lowercase string. Take this + into account when implementing custom authentication types and when + implementing custom message factories. + +Custom Authentication Schemes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also provide a string representing a custom authentication type name. +When using a custom authentication type string, you will need to implement +the authentication method in an event listener that checks the ``auth`` request +option of a request before it is sent. Authentication listeners that require +a request is not modified after they are signed should have a very low priority +to ensure that they are fired last or near last in the event chain. + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\RequestEvents; + + /** + * Custom authentication listener that handles the "foo" auth type. + * + * Listens to the "before" event of a request and only modifies the request + * when the "auth" config setting of the request is "foo". + */ + class FooAuth implements GuzzleHttp\Event\SubscriberInterface + { + private $password; + + public function __construct($password) + { + $this->password = $password; + } + + public function getEvents() + { + return ['before' => ['sign', RequestEvents::SIGN_REQUEST]]; + } + + public function sign(BeforeEvent $e) + { + if ($e->getRequest()->getConfig()['auth'] == 'foo') { + $e->getRequest()->setHeader('X-Foo', 'Foo ' . $this->password); + } + } + } + + $client->getEmitter()->attach(new FooAuth('password')); + $client->get('/', ['auth' => 'foo']); + +Adapter Specific Authentication Schemes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to use authentication methods provided by cURL (e.g., NTLM, GSS, +etc.), then you need to specify a curl handler option in the ``options`` +request option array. See :ref:`config-option` for more information. + +.. _cookies-option: + +cookies +------- + +:Summary: Specifies whether or not cookies are used in a request or what cookie + jar to use or what cookies to send. +:Types: + - bool + - array + - ``GuzzleHttp\Cookie\CookieJarInterface`` +:Default: None + +Set to ``true`` to use a shared cookie session associated with the client. + +.. code-block:: php + + // Enable cookies using the shared cookie jar of the client. + $client->get('/get', ['cookies' => true]); + +Pass an associative array containing cookies to send in the request and start a +new cookie session. + +.. code-block:: php + + // Enable cookies and send specific cookies + $client->get('/get', ['cookies' => ['foo' => 'bar']]); + +Set to a ``GuzzleHttp\Cookie\CookieJarInterface`` object to use an existing +cookie jar. + +.. code-block:: php + + $jar = new GuzzleHttp\Cookie\CookieJar(); + $client->get('/get', ['cookies' => $jar]); + +.. _allow_redirects-option: + +allow_redirects +--------------- + +:Summary: Describes the redirect behavior of a request +:Types: + - bool + - array +:Default: + :: + + [ + 'max' => 5, + 'strict' => false, + 'referer' => true, + 'protocols' => ['http', 'https'] + ] + +Set to ``false`` to disable redirects. + +.. code-block:: php + + $res = $client->get('/redirect/3', ['allow_redirects' => false]); + echo $res->getStatusCode(); + // 302 + +Set to ``true`` (the default setting) to enable normal redirects with a maximum +number of 5 redirects. + +.. code-block:: php + + $res = $client->get('/redirect/3'); + echo $res->getStatusCode(); + // 200 + +Pass an associative array containing the 'max' key to specify the maximum +number of redirects, provide a 'strict' key value to specify whether or not to +use strict RFC compliant redirects (meaning redirect POST requests with POST +requests vs. doing what most browsers do which is redirect POST requests with +GET requests), provide a 'referer' key to specify whether or not the "Referer" +header should be added when redirecting, and provide a 'protocols' array that +specifies which protocols are supported for redirects (defaults to +``['http', 'https']``). + +.. code-block:: php + + $res = $client->get('/redirect/3', [ + 'allow_redirects' => [ + 'max' => 10, // allow at most 10 redirects. + 'strict' => true, // use "strict" RFC compliant redirects. + 'referer' => true, // add a Referer header + 'protocols' => ['https'] // only allow https URLs + ] + ]); + echo $res->getStatusCode(); + // 200 + +decode_content +-------------- + +:Summary: Specify whether or not ``Content-Encoding`` responses (gzip, + deflate, etc.) are automatically decoded. +:Types: + - string + - bool +:Default: ``true`` + +This option can be used to control how content-encoded response bodies are +handled. By default, ``decode_content`` is set to true, meaning any gzipped +or deflated response will be decoded by Guzzle. + +When set to ``false``, the body of a response is never decoded, meaning the +bytes pass through the handler unchanged. + +.. code-block:: php + + // Request gzipped data, but do not decode it while downloading + $client->get('/foo.js', [ + 'headers' => ['Accept-Encoding' => 'gzip'], + 'decode_content' => false + ]); + +When set to a string, the bytes of a response are decoded and the string value +provided to the ``decode_content`` option is passed as the ``Accept-Encoding`` +header of the request. + +.. code-block:: php + + // Pass "gzip" as the Accept-Encoding header. + $client->get('/foo.js', ['decode_content' => 'gzip']); + +.. _save_to-option: + +save_to +------- + +:Summary: Specify where the body of a response will be saved. +:Types: + - string + - ``fopen()`` resource + - ``GuzzleHttp\Stream\StreamInterface`` +:Default: PHP temp stream + +Pass a string to specify the path to a file that will store the contents of the +response body: + +.. code-block:: php + + $client->get('/stream/20', ['save_to' => '/path/to/file']); + +Pass a resource returned from ``fopen()`` to write the response to a PHP stream: + +.. code-block:: php + + $resource = fopen('/path/to/file', 'w'); + $client->get('/stream/20', ['save_to' => $resource]); + +Pass a ``GuzzleHttp\Stream\StreamInterface`` object to stream the response body +to an open Guzzle stream: + +.. code-block:: php + + $resource = fopen('/path/to/file', 'w'); + $stream = GuzzleHttp\Stream\Stream::factory($resource); + $client->get('/stream/20', ['save_to' => $stream]); + +.. _events-option: + +events +------ + +:Summary: An associative array mapping event names to a callable. Or an + associative array containing the 'fn' key that maps to a callable, an + optional 'priority' key used to specify the event priority, and an optional + 'once' key used to specify if the event should remove itself the first time + it is triggered. +:Types: array +:Default: None + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\HeadersEvent; + use GuzzleHttp\Event\CompleteEvent; + use GuzzleHttp\Event\ErrorEvent; + + $client->get('/', [ + 'events' => [ + 'before' => function (BeforeEvent $e) { echo 'Before'; }, + 'complete' => function (CompleteEvent $e) { echo 'Complete'; }, + 'error' => function (ErrorEvent $e) { echo 'Error'; }, + ] + ]); + +Here's an example of using the associative array format for control over the +priority and whether or not an event should be triggered more than once. + +.. code-block:: php + + $client->get('/', [ + 'events' => [ + 'before' => [ + 'fn' => function (BeforeEvent $e) { echo 'Before'; }, + 'priority' => 100, + 'once' => true + ] + ] + ]); + +.. _subscribers-option: + +subscribers +----------- + +:Summary: Array of event subscribers to add to the request. Each value in the + array must be an instance of ``GuzzleHttp\Event\SubscriberInterface``. +:Types: array +:Default: None + +.. code-block:: php + + use GuzzleHttp\Subscriber\History; + use GuzzleHttp\Subscriber\Mock; + use GuzzleHttp\Message\Response; + + $history = new History(); + $mock = new Mock([new Response(200)]); + $client->get('/', ['subscribers' => [$history, $mock]]); + + echo $history; + // Outputs the request and response history + +.. _exceptions-option: + +exceptions +---------- + +:Summary: Set to ``false`` to disable throwing exceptions on an HTTP protocol + errors (i.e., 4xx and 5xx responses). Exceptions are thrown by default when + HTTP protocol errors are encountered. +:Types: bool +:Default: ``true`` + +.. code-block:: php + + $client->get('/status/500'); + // Throws a GuzzleHttp\Exception\ServerException + + $res = $client->get('/status/500', ['exceptions' => false]); + echo $res->getStatusCode(); + // 500 + +.. _timeout-option: + +timeout +------- + +:Summary: Float describing the timeout of the request in seconds. Use ``0`` + to wait indefinitely (the default behavior). +:Types: float +:Default: ``0`` + +.. code-block:: php + + // Timeout if a server does not return a response in 3.14 seconds. + $client->get('/delay/5', ['timeout' => 3.14]); + // PHP Fatal error: Uncaught exception 'GuzzleHttp\Exception\RequestException' + +.. _connect_timeout-option: + +connect_timeout +--------------- + +:Summary: Float describing the number of seconds to wait while trying to connect + to a server. Use ``0`` to wait indefinitely (the default behavior). +:Types: float +:Default: ``0`` + +.. code-block:: php + + // Timeout if the client fails to connect to the server in 3.14 seconds. + $client->get('/delay/5', ['connect_timeout' => 3.14]); + +.. note:: + + This setting must be supported by the HTTP handler used to send a request. + ``connect_timeout`` is currently only supported by the built-in cURL + handler. + +.. _verify-option: + +verify +------ + +:Summary: Describes the SSL certificate verification behavior of a request. + + - Set to ``true`` to enable SSL certificate verification and use the default + CA bundle provided by operating system. + - Set to ``false`` to disable certificate verification (this is insecure!). + - Set to a string to provide the path to a CA bundle to enable verification + using a custom certificate. +:Types: + - bool + - string +:Default: ``true`` + +.. code-block:: php + + // Use the system's CA bundle (this is the default setting) + $client->get('/', ['verify' => true]); + + // Use a custom SSL certificate on disk. + $client->get('/', ['verify' => '/path/to/cert.pem']); + + // Disable validation entirely (don't do this!). + $client->get('/', ['verify' => false]); + +Not all system's have a known CA bundle on disk. For example, Windows and +OS X do not have a single common location for CA bundles. When setting +"verify" to ``true``, Guzzle will do its best to find the most appropriate +CA bundle on your system. When using cURL or the PHP stream wrapper on PHP +versions >= 5.6, this happens by default. When using the PHP stream +wrapper on versions < 5.6, Guzzle tries to find your CA bundle in the +following order: + +1. Check if ``openssl.cafile`` is set in your php.ini file. +2. Check if ``curl.cainfo`` is set in your php.ini file. +3. Check if ``/etc/pki/tls/certs/ca-bundle.crt`` exists (Red Hat, CentOS, + Fedora; provided by the ca-certificates package) +4. Check if ``/etc/ssl/certs/ca-certificates.crt`` exists (Ubuntu, Debian; + provided by the ca-certificates package) +5. Check if ``/usr/local/share/certs/ca-root-nss.crt`` exists (FreeBSD; + provided by the ca_root_nss package) +6. Check if ``/usr/local/etc/openssl/cert.pem`` (OS X; provided by homebrew) +7. Check if ``C:\windows\system32\curl-ca-bundle.crt`` exists (Windows) +8. Check if ``C:\windows\curl-ca-bundle.crt`` exists (Windows) + +The result of this lookup is cached in memory so that subsequent calls +in the same process will return very quickly. However, when sending only +a single request per-process in something like Apache, you should consider +setting the ``openssl.cafile`` environment variable to the path on disk +to the file so that this entire process is skipped. + +If you do not need a specific certificate bundle, then Mozilla provides a +commonly used CA bundle which can be downloaded +`here `_ +(provided by the maintainer of cURL). Once you have a CA bundle available on +disk, you can set the "openssl.cafile" PHP ini setting to point to the path to +the file, allowing you to omit the "verify" request option. Much more detail on +SSL certificates can be found on the +`cURL website `_. + +.. _cert-option: + +cert +---- + +:Summary: Set to a string to specify the path to a file containing a PEM + formatted client side certificate. If a password is required, then set to + an array containing the path to the PEM file in the first array element + followed by the password required for the certificate in the second array + element. +:Types: + - string + - array +:Default: None + +.. code-block:: php + + $client->get('/', ['cert' => ['/path/server.pem', 'password']]); + +.. _ssl_key-option: + +ssl_key +------- + +:Summary: Specify the path to a file containing a private SSL key in PEM + format. If a password is required, then set to an array containing the path + to the SSL key in the first array element followed by the password required + for the certificate in the second element. +:Types: + - string + - array +:Default: None + +.. note:: + + ``ssl_key`` is implemented by HTTP handlers. This is currently only + supported by the cURL handler, but might be supported by other third-part + handlers. + +.. _proxy-option: + +proxy +----- + +:Summary: Pass a string to specify an HTTP proxy, or an array to specify + different proxies for different protocols. +:Types: + - string + - array +:Default: None + +Pass a string to specify a proxy for all protocols. + +.. code-block:: php + + $client->get('/', ['proxy' => 'tcp://localhost:8125']); + +Pass an associative array to specify HTTP proxies for specific URI schemes +(i.e., "http", "https"). + +.. code-block:: php + + $client->get('/', [ + 'proxy' => [ + 'http' => 'tcp://localhost:8125', // Use this proxy with "http" + 'https' => 'tcp://localhost:9124' // Use this proxy with "https" + ] + ]); + +.. note:: + + You can provide proxy URLs that contain a scheme, username, and password. + For example, ``"http://username:password@192.168.16.1:10"``. + +.. _debug-option: + +debug +----- + +:Summary: Set to ``true`` or set to a PHP stream returned by ``fopen()`` to + enable debug output with the handler used to send a request. For example, + when using cURL to transfer requests, cURL's verbose of ``CURLOPT_VERBOSE`` + will be emitted. When using the PHP stream wrapper, stream wrapper + notifications will be emitted. If set to true, the output is written to + PHP's STDOUT. If a PHP stream is provided, output is written to the stream. +:Types: + - bool + - ``fopen()`` resource +:Default: None + +.. code-block:: php + + $client->get('/get', ['debug' => true]); + +Running the above example would output something like the following: + +:: + + * About to connect() to httpbin.org port 80 (#0) + * Trying 107.21.213.98... * Connected to httpbin.org (107.21.213.98) port 80 (#0) + > GET /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0 curl/7.21.4 PHP/5.5.7 + + < HTTP/1.1 200 OK + < Access-Control-Allow-Origin: * + < Content-Type: application/json + < Date: Sun, 16 Feb 2014 06:50:09 GMT + < Server: gunicorn/0.17.4 + < Content-Length: 335 + < Connection: keep-alive + < + * Connection #0 to host httpbin.org left intact + +.. _stream-option: + +stream +------ + +:Summary: Set to ``true`` to stream a response rather than download it all + up-front. +:Types: bool +:Default: ``false`` + +.. code-block:: php + + $response = $client->get('/stream/20', ['stream' => true]); + // Read bytes off of the stream until the end of the stream is reached + $body = $response->getBody(); + while (!$body->eof()) { + echo $body->read(1024); + } + +.. note:: + + Streaming response support must be implemented by the HTTP handler used by + a client. This option might not be supported by every HTTP handler, but the + interface of the response object remains the same regardless of whether or + not it is supported by the handler. + +.. _expect-option: + +expect +------ + +:Summary: Controls the behavior of the "Expect: 100-Continue" header. +:Types: + - bool + - integer +:Default: ``1048576`` + +Set to ``true`` to enable the "Expect: 100-Continue" header for all requests +that sends a body. Set to ``false`` to disable the "Expect: 100-Continue" +header for all requests. Set to a number so that the size of the payload must +be greater than the number in order to send the Expect header. Setting to a +number will send the Expect header for all requests in which the size of the +payload cannot be determined or where the body is not rewindable. + +By default, Guzzle will add the "Expect: 100-Continue" header when the size of +the body of a request is greater than 1 MB and a request is using HTTP/1.1. + +.. note:: + + This option only takes effect when using HTTP/1.1. The HTTP/1.0 and + HTTP/2.0 protocols do not support the "Expect: 100-Continue" header. + Support for handling the "Expect: 100-Continue" workflow must be + implemented by Guzzle HTTP handlers used by a client. + +.. _version-option: + +version +------- + +:Summary: Protocol version to use with the request. +:Types: string, float +:Default: ``1.1`` + +.. code-block:: php + + // Force HTTP/1.0 + $request = $client->createRequest('GET', '/get', ['version' => 1.0]); + echo $request->getProtocolVersion(); + // 1.0 + +.. _config-option: + +config +------ + +:Summary: Associative array of config options that are forwarded to a request's + configuration collection. These values are used as configuration options + that can be consumed by plugins and handlers. +:Types: array +:Default: None + +.. code-block:: php + + $request = $client->createRequest('GET', '/get', ['config' => ['foo' => 'bar']]); + echo $request->getConfig('foo'); + // 'bar' + +Some HTTP handlers allow you to specify custom handler-specific settings. For +example, you can pass custom cURL options to requests by passing an associative +array in the ``config`` request option under the ``curl`` key. + +.. code-block:: php + + // Use custom cURL options with the request. This example uses NTLM auth + // to authenticate with a server. + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_HTTPAUTH => CURLAUTH_NTLM, + CURLOPT_USERPWD => 'username:password' + ] + ] + ]); + +future +------ + +:Summary: Specifies whether or not a response SHOULD be an instance of a + ``GuzzleHttp\Message\FutureResponse`` object. +:Types: + - bool + - string +:Default: ``false`` + +By default, Guzzle requests should be synchronous. You can create asynchronous +future responses by passing the ``future`` request option as ``true``. The +response will only be executed when it is used like a normal response, the +``wait()`` method of the response is called, or the corresponding handler that +created the response is destructing and there are futures that have not been +resolved. + +.. important:: + + This option only has an effect if your handler can create and return future + responses. However, even if a response is completed synchronously, Guzzle + will ensure that a FutureResponse object is returned for API consistency. + +.. code-block:: php + + $response = $client->get('/foo', ['future' => true]) + ->then(function ($response) { + echo 'I got a response! ' . $response; + }); + +Event Subscribers +================= + +Requests emit lifecycle events when they are transferred. A client object has a +``GuzzleHttp\Common\EventEmitter`` object that can be used to add event +*listeners* and event *subscribers* to all requests created by the client. + +.. important:: + + **Every** event listener or subscriber added to a client will be added to + every request created by the client. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\BeforeEvent; + + $client = new Client(); + + // Add a listener that will echo out requests before they are sent + $client->getEmitter()->on('before', function (BeforeEvent $e) { + echo 'About to send request: ' . $e->getRequest(); + }); + + $client->get('http://httpbin.org/get'); + // Outputs the request as a string because of the event + +See :doc:`events` for more information on the event system used in Guzzle. + +Environment Variables +===================== + +Guzzle exposes a few environment variables that can be used to customize the +behavior of the library. + +``GUZZLE_CURL_SELECT_TIMEOUT`` + Controls the duration in seconds that a curl_multi_* handler will use when + selecting on curl handles using ``curl_multi_select()``. Some systems + have issues with PHP's implementation of ``curl_multi_select()`` where + calling this function always results in waiting for the maximum duration of + the timeout. +``HTTP_PROXY`` + Defines the proxy to use when sending requests using the "http" protocol. +``HTTPS_PROXY`` + Defines the proxy to use when sending requests using the "https" protocol. + +Relevant ini Settings +--------------------- + +Guzzle can utilize PHP ini settings when configuring clients. + +``openssl.cafile`` + Specifies the path on disk to a CA file in PEM format to use when sending + requests over "https". See: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/conf.py b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/conf.py new file mode 100755 index 000000000..917bdf4ca --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/conf.py @@ -0,0 +1,28 @@ +import sys, os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'Guzzle' +copyright = u'2014, Michael Dowling' +version = '5.0.0' +html_title = "Guzzle Documentation" +html_short_title = "Guzzle" + +exclude_patterns = ['_build'] +html_static_path = ['_static'] + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/events.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/events.rst new file mode 100755 index 000000000..30afdb61b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/events.rst @@ -0,0 +1,516 @@ +============ +Event System +============ + +Guzzle uses an event emitter to allow you to easily extend the behavior of a +request, change the response associated with a request, and implement custom +error handling. All events in Guzzle are managed and emitted by an +**event emitter**. + +Event Emitters +============== + +Clients, requests, and any other class that implements the +``GuzzleHttp\Event\HasEmitterInterface`` interface have a +``GuzzleHttp\Event\Emitter`` object. You can add event *listeners* and +event *subscribers* to an event *emitter*. + +emitter + An object that implements ``GuzzleHttp\Event\EmitterInterface``. This + object emits named events to event listeners. You may register event + listeners on subscribers on an emitter. + +event listeners + Callable functions that are registered on an event emitter for specific + events. Event listeners are registered on an emitter with a *priority* + setting. If no priority is provided, ``0`` is used by default. + +event subscribers + Classes that tell an event emitter what methods to listen to and what + functions on the class to invoke when the event is triggered. Event + subscribers subscribe event listeners to an event emitter. They should be + used when creating more complex event based logic in applications (i.e., + cookie handling is implemented using an event subscriber because it's + easier to share a subscriber than an anonymous function and because + handling cookies is a complex process). + +priority + Describes the order in which event listeners are invoked when an event is + emitted. The higher a priority value, the earlier the event listener will + be invoked (a higher priority means the listener is more important). If + no priority is provided, the priority is assumed to be ``0``. + + When specifying an event priority, you can pass ``"first"`` or ``"last"`` to + dynamically specify the priority based on the current event priorities + associated with the given event name in the emitter. Use ``"first"`` to set + the priority to the current highest priority plus one. Use ``"last"`` to + set the priority to the current lowest event priority minus one. It is + important to remember that these dynamic priorities are calculated only at + the point of insertion into the emitter and they are not rearranged after + subsequent listeners are added to an emitter. + +propagation + Describes whether or not other event listeners are triggered. Event + emitters will trigger every event listener registered to a specific event + in priority order until all of the listeners have been triggered **or** + until the propagation of an event is stopped. + +Getting an EventEmitter +----------------------- + +You can get the event emitter of ``GuzzleHttp\Event\HasEmitterInterface`` +object using the the ``getEmitter()`` method. Here's an example of getting a +client object's event emitter. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + +.. note:: + + You'll notice that the event emitter used in Guzzle is very similar to the + `Symfony2 EventDispatcher component `_. + This is because the Guzzle event system is based on the Symfony2 event + system with several changes. Guzzle uses its own event emitter to improve + performance, isolate Guzzle from changes to the Symfony, and provide a few + improvements that make it easier to use for an HTTP client (e.g., the + addition of the ``once()`` method). + +Adding Event Listeners +---------------------- + +After you have the emitter, you can register event listeners that listen to +specific events using the ``on()`` method. When registering an event listener, +you must tell the emitter what event to listen to (e.g., "before", "after", +"progress", "complete", "error", etc.), what callable to invoke when the +event is triggered, and optionally provide a priority. + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + + $emitter->on('before', function (BeforeEvent $event) { + echo $event->getRequest(); + }); + +When a listener is triggered, it is passed an event that implements the +``GuzzleHttp\Event\EventInterface`` interface, the name of the event, and the +event emitter itself. The above example could more verbosely be written as +follows: + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + + $emitter->on('before', function (BeforeEvent $event, $name) { + echo $event->getRequest(); + }); + +You can add an event listener that automatically removes itself after it is +triggered using the ``once()`` method of an event emitter. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $client->getEmitter()->once('before', function () { + echo 'This will only happen once... per request!'; + }); + +Event Propagation +----------------- + +Event listeners can prevent other event listeners from being triggered by +stopping an event's propagation. + +Stopping event propagation can be useful, for example, if an event listener has +changed the state of the subject to such an extent that allowing subsequent +event listeners to be triggered could place the subject in an inconsistent +state. This technique is used in Guzzle extensively when intercepting error +events with responses. + +You can stop the propagation of an event using the ``stopPropagation()`` method +of a ``GuzzleHttp\Event\EventInterface`` object: + +.. code-block:: php + + use GuzzleHttp\Event\ErrorEvent; + + $emitter->on('error', function (ErrorEvent $event) { + $event->stopPropagation(); + }); + +After stopping the propagation of an event, any subsequent event listeners that +have not yet been triggered will not be triggered. You can check to see if the +propagation of an event was stopped using the ``isPropagationStopped()`` method +of the event. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + // Note: assume that the $errorEvent was created + if ($emitter->emit('error', $errorEvent)->isPropagationStopped()) { + echo 'It was stopped!'; + } + +.. hint:: + + When emitting events, the event that was emitted is returned from the + emitter. This allows you to easily chain calls as shown in the above + example. + +Event Subscribers +----------------- + +Event subscribers are classes that implement the +``GuzzleHttp\Event\SubscriberInterface`` object. They are used to register +one or more event listeners to methods of the class. Event subscribers tell +event emitters exactly which events to listen to and what method to invoke on +the class when the event is triggered by called the ``getEvents()`` method of +a subscriber. + +The following example registers event listeners to the ``before`` and +``complete`` event of a request. When the ``before`` event is emitted, the +``onBefore`` instance method of the subscriber is invoked. When the +``complete`` event is emitted, the ``onComplete`` event of the subscriber is +invoked. Each array value in the ``getEvents()`` return value MUST +contain the name of the method to invoke and can optionally contain the +priority of the listener (as shown in the ``before`` listener in the example). + +.. code-block:: php + + use GuzzleHttp\Event\EmitterInterface; + use GuzzleHttp\Event\SubscriberInterface; + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\CompleteEvent; + + class SimpleSubscriber implements SubscriberInterface + { + public function getEvents() + { + return [ + // Provide name and optional priority + 'before' => ['onBefore', 100], + 'complete' => ['onComplete'], + // You can pass a list of listeners with different priorities + 'error' => [['beforeError', 'first'], ['afterError', 'last']] + ]; + } + + public function onBefore(BeforeEvent $event, $name) + { + echo 'Before!'; + } + + public function onComplete(CompleteEvent $event, $name) + { + echo 'Complete!'; + } + } + +To register the listeners the subscriber needs to be attached to the emitter: + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + $subscriber = new SimpleSubscriber(); + $emitter->attach($subscriber); + + //to remove the listeners + $emitter->detach($subscriber); + +.. note:: + + You can specify event priorities using integers or ``"first"`` and + ``"last"`` to dynamically determine the priority. + +Event Priorities +================ + +When adding event listeners or subscribers, you can provide an optional event +priority. This priority is used to determine how early or late a listener is +triggered. Specifying the correct priority is an important aspect of ensuring +a listener behaves as expected. For example, if you wanted to ensure that +cookies associated with a redirect were added to a cookie jar, you'd need to +make sure that the listener that collects the cookies is triggered before the +listener that performs the redirect. + +In order to help make the process of determining the correct event priority of +a listener easier, Guzzle provides several pre-determined named event +priorities. These priorities are exposed as constants on the +``GuzzleHttp\Event\RequestEvents`` object. + +last + Use ``"last"`` as an event priority to set the priority to the current + lowest event priority minus one. + +first + Use ``"first"`` as an event priority to set the priority to the current + highest priority plus one. + +``GuzzleHttp\Event\RequestEvents::EARLY`` + Used when you want a listener to be triggered as early as possible in the + event chain. + +``GuzzleHttp\Event\RequestEvents::LATE`` + Used when you want a listener to be to be triggered as late as possible in + the event chain. + +``GuzzleHttp\Event\RequestEvents::PREPARE_REQUEST`` + Used when you want a listener to be trigger while a request is being + prepared during the ``before`` event. This event priority is used by the + ``GuzzleHttp\Subscriber\Prepare`` event subscriber which is responsible for + guessing a Content-Type, Content-Length, and Expect header of a request. + You should subscribe after this event is triggered if you want to ensure + that this subscriber has already been triggered. + +``GuzzleHttp\Event\RequestEvents::SIGN_REQUEST`` + Used when you want a listener to be triggered when a request is about to be + signed. Any listener triggered at this point should expect that the request + object will no longer be mutated. If you are implementing a custom + signature subscriber, then you should use this event priority to sign + requests. + +``GuzzleHttp\Event\RequestEvents::VERIFY_RESPONSE`` + Used when you want a listener to be triggered when a response is being + validated during the ``complete`` event. The + ``GuzzleHttp\Subscriber\HttpError`` event subscriber uses this event + priority to check if an exception should be thrown due to a 4xx or 5xx + level response status code. If you are doing any kind of verification of a + response during the complete event, it should happen at this priority. + +``GuzzleHttp\Event\RequestEvents::REDIRECT_RESPONSE`` + Used when you want a listener to be triggered when a response is being + redirected during the ``complete`` event. The + ``GuzzleHttp\Subscriber\Redirect`` event subscriber uses this event + priority when performing redirects. + +You can use the above event priorities as a guideline for determining the +priority of you event listeners. You can use these constants and add to or +subtract from them to ensure that a listener happens before or after the named +priority. + +.. note:: + + "first" and "last" priorities are not adjusted after they added to an + emitter. For example, if you add a listener with a priority of "first", + you can still add subsequent listeners with a higher priority which would + be triggered before the listener added with a priority of "first". + +Working With Request Events +=========================== + +Requests emit lifecycle events when they are transferred. + +.. important:: + + Excluding the ``end`` event, request lifecycle events may be triggered + multiple times due to redirects, retries, or reusing a request multiple + times. Use the ``once()`` method want the event to be triggered once. You + can also remove an event listener from an emitter by using the emitter which + is provided to the listener. + +.. _before_event: + +before +------ + +The ``before`` event is emitted before a request is sent. The event emitted is +a ``GuzzleHttp\Event\BeforeEvent``. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\EmitterInterface; + use GuzzleHttp\Event\BeforeEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/'); + $request->getEmitter()->on( + 'before', + function (BeforeEvent $e, $name) { + echo $name . "\n"; + // "before" + echo $e->getRequest()->getMethod() . "\n"; + // "GET" / "POST" / "PUT" / etc. + echo get_class($e->getClient()); + // "GuzzleHttp\Client" + } + ); + +You can intercept a request with a response before the request is sent over the +wire. The ``intercept()`` method of the ``BeforeEvent`` accepts a +``GuzzleHttp\Message\ResponseInterface``. Intercepting the event will prevent +the request from being sent over the wire and stops the propagation of the +``before`` event, preventing subsequent event listeners from being invoked. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Message\Response; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/status/500'); + $request->getEmitter()->on('before', function (BeforeEvent $e) { + $response = new Response(200); + $e->intercept($response); + }); + + $response = $client->send($request); + echo $response->getStatusCode(); + // 200 + +.. attention:: + + Any exception encountered while executing the ``before`` event will trigger + the ``error`` event of a request. + +.. _complete_event: + +complete +-------- + +The ``complete`` event is emitted after a transaction completes and an entire +response has been received. The event is a ``GuzzleHttp\Event\CompleteEvent``. + +You can intercept the ``complete`` event with a different response if needed +using the ``intercept()`` method of the event. This can be useful, for example, +for changing the response for caching. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\CompleteEvent; + use GuzzleHttp\Message\Response; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/status/302'); + $cachedResponse = new Response(200); + + $request->getEmitter()->on( + 'complete', + function (CompleteEvent $e) use ($cachedResponse) { + if ($e->getResponse()->getStatusCode() == 302) { + // Intercept the original transaction with the new response + $e->intercept($cachedResponse); + } + } + ); + + $response = $client->send($request); + echo $response->getStatusCode(); + // 200 + +.. attention:: + + Any ``GuzzleHttp\Exception\RequestException`` encountered while executing + the ``complete`` event will trigger the ``error`` event of a request. + +.. _error_event: + +error +----- + +The ``error`` event is emitted when a request fails (whether it's from a +networking error or an HTTP protocol error). The event emitted is a +``GuzzleHttp\Event\ErrorEvent``. + +This event is useful for retrying failed requests. Here's an example of +retrying failed basic auth requests by re-sending the original request with +a username and password. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\ErrorEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/basic-auth/foo/bar'); + $request->getEmitter()->on('error', function (ErrorEvent $e) { + if ($e->getResponse()->getStatusCode() == 401) { + // Add authentication stuff as needed and retry the request + $e->getRequest()->setHeader('Authorization', 'Basic ' . base64_encode('foo:bar')); + // Get the client of the event and retry the request + $newResponse = $e->getClient()->send($e->getRequest()); + // Intercept the original transaction with the new response + $e->intercept($newResponse); + } + }); + +.. attention:: + + If an ``error`` event is intercepted with a response, then the ``complete`` + event of a request is triggered. If the ``complete`` event fails, then the + ``error`` event is triggered once again. + +.. _progress_event: + +progress +-------- + +The ``progress`` event is emitted when data is uploaded or downloaded. The +event emitted is a ``GuzzleHttp\Event\ProgressEvent``. + +You can access the emitted progress values using the corresponding public +properties of the event object: + +- ``$downloadSize``: The number of bytes that will be downloaded (if known) +- ``$downloaded``: The number of bytes that have been downloaded +- ``$uploadSize``: The number of bytes that will be uploaded (if known) +- ``$uploaded``: The number of bytes that have been uploaded + +This event cannot be intercepted. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\ProgressEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('PUT', '/put', [ + 'body' => str_repeat('.', 100000) + ]); + + $request->getEmitter()->on('progress', function (ProgressEvent $e) { + echo 'Downloaded ' . $e->downloaded . ' of ' . $e->downloadSize . ' ' + . 'Uploaded ' . $e->uploaded . ' of ' . $e->uploadSize . "\r"; + }); + + $client->send($request); + echo "\n"; + +.. _end_event: + +end +--- + +The ``end`` event is a terminal event, emitted once per request, that provides +access to the response that was received or the exception that was encountered. +The event emitted is a ``GuzzleHttp\Event\EndEvent``. + +This event can be intercepted, but keep in mind that the ``complete`` event +will not fire after intercepting this event. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\EndEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('PUT', '/put', [ + 'body' => str_repeat('.', 100000) + ]); + + $request->getEmitter()->on('end', function (EndEvent $e) { + if ($e->getException()) { + echo 'Error: ' . $e->getException()->getMessage(); + } else { + echo 'Response: ' . $e->getResponse(); + } + }); + + $client->send($request); + echo "\n"; diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/faq.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/faq.rst new file mode 100755 index 000000000..a8e9ad060 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/faq.rst @@ -0,0 +1,199 @@ +=== +FAQ +=== + +Why should I use Guzzle? +======================== + +Guzzle makes it easy to send HTTP requests and super simple to integrate with +web services. Guzzle manages things like persistent connections, represents +query strings as collections, makes it simple to send streaming POST requests +with fields and files, and abstracts away the underlying HTTP transport layer. +By providing an object oriented interface for HTTP clients, requests, responses, +headers, and message bodies, Guzzle makes it so that you no longer need to fool +around with cURL options, stream contexts, or sockets. + +**Asynchronous and Synchronous Requests** + +Guzzle allows you to send both asynchronous and synchronous requests using the +same interface and no direct dependency on an event loop. This flexibility +allows Guzzle to send an HTTP request using the most appropriate HTTP handler +based on the request being sent. For example, when sending synchronous +requests, Guzzle will by default send requests using cURL easy handles to +ensure you're using the fastest possible method for serially transferring HTTP +requests. When sending asynchronous requests, Guzzle might use cURL's multi +interface or any other asynchronous handler you configure. When you request +streaming data, Guzzle will by default use PHP's stream wrapper. + +**Streams** + +Request and response message bodies use :doc:`Guzzle Streams `, +allowing you to stream data without needing to load it all into memory. +Guzzle's stream layer provides a large suite of functionality: + +- You can modify streams at runtime using custom or a number of + pre-made decorators. +- You can emit progress events as data is read from a stream. +- You can validate the integrity of a stream using a rolling hash as data is + read from a stream. + +**Event System and Plugins** + +Guzzle's event system allows you to completely modify the behavior of a client +or request at runtime to cater them for any API. You can send a request with a +client, and the client can do things like automatically retry your request if +it fails, automatically redirect, log HTTP messages that are sent over the +wire, emit progress events as data is uploaded and downloaded, sign requests +using OAuth 1.0, verify the integrity of messages before and after they are +sent over the wire, and anything else you might need. + +**Testable** + +Another important aspect of Guzzle is that it's really +:doc:`easy to test clients `. You can mock HTTP responses and when +testing an handler implementation, Guzzle provides a mock node.js web server. + +**Ecosystem** + +Guzzle has a large `ecosystem of plugins `_, +including `service descriptions `_ +which allows you to abstract web services using service descriptions. These +service descriptions define how to serialize an HTTP request and how to parse +an HTTP response into a more meaningful model object. + +- `Guzzle Command `_: Provides the building + blocks for service description abstraction. +- `Guzzle Services `_: Provides an + implementation of "Guzzle Command" that utilizes Guzzle's service description + format. + +Does Guzzle require cURL? +========================= + +No. Guzzle can use any HTTP handler to send requests. This means that Guzzle +can be used with cURL, PHP's stream wrapper, sockets, and non-blocking libraries +like `React `_. You just need to configure a +`RingPHP `_ handler to use a +different method of sending requests. + +.. note:: + + Guzzle has historically only utilized cURL to send HTTP requests. cURL is + an amazing HTTP client (arguably the best), and Guzzle will continue to use + it by default when it is available. It is rare, but some developers don't + have cURL installed on their systems or run into version specific issues. + By allowing swappable HTTP handlers, Guzzle is now much more customizable + and able to adapt to fit the needs of more developers. + +Can Guzzle send asynchronous requests? +====================================== + +Yes. Pass the ``future`` true request option to a request to send it +asynchronously. Guzzle will then return a ``GuzzleHttp\Message\FutureResponse`` +object that can be used synchronously by accessing the response object like a +normal response, and it can be used asynchronously using a promise that is +notified when the response is resolved with a real response or rejected with an +exception. + +.. code-block:: php + + $request = $client->createRequest('GET', ['future' => true]); + $client->send($request)->then(function ($response) { + echo 'Got a response! ' . $response; + }); + +You can force an asynchronous response to complete using the ``wait()`` method +of a response. + +.. code-block:: php + + $request = $client->createRequest('GET', ['future' => true]); + $futureResponse = $client->send($request); + $futureResponse->wait(); + +How can I add custom cURL options? +================================== + +cURL offer a huge number of `customizable options `_. +While Guzzle normalizes many of these options across different handlers, there +are times when you need to set custom cURL options. This can be accomplished +by passing an associative array of cURL settings in the **curl** key of the +**config** request option. + +For example, let's say you need to customize the outgoing network interface +used with a client. + +.. code-block:: php + + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_INTERFACE => 'xxx.xxx.xxx.xxx' + ] + ] + ]); + +How can I add custom stream context options? +============================================ + +You can pass custom `stream context options `_ +using the **stream_context** key of the **config** request option. The +**stream_context** array is an associative array where each key is a PHP +transport, and each value is an associative array of transport options. + +For example, let's say you need to customize the outgoing network interface +used with a client and allow self-signed certificates. + +.. code-block:: php + + $client->get('/', [ + 'stream' => true, + 'config' => [ + 'stream_context' => [ + 'ssl' => [ + 'allow_self_signed' => true + ], + 'socket' => [ + 'bindto' => 'xxx.xxx.xxx.xxx' + ] + ] + ] + ]); + +Why am I getting an SSL verification error? +=========================================== + +You need to specify the path on disk to the CA bundle used by Guzzle for +verifying the peer certificate. See :ref:`verify-option`. + +What is this Maximum function nesting error? +============================================ + + Maximum function nesting level of '100' reached, aborting + +You could run into this error if you have the XDebug extension installed and +you execute a lot of requests in callbacks. This error message comes +specifically from the XDebug extension. PHP itself does not have a function +nesting limit. Change this setting in your php.ini to increase the limit:: + + xdebug.max_nesting_level = 1000 + +Why am I getting a 417 error response? +====================================== + +This can occur for a number of reasons, but if you are sending PUT, POST, or +PATCH requests with an ``Expect: 100-Continue`` header, a server that does not +support this header will return a 417 response. You can work around this by +setting the ``expect`` request option to ``false``: + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + + // Disable the expect header on a single request + $response = $client->put('/', [], 'the body', [ + 'expect' => false + ]); + + // Disable the expect header on all client requests + $client->setDefaultOption('expect', false) diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/handlers.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/handlers.rst new file mode 100755 index 000000000..d452003fd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/handlers.rst @@ -0,0 +1,43 @@ +================ +RingPHP Handlers +================ + +Guzzle uses RingPHP handlers to send HTTP requests over the wire. +RingPHP provides a low-level library that can be used to "glue" Guzzle with +any transport method you choose. By default, Guzzle utilizes cURL and PHP's +stream wrappers to send HTTP requests. + +RingPHP handlers makes it extremely simple to integrate Guzzle with any +HTTP transport. For example, you could quite easily bridge Guzzle and React +to use Guzzle in React's event loop. + +Using a handler +--------------- + +You can change the handler used by a client using the ``handler`` option in the +``GuzzleHttp\Client`` constructor. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Ring\Client\MockHandler; + + // Create a mock handler that always returns a 200 response. + $handler = new MockHandler(['status' => 200]); + + // Configure to client to use the mock handler. + $client = new Client(['handler' => $handler]); + +At its core, handlers are simply PHP callables that accept a request array +and return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. This future array +can be used just like a normal PHP array, causing it to block, or you can use +the promise interface using the ``then()`` method of the future. Guzzle hooks +up to the RingPHP project using a very simple bridge class +(``GuzzleHttp\RingBridge``). + +Creating a handler +------------------ + +See the `RingPHP `_ project +documentation for more information on creating custom handlers that can be +used with Guzzle clients. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/http-messages.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/http-messages.rst new file mode 100755 index 000000000..0c6527a8d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/http-messages.rst @@ -0,0 +1,483 @@ +============================= +Request and Response Messages +============================= + +Guzzle is an HTTP client that sends HTTP requests to a server and receives HTTP +responses. Both requests and responses are referred to as messages. + +Headers +======= + +Both request and response messages contain HTTP headers. + +Complex Headers +--------------- + +Some headers contain additional key value pair information. For example, Link +headers contain a link and several key value pairs: + +:: + + ; rel="thing"; type="image/jpeg" + +Guzzle provides a convenience feature that can be used to parse these types of +headers: + +.. code-block:: php + + use GuzzleHttp\Message\Request; + + $request = new Request('GET', '/', [ + 'Link' => '; rel="front"; type="image/jpeg"' + ]); + + $parsed = Request::parseHeader($request, 'Link'); + var_export($parsed); + +Will output: + +.. code-block:: php + + array ( + 0 => + array ( + 0 => '', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + ) + +The result contains a hash of key value pairs. Header values that have no key +(i.e., the link) are indexed numerically while headers parts that form a key +value pair are added as a key value pair. + +See :ref:`headers` for information on how the headers of a request and response +can be accessed and modified. + +Body +==== + +Both request and response messages can contain a body. + +You can check to see if a request or response has a body using the +``getBody()`` method: + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + if ($response->getBody()) { + echo $response->getBody(); + // JSON string: { ... } + } + +The body used in request and response objects is a +``GuzzleHttp\Stream\StreamInterface``. This stream is used for both uploading +data and downloading data. Guzzle will, by default, store the body of a message +in a stream that uses PHP temp streams. When the size of the body exceeds +2 MB, the stream will automatically switch to storing data on disk rather than +in memory (protecting your application from memory exhaustion). + +You can change the body used in a request or response using the ``setBody()`` +method: + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + $request = $client->createRequest('PUT', 'http://httpbin.org/put'); + $request->setBody(Stream::factory('foo')); + +The easiest way to create a body for a request is using the static +``GuzzleHttp\Stream\Stream::factory()`` method. This method accepts various +inputs like strings, resources returned from ``fopen()``, and other +``GuzzleHttp\Stream\StreamInterface`` objects. + +The body of a request or response can be cast to a string or you can read and +write bytes off of the stream as needed. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + $request = $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'testing...']); + + echo $request->getBody()->read(4); + // test + echo $request->getBody()->read(4); + // ing. + echo $request->getBody()->read(1024); + // .. + var_export($request->eof()); + // true + +You can find out more about Guzzle stream objects in :doc:`streams`. + +Requests +======== + +Requests are sent from a client to a server. Requests include the method to +be applied to a resource, the identifier of the resource, and the protocol +version to use. + +Clients are used to create request messages. More precisely, clients use +a ``GuzzleHttp\Message\MessageFactoryInterface`` to create request messages. +You create requests with a client using the ``createRequest()`` method. + +.. code-block:: php + + // Create a request but don't send it immediately + $request = $client->createRequest('GET', 'http://httpbin.org/get'); + +Request Methods +--------------- + +When creating a request, you are expected to provide the HTTP method you wish +to perform. You can specify any method you'd like, including a custom method +that might not be part of RFC 7231 (like "MOVE"). + +.. code-block:: php + + // Create a request using a completely custom HTTP method + $request = $client->createRequest('MOVE', 'http://httpbin.org/move', ['exceptions' => false]); + + echo $request->getMethod(); + // MOVE + + $response = $client->send($request); + echo $response->getStatusCode(); + // 405 + +You can create and send a request using methods on a client that map to the +HTTP method you wish to use. + +:GET: ``$client->get('http://httpbin.org/get', [/** options **/])`` +:POST: ``$client->post('http://httpbin.org/post', [/** options **/])`` +:HEAD: ``$client->head('http://httpbin.org/get', [/** options **/])`` +:PUT: ``$client->put('http://httpbin.org/put', [/** options **/])`` +:DELETE: ``$client->delete('http://httpbin.org/delete', [/** options **/])`` +:OPTIONS: ``$client->options('http://httpbin.org/get', [/** options **/])`` +:PATCH: ``$client->patch('http://httpbin.org/put', [/** options **/])`` + +.. code-block:: php + + $response = $client->patch('http://httpbin.org/patch', ['body' => 'content']); + +Request URI +----------- + +The resource you are requesting with an HTTP request is identified by the +path of the request, the query string, and the "Host" header of the request. + +When creating a request, you can provide the entire resource URI as a URL. + +.. code-block:: php + + $response = $client->get('http://httbin.org/get?q=foo'); + +Using the above code, you will send a request that uses ``httpbin.org`` as +the Host header, sends the request over port 80, uses ``/get`` as the path, +and sends ``?q=foo`` as the query string. All of this is parsed automatically +from the provided URI. + +Sometimes you don't know what the entire request will be when it is created. +In these cases, you can modify the request as needed before sending it using +the ``createRequest()`` method of the client and methods on the request that +allow you to change it. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + +You can change the path of the request using ``setPath()``: + +.. code-block:: php + + $request->setPath('/get'); + echo $request->getPath(); + // /get + echo $request->getUrl(); + // http://httpbin.com/get + +Scheme +~~~~~~ + +The `scheme `_ of a request +specifies the protocol to use when sending the request. When using Guzzle, the +scheme can be set to "http" or "https". + +You can change the scheme of the request using the ``setScheme()`` method: + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $request->setScheme('https'); + echo $request->getScheme(); + // https + echo $request->getUrl(); + // https://httpbin.com/get + +Port +~~~~ + +No port is necessary when using the "http" or "https" schemes, but you can +override the port using ``setPort()``. If you need to modify the port used with +the specified scheme from the default setting, then you must use the +``setPort()`` method. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $request->setPort(8080); + echo $request->getPort(); + // 8080 + echo $request->getUrl(); + // https://httpbin.com:8080/get + + // Set the port back to the default value for the scheme + $request->setPort(443); + echo $request->getUrl(); + // https://httpbin.com/get + +Query string +~~~~~~~~~~~~ + +You can get the query string of the request using the ``getQuery()`` method. +This method returns a ``GuzzleHttp\Query`` object. A Query object can be +accessed like a PHP array, iterated in a foreach statement like a PHP array, +and cast to a string. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $query = $request->getQuery(); + $query['foo'] = 'bar'; + $query['baz'] = 'bam'; + $query['bam'] = ['test' => 'abc']; + + echo $request->getQuery(); + // foo=bar&baz=bam&bam%5Btest%5D=abc + + echo $request->getQuery()['foo']; + // bar + echo $request->getQuery()->get('foo'); + // bar + echo $request->getQuery()->get('foo'); + // bar + + var_export($request->getQuery()['bam']); + // array('test' => 'abc') + + foreach ($query as $key => $value) { + var_export($value); + } + + echo $request->getUrl(); + // https://httpbin.com/get?foo=bar&baz=bam&bam%5Btest%5D=abc + +Query Aggregators +^^^^^^^^^^^^^^^^^ + +Query objects can store scalar values or arrays of values. When an array of +values is added to a query object, the query object uses a query aggregator to +convert the complex structure into a string. Query objects will use +`PHP style query strings `_ when complex +query string parameters are converted to a string. You can customize how +complex query string parameters are aggregated using the ``setAggregator()`` +method of a query string object. + +.. code-block:: php + + $query->setAggregator($query::duplicateAggregator()); + +In the above example, we've changed the query object to use the +"duplicateAggregator". This aggregator will allow duplicate entries to appear +in a query string rather than appending "[n]" to each value. So if you had a +query string with ``['a' => ['b', 'c']]``, the duplicate aggregator would +convert this to "a=b&a=c" while the default aggregator would convert this to +"a[0]=b&a[1]=c" (with urlencoded brackets). + +The ``setAggregator()`` method accepts a ``callable`` which is used to convert +a deeply nested array of query string variables into a flattened array of key +value pairs. The callable accepts an array of query data and returns a +flattened array of key value pairs where each value is an array of strings. +You can use the ``GuzzleHttp\Query::walkQuery()`` static function to easily +create custom query aggregators. + +Host +~~~~ + +You can change the host header of the request in a predictable way using the +``setHost()`` method of a request: + +.. code-block:: php + + $request->setHost('www.google.com'); + echo $request->getHost(); + // www.google.com + echo $request->getUrl(); + // https://www.google.com/get?foo=bar&baz=bam + +.. note:: + + The Host header can also be changed by modifying the Host header of a + request directly, but modifying the Host header directly could result in + sending a request to a different Host than what is specified in the Host + header (sometimes this is actually the desired behavior). + +Resource +~~~~~~~~ + +You can use the ``getResource()`` method of a request to return the path and +query string of a request in a single string. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org/get?baz=bar'); + echo $request->getResource(); + // /get?baz=bar + +Request Config +-------------- + +Request messages contain a configuration collection that can be used by +event listeners and HTTP handlers to modify how a request behaves or is +transferred over the wire. For example, many of the request options that are +specified when creating a request are actually set as config options that are +only acted upon by handlers and listeners when the request is sent. + +You can get access to the request's config object using the ``getConfig()`` +method of a request. + +.. code-block:: php + + $request = $client->createRequest('GET', '/'); + $config = $request->getConfig(); + +The config object is a ``GuzzleHttp\Collection`` object that acts like +an associative array. You can grab values from the collection using array like +access. You can also modify and remove values using array like access. + +.. code-block:: php + + $config['foo'] = 'bar'; + echo $config['foo']; + // bar + + var_export(isset($config['foo'])); + // true + + unset($config['foo']); + var_export(isset($config['foo'])); + // false + + var_export($config['foo']); + // NULL + +HTTP handlers and event listeners can expose additional customization options +through request config settings. For example, in order to specify custom cURL +options to the cURL handler, you need to specify an associative array in the +``curl`` ``config`` request option. + +.. code-block:: php + + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_HTTPAUTH => CURLAUTH_NTLM, + CURLOPT_USERPWD => 'username:password' + ] + ] + ]); + +Consult the HTTP handlers and event listeners you are using to see if they +allow customization through request configuration options. + +Event Emitter +------------- + +Request objects implement ``GuzzleHttp\Event\HasEmitterInterface``, so they +have a method called ``getEmitter()`` that can be used to get an event emitter +used by the request. Any listener or subscriber attached to a request will only +be triggered for the lifecycle events of a specific request. Conversely, adding +an event listener or subscriber to a client will listen to all lifecycle events +of all requests created by the client. + +See :doc:`events` for more information. + +Responses +========= + +Responses are the HTTP messages a client receives from a server after sending +an HTTP request message. + +Start-Line +---------- + +The start-line of a response contains the protocol and protocol version, +status code, and reason phrase. + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + echo $response->getStatusCode(); + // 200 + echo $response->getReasonPhrase(); + // OK + echo $response->getProtocolVersion(); + // 1.1 + +Body +---- + +As described earlier, you can get the body of a response using the +``getBody()`` method. + +.. code-block:: php + + if ($body = $response->getBody()) { + echo $body; + // Cast to a string: { ... } + $body->seek(0); + // Rewind the body + $body->read(1024); + // Read bytes of the body + } + +When working with JSON responses, you can use the ``json()`` method of a +response: + +.. code-block:: php + + $json = $response->json(); + +.. note:: + + Guzzle uses the ``json_decode()`` method of PHP and uses arrays rather than + ``stdClass`` objects for objects. + +You can use the ``xml()`` method when working with XML data. + +.. code-block:: php + + $xml = $response->xml(); + +.. note:: + + Guzzle uses the ``SimpleXMLElement`` objects when converting response + bodies to XML. + +Effective URL +------------- + +The URL that was ultimately accessed that returned a response can be accessed +using the ``getEffectiveUrl()`` method of a response. This method will return +the URL of a request or the URL of the last redirected URL if any redirects +occurred while transferring a request. + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + echo $response->getEffectiveUrl(); + // http://httpbin.org/get + + $response = GuzzleHttp\get('http://httpbin.org/redirect-to?url=http://www.google.com'); + echo $response->getEffectiveUrl(); + // http://www.google.com diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/index.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/index.rst new file mode 100755 index 000000000..d456a5f08 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/index.rst @@ -0,0 +1,98 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services + +====== +Guzzle +====== + +Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and +trivial to integrate with web services. + +- Manages things like persistent connections, represents query strings as + collections, simplifies sending streaming POST requests with fields and + files, and abstracts away the underlying HTTP transport layer. +- Can send both synchronous and asynchronous requests using the same interface + without requiring a dependency on a specific event loop. +- Pluggable HTTP handlers allows Guzzle to integrate with any method you choose + for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream + wrapper, non-blocking event loops like `React `_, etc.). +- Guzzle makes it so that you no longer need to fool around with cURL options, + stream contexts, or sockets. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $response = $client->get('http://guzzlephp.org'); + $res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); + echo $res->getStatusCode(); + // "200" + echo $res->getHeader('content-type'); + // 'application/json; charset=utf8' + echo $res->getBody(); + // {"type":"User"...' + var_export($res->json()); + // Outputs the JSON decoded data + + // Send an asynchronous request. + $req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]); + $client->send($req)->then(function ($response) { + echo 'I completed! ' . $response; + }); + +User guide +---------- + +.. toctree:: + :maxdepth: 2 + + overview + quickstart + clients + http-messages + events + streams + handlers + testing + faq + +HTTP Components +--------------- + +There are a number of optional libraries you can use along with Guzzle's HTTP +layer to add capabilities to the client. + +`Log Subscriber `_ + Logs HTTP requests and responses sent over the wire using customizable + log message templates. + +`OAuth Subscriber `_ + Signs requests using OAuth 1.0. + +`Cache Subscriber `_ + Implements a private transparent proxy cache that caches HTTP responses. + +`Retry Subscriber `_ + Retries failed requests using customizable retry strategies (e.g., retry + based on response status code, cURL error codes, etc.) + +`Message Integrity Subscriber `_ + Verifies the message integrity of HTTP responses using customizable + validators. This plugin can be used, for example, to verify the Content-MD5 + headers of responses. + +Service Description Commands +---------------------------- + +You can use the **Guzzle Command** library to encapsulate interaction with a +web service using command objects. Building on top of Guzzle's command +abstraction allows you to easily implement things like service description that +can be used to serialize requests and parse responses using a meta-description +of a web service. + +`Guzzle Command `_ + Provides the foundational elements used to build high-level, command based, + web service clients with Guzzle. + +`Guzzle Services `_ + Provides an implementation of the *Guzzle Command* library that uses + Guzzle service descriptions to describe web services, serialize requests, + and parse responses into easy to use model structures. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/overview.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/overview.rst new file mode 100755 index 000000000..1355afa12 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/overview.rst @@ -0,0 +1,150 @@ +======== +Overview +======== + +Requirements +============ + +#. PHP 5.4.0 +#. To use the PHP stream handler, ``allow_url_fopen`` must be enabled in your + system's php.ini. +#. To use the cURL handler, you must have a recent version of cURL >= 7.16.2 + compiled with OpenSSL and zlib. + +.. note:: + + Guzzle no longer requires cURL in order to send HTTP requests. Guzzle will + use the PHP stream wrapper to send HTTP requests if cURL is not installed. + Alternatively, you can provide your own HTTP handler used to send requests. + +.. _installation: + +Installation +============ + +The recommended way to install Guzzle is with `Composer `_. Composer is a dependency +management tool for PHP that allows you to declare the dependencies your project needs and installs them into your +project. + +.. code-block:: bash + + # Install Composer + curl -sS https://getcomposer.org/installer | php + +You can add Guzzle as a dependency using the composer.phar CLI: + +.. code-block:: bash + + php composer.phar require guzzlehttp/guzzle:~5.0 + +Alternatively, you can specify Guzzle as a dependency in your project's +existing composer.json file: + +.. code-block:: js + + { + "require": { + "guzzlehttp/guzzle": "~5.0" + } + } + +After installing, you need to require Composer's autoloader: + +.. code-block:: php + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and +other best-practices for defining dependencies at `getcomposer.org `_. + +Bleeding edge +------------- + +During your development, you can keep up with the latest changes on the master +branch by setting the version requirement for Guzzle to ``~5.0@dev``. + +.. code-block:: js + + { + "require": { + "guzzlehttp/guzzle": "~5.0@dev" + } + } + +License +======= + +Licensed using the `MIT license `_. + + Copyright (c) 2014 Michael Dowling + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Contributing +============ + +Guidelines +---------- + +1. Guzzle follows PSR-0, PSR-1, and PSR-2. +2. Guzzle is meant to be lean and fast with very few dependencies. +3. Guzzle has a minimum PHP version requirement of PHP 5.4. Pull requests must + not require a PHP version greater than PHP 5.4. +4. All pull requests must include unit tests to ensure the change works as + expected and to prevent regressions. + +Running the tests +----------------- + +In order to contribute, you'll need to checkout the source from GitHub and +install Guzzle's dependencies using Composer: + +.. code-block:: bash + + git clone https://github.com/guzzle/guzzle.git + cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev + +Guzzle is unit tested with PHPUnit. Run the tests using the vendored PHPUnit +binary: + +.. code-block:: bash + + vendor/bin/phpunit + +.. note:: + + You'll need to install node.js v0.5.0 or newer in order to perform + integration tests on Guzzle's HTTP handlers. + +Reporting a security vulnerability +================================== + +We want to ensure that Guzzle is a secure HTTP client library for everyone. If +you've discovered a security vulnerability in Guzzle, we appreciate your help +in disclosing it to us in a `responsible manner `_. + +Publicly disclosing a vulnerability can put the entire community at risk. If +you've discovered a security concern, please email us at +security@guzzlephp.org. We'll work with you to make sure that we understand the +scope of the issue, and that we fully address your concern. We consider +correspondence sent to security@guzzlephp.org our highest priority, and work to +address any issues that arise as quickly as possible. + +After a security vulnerability has been corrected, a security hotfix release will +be deployed as soon as possible. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/quickstart.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/quickstart.rst new file mode 100755 index 000000000..65a70ed23 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/quickstart.rst @@ -0,0 +1,448 @@ +========== +Quickstart +========== + +This page provides a quick introduction to Guzzle and introductory examples. +If you have not already installed, Guzzle, head over to the :ref:`installation` +page. + +Make a Request +============== + +You can send requests with Guzzle using a ``GuzzleHttp\ClientInterface`` +object. + +Creating a Client +----------------- + +The procedural API is simple but not very testable; it's best left for quick +prototyping. If you want to use Guzzle in a more flexible and testable way, +then you'll need to use a ``GuzzleHttp\ClientInterface`` object. + +.. code-block:: php + + use GuzzleHttp\Client; + + $client = new Client(); + $response = $client->get('http://httpbin.org/get'); + + // You can use the same methods you saw in the procedural API + $response = $client->delete('http://httpbin.org/delete'); + $response = $client->head('http://httpbin.org/get'); + $response = $client->options('http://httpbin.org/get'); + $response = $client->patch('http://httpbin.org/patch'); + $response = $client->post('http://httpbin.org/post'); + $response = $client->put('http://httpbin.org/put'); + +You can create a request with a client and then send the request with the +client when you're ready. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://www.foo.com'); + $response = $client->send($request); + +Client objects provide a great deal of flexibility in how request are +transferred including default request options, subscribers that are attached +to each request, and a base URL that allows you to send requests with relative +URLs. You can find out all about clients in the :doc:`clients` page of the +documentation. + +Using Responses +=============== + +In the previous examples, we retrieved a ``$response`` variable. This value is +actually a ``GuzzleHttp\Message\ResponseInterface`` object and contains lots +of helpful information. + +You can get the status code and reason phrase of the response. + +.. code-block:: php + + $code = $response->getStatusCode(); + // 200 + + $reason = $response->getReasonPhrase(); + // OK + +By providing the ``future`` request option to a request, you can send requests +asynchronously using the promise interface of a future response. + +.. code-block:: php + + $client->get('http://httpbin.org', ['future' => true]) + ->then(function ($response) { + echo $response->getStatusCode(); + }); + +Response Body +------------- + +The body of a response can be retrieved and cast to a string. + +.. code-block:: php + + $body = $response->getBody(); + echo $body; + // { "some_json_data" ...} + +You can also read read bytes from body of a response like a stream. + +.. code-block:: php + + $body = $response->getBody(); + + while (!$body->eof()) { + echo $body->read(1024); + } + +JSON Responses +~~~~~~~~~~~~~~ + +You can more easily work with JSON responses using the ``json()`` method of a +response. + +.. code-block:: php + + $response = $client->get('http://httpbin.org/get'); + $json = $response->json(); + var_dump($json[0]['origin']); + +Guzzle internally uses PHP's ``json_decode()`` function to parse responses. If +Guzzle is unable to parse the JSON response body, then a +``GuzzleHttp\Exception\ParseException`` is thrown. + +XML Responses +~~~~~~~~~~~~~ + +You can use a response's ``xml()`` method to more easily work with responses +that contain XML data. + +.. code-block:: php + + $response = $client->get('https://github.com/mtdowling.atom'); + $xml = $response->xml(); + echo $xml->id; + // tag:github.com,2008:/mtdowling + +Guzzle internally uses a ``SimpleXMLElement`` object to parse responses. If +Guzzle is unable to parse the XML response body, then a +``GuzzleHttp\Exception\ParseException`` is thrown. + +Query String Parameters +======================= + +Sending query string parameters with a request is easy. You can set query +string parameters in the request's URL. + +.. code-block:: php + + $response = $client->get('http://httpbin.org?foo=bar'); + +You can also specify the query string parameters using the ``query`` request +option. + +.. code-block:: php + + $client->get('http://httpbin.org', [ + 'query' => ['foo' => 'bar'] + ]); + +And finally, you can build up the query string of a request as needed by +calling the ``getQuery()`` method of a request and modifying the request's +``GuzzleHttp\Query`` object as needed. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org'); + $query = $request->getQuery(); + $query->set('foo', 'bar'); + + // You can use the query string object like an array + $query['baz'] = 'bam'; + + // The query object can be cast to a string + echo $query; + // foo=bar&baz=bam + + // Setting a value to false or null will cause the "=" sign to be omitted + $query['empty'] = null; + echo $query; + // foo=bar&baz=bam&empty + + // Use an empty string to include the "=" sign with an empty value + $query['empty'] = ''; + echo $query; + // foo=bar&baz=bam&empty= + +.. _headers: + +Request and Response Headers +---------------------------- + +You can specify request headers when sending or creating requests with a +client. In the following example, we send the ``X-Foo-Header`` with a value of +``value`` by setting the ``headers`` request option. + +.. code-block:: php + + $response = $client->get('http://httpbin.org/get', [ + 'headers' => ['X-Foo-Header' => 'value'] + ]); + +You can view the headers of a response using header specific methods of a +response class. Headers work exactly the same way for request and response +object. + +You can retrieve a header from a request or response using the ``getHeader()`` +method of the object. This method is case-insensitive and by default will +return a string containing the header field value. + +.. code-block:: php + + $response = $client->get('http://www.yahoo.com'); + $length = $response->getHeader('Content-Length'); + +Header fields that contain multiple values can be retrieved as a string or as +an array. Retrieving the field values as a string will naively concatenate all +of the header values together with a comma. Because not all header fields +should be represented this way (e.g., ``Set-Cookie``), you can pass an optional +flag to the ``getHeader()`` method to retrieve the header values as an array. + +.. code-block:: php + + $values = $response->getHeader('Set-Cookie', true); + foreach ($values as $value) { + echo $value; + } + +You can test if a request or response has a specific header using the +``hasHeader()`` method. This method accepts a case-insensitive string and +returns true if the header is present or false if it is not. + +You can retrieve all of the headers of a message using the ``getHeaders()`` +method of a request or response. The return value is an associative array where +the keys represent the header name as it will be sent over the wire, and each +value is an array of strings associated with the header. + +.. code-block:: php + + $headers = $response->getHeaders(); + foreach ($message->getHeaders() as $name => $values) { + echo $name . ": " . implode(", ", $values); + } + +Modifying headers +----------------- + +The headers of a message can be modified using the ``setHeader()``, +``addHeader()``, ``setHeaders()``, and ``removeHeader()`` methods of a request +or response object. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org/get'); + + // Set a single value for a header + $request->setHeader('User-Agent', 'Testing!'); + + // Set multiple values for a header in one call + $request->setHeader('X-Foo', ['Baz', 'Bar']); + + // Add a header to the message + $request->addHeader('X-Foo', 'Bam'); + + echo $request->getHeader('X-Foo'); + // Baz, Bar, Bam + + // Remove a specific header using a case-insensitive name + $request->removeHeader('x-foo'); + echo $request->getHeader('X-Foo'); + // Echoes an empty string: '' + +Uploading Data +============== + +Guzzle provides several methods of uploading data. + +You can send requests that contain a stream of data by passing a string, +resource returned from ``fopen``, or a ``GuzzleHttp\Stream\StreamInterface`` +object to the ``body`` request option. + +.. code-block:: php + + $r = $client->post('http://httpbin.org/post', ['body' => 'raw data']); + +You can easily upload JSON data using the ``json`` request option. + +.. code-block:: php + + $r = $client->put('http://httpbin.org/put', ['json' => ['foo' => 'bar']]); + +POST Requests +------------- + +In addition to specifying the raw data of a request using the ``body`` request +option, Guzzle provides helpful abstractions over sending POST data. + +Sending POST Fields +~~~~~~~~~~~~~~~~~~~ + +Sending ``application/x-www-form-urlencoded`` POST requests requires that you +specify the body of a POST request as an array. + +.. code-block:: php + + $response = $client->post('http://httpbin.org/post', [ + 'body' => [ + 'field_name' => 'abc', + 'other_field' => '123' + ] + ]); + +You can also build up POST requests before sending them. + +.. code-block:: php + + $request = $client->createRequest('POST', 'http://httpbin.org/post'); + $postBody = $request->getBody(); + + // $postBody is an instance of GuzzleHttp\Post\PostBodyInterface + $postBody->setField('foo', 'bar'); + echo $postBody->getField('foo'); + // 'bar' + + echo json_encode($postBody->getFields()); + // {"foo": "bar"} + + // Send the POST request + $response = $client->send($request); + +Sending POST Files +~~~~~~~~~~~~~~~~~~ + +Sending ``multipart/form-data`` POST requests (POST requests that contain +files) is the same as sending ``application/x-www-form-urlencoded``, except +some of the array values of the POST fields map to PHP ``fopen`` resources, or +``GuzzleHttp\Stream\StreamInterface``, or +``GuzzleHttp\Post\PostFileInterface`` objects. + +.. code-block:: php + + use GuzzleHttp\Post\PostFile; + + $response = $client->post('http://httpbin.org/post', [ + 'body' => [ + 'field_name' => 'abc', + 'file_filed' => fopen('/path/to/file', 'r'), + 'other_file' => new PostFile('other_file', 'this is the content') + ] + ]); + +Just like when sending POST fields, you can also build up POST requests with +files before sending them. + +.. code-block:: php + + use GuzzleHttp\Post\PostFile; + + $request = $client->createRequest('POST', 'http://httpbin.org/post'); + $postBody = $request->getBody(); + $postBody->setField('foo', 'bar'); + $postBody->addFile(new PostFile('test', fopen('/path/to/file', 'r'))); + $response = $client->send($request); + +Cookies +======= + +Guzzle can maintain a cookie session for you if instructed using the +``cookies`` request option. + +- Set to ``true`` to use a shared cookie session associated with the client. +- Pass an associative array containing cookies to send in the request and start + a new cookie session. +- Set to a ``GuzzleHttp\Subscriber\CookieJar\CookieJarInterface`` object to use + an existing cookie jar. + +Redirects +========= + +Guzzle will automatically follow redirects unless you tell it not to. You can +customize the redirect behavior using the ``allow_redirects`` request option. + +- Set to true to enable normal redirects with a maximum number of 5 redirects. + This is the default setting. +- Set to false to disable redirects. +- Pass an associative array containing the 'max' key to specify the maximum + number of redirects and optionally provide a 'strict' key value to specify + whether or not to use strict RFC compliant redirects (meaning redirect POST + requests with POST requests vs. doing what most browsers do which is + redirect POST requests with GET requests). + +.. code-block:: php + + $response = $client->get('http://github.com'); + echo $response->getStatusCode(); + // 200 + echo $response->getEffectiveUrl(); + // 'https://github.com/' + +The following example shows that redirects can be disabled. + +.. code-block:: php + + $response = $client->get('http://github.com', ['allow_redirects' => false]); + echo $response->getStatusCode(); + // 301 + echo $response->getEffectiveUrl(); + // 'http://github.com/' + +Exceptions +========== + +Guzzle throws exceptions for errors that occur during a transfer. + +- In the event of a networking error (connection timeout, DNS errors, etc.), + a ``GuzzleHttp\Exception\RequestException`` is thrown. This exception + extends from ``GuzzleHttp\Exception\TransferException``. Catching this + exception will catch any exception that can be thrown while transferring + (non-parallel) requests. + + .. code-block:: php + + use GuzzleHttp\Exception\RequestException; + + try { + $client->get('https://github.com/_abc_123_404'); + } catch (RequestException $e) { + echo $e->getRequest(); + if ($e->hasResponse()) { + echo $e->getResponse(); + } + } + +- A ``GuzzleHttp\Exception\ClientException`` is thrown for 400 + level errors if the ``exceptions`` request option is set to true. This + exception extends from ``GuzzleHttp\Exception\BadResponseException`` and + ``GuzzleHttp\Exception\BadResponseException`` extends from + ``GuzzleHttp\Exception\RequestException``. + + .. code-block:: php + + use GuzzleHttp\Exception\ClientException; + + try { + $client->get('https://github.com/_abc_123_404'); + } catch (ClientException $e) { + echo $e->getRequest(); + echo $e->getResponse(); + } + +- A ``GuzzleHttp\Exception\ServerException`` is thrown for 500 level + errors if the ``exceptions`` request option is set to true. This + exception extends from ``GuzzleHttp\Exception\BadResponseException``. +- A ``GuzzleHttp\Exception\TooManyRedirectsException`` is thrown when too + many redirects are followed. This exception extends from ``GuzzleHttp\Exception\RequestException``. + +All of the above exceptions extend from +``GuzzleHttp\Exception\TransferException``. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/requirements.txt b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/requirements.txt new file mode 100755 index 000000000..fe7a4eab4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/requirements.txt @@ -0,0 +1,2 @@ +Sphinx>=1.2b1 +guzzle_sphinx_theme>=0.6.0 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/streams.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/streams.rst new file mode 100755 index 000000000..8fe9a6984 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/streams.rst @@ -0,0 +1,213 @@ +======= +Streams +======= + +Guzzle uses stream objects to represent request and response message bodies. +These stream objects allow you to work with various types of data all using a +common interface. + +HTTP messages consist of a start-line, headers, and a body. The body of an HTTP +message can be very small or extremely large. Attempting to represent the body +of a message as a string can easily consume more memory than intended because +the body must be stored completely in memory. Attempting to store the body of a +request or response in memory would preclude the use of that implementation from +being able to work with large message bodies. The StreamInterface is used in +order to hide the implementation details of where a stream of data is read from +or written to. + +Guzzle's StreamInterface exposes several methods that enable streams to be read +from, written to, and traversed effectively. + +Streams expose their capabilities using three methods: ``isReadable()``, +``isWritable()``, and ``isSeekable()``. These methods can be used by stream +collaborators to determine if a stream is capable of their requirements. + +Each stream instance has various capabilities: they can be read-only, +write-only, read-write, allow arbitrary random access (seeking forwards or +backwards to any location), or only allow sequential access (for example in the +case of a socket or pipe). + +Creating Streams +================ + +The best way to create a stream is using the static factory method, +``GuzzleHttp\Stream\Stream::factory()``. This factory accepts strings, +resources returned from ``fopen()``, an object that implements +``__toString()``, and an object that implements +``GuzzleHttp\Stream\StreamInterface``. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + + $stream = Stream::factory('string data'); + echo $stream; + // string data + echo $stream->read(3); + // str + echo $stream->getContents(); + // ing data + var_export($stream->eof()); + // true + var_export($stream->tell()); + // 11 + +Metadata +======== + +Guzzle streams expose stream metadata through the ``getMetadata()`` method. +This method provides the data you would retrieve when calling PHP's +`stream_get_meta_data() function `_, +and can optionally expose other custom data. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + + $resource = fopen('/path/to/file', 'r'); + $stream = Stream::factory($resource); + echo $stream->getMetadata('uri'); + // /path/to/file + var_export($stream->isReadable()); + // true + var_export($stream->isWritable()); + // false + var_export($stream->isSeekable()); + // true + +Stream Decorators +================= + +With the small and focused interface, add custom functionality to streams is +very simple with stream decorators. Guzzle provides several built-in decorators +that provide additional stream functionality. + +CachingStream +------------- + +The CachingStream is used to allow seeking over previously read bytes on +non-seekable streams. This can be useful when transferring a non-seekable +entity body fails due to needing to rewind the stream (for example, resulting +from a redirect). Data that is read from the remote stream will be buffered in +a PHP temp stream so that previously read bytes are cached first in memory, +then on disk. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + use GuzzleHttp\Stream\CachingStream; + + $original = Stream::factory(fopen('http://www.google.com', 'r')); + $stream = new CachingStream($original); + + $stream->read(1024); + echo $stream->tell(); + // 1024 + + $stream->seek(0); + echo $stream->tell(); + // 0 + +LimitStream +----------- + +LimitStream can be used to read a subset or slice of an existing stream object. +This can be useful for breaking a large file into smaller pieces to be sent in +chunks (e.g. Amazon S3's multipart upload API). + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + use GuzzleHttp\Stream\LimitStream; + + $original = Stream::factory(fopen('/tmp/test.txt', 'r+')); + echo $original->getSize(); + // >>> 1048576 + + // Limit the size of the body to 1024 bytes and start reading from byte 2048 + $stream = new LimitStream($original, 1024, 2048); + echo $stream->getSize(); + // >>> 1024 + echo $stream->tell(); + // >>> 0 + +NoSeekStream +------------ + +NoSeekStream wraps a stream and does not allow seeking. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + use GuzzleHttp\Stream\LimitStream; + + $original = Stream::factory('foo'); + $noSeek = new NoSeekStream($original); + + echo $noSeek->read(3); + // foo + var_export($noSeek->isSeekable()); + // false + $noSeek->seek(0); + var_export($noSeek->read(3)); + // NULL + +Creating Custom Decorators +-------------------------- + +Creating a stream decorator is very easy thanks to the +``GuzzleHttp\Stream\StreamDecoratorTrait``. This trait provides methods that +implement ``GuzzleHttp\Stream\StreamInterface`` by proxying to an underlying +stream. Just ``use`` the ``StreamDecoratorTrait`` and implement your custom +methods. + +For example, let's say we wanted to call a specific function each time the last +byte is read from a stream. This could be implemented by overriding the +``read()`` method. + +.. code-block:: php + + use GuzzleHttp\Stream\StreamDecoratorTrait; + + class EofCallbackStream implements StreamInterface + { + use StreamDecoratorTrait; + + private $callback; + + public function __construct(StreamInterface $stream, callable $callback) + { + $this->stream = $stream; + $this->callback = $callback; + } + + public function read($length) + { + $result = $this->stream->read($length); + + // Invoke the callback when EOF is hit. + if ($this->eof()) { + call_user_func($this->callback); + } + + return $result; + } + } + +This decorator could be added to any existing stream and used like so: + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + + $original = Stream::factory('foo'); + $eofStream = new EofCallbackStream($original, function () { + echo 'EOF!'; + }); + + $eofStream->read(2); + $eofStream->read(1); + // echoes "EOF!" + $eofStream->seek(0); + $eofStream->read(3); + // echoes "EOF!" diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/testing.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/testing.rst new file mode 100755 index 000000000..03bcc2ee1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/docs/testing.rst @@ -0,0 +1,232 @@ +====================== +Testing Guzzle Clients +====================== + +Guzzle provides several tools that will enable you to easily mock the HTTP +layer without needing to send requests over the internet. + +* Mock subscriber +* Mock handler +* Node.js web server for integration testing + +Mock Subscriber +=============== + +When testing HTTP clients, you often need to simulate specific scenarios like +returning a successful response, returning an error, or returning specific +responses in a certain order. Because unit tests need to be predictable, easy +to bootstrap, and fast, hitting an actual remote API is a test smell. + +Guzzle provides a mock subscriber that can be attached to clients or requests +that allows you to queue up a list of responses to use rather than hitting a +remote API. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Subscriber\Mock; + use GuzzleHttp\Message\Response; + + $client = new Client(); + + // Create a mock subscriber and queue two responses. + $mock = new Mock([ + new Response(200, ['X-Foo' => 'Bar']), // Use response object + "HTTP/1.1 202 OK\r\nContent-Length: 0\r\n\r\n" // Use a response string + ]); + + // Add the mock subscriber to the client. + $client->getEmitter()->attach($mock); + // The first request is intercepted with the first response. + echo $client->get('/')->getStatusCode(); + //> 200 + // The second request is intercepted with the second response. + echo $client->get('/')->getStatusCode(); + //> 202 + +When no more responses are in the queue and a request is sent, an +``OutOfBoundsException`` is thrown. + +History Subscriber +================== + +When using things like the ``Mock`` subscriber, you often need to know if the +requests you expected to send were sent exactly as you intended. While the mock +subscriber responds with mocked responses, the ``GuzzleHttp\Subscriber\History`` +subscriber maintains a history of the requests that were sent by a client. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Subscriber\History; + + $client = new Client(); + $history = new History(); + + // Add the history subscriber to the client. + $client->getEmitter()->attach($history); + + $client->get('http://httpbin.org/get'); + $client->head('http://httpbin.org/get'); + + // Count the number of transactions + echo count($history); + //> 2 + // Get the last request + $lastRequest = $history->getLastRequest(); + // Get the last response + $lastResponse = $history->getLastResponse(); + + // Iterate over the transactions that were sent + foreach ($history as $transaction) { + echo $transaction['request']->getMethod(); + //> GET, HEAD + echo $transaction['response']->getStatusCode(); + //> 200, 200 + } + +The history subscriber can also be printed, revealing the requests and +responses that were sent as a string, in order. + +.. code-block:: php + + echo $history; + +:: + + > GET /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 + + < HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Content-Type: application/json + Date: Tue, 25 Mar 2014 03:53:27 GMT + Server: gunicorn/0.17.4 + Content-Length: 270 + Connection: keep-alive + + { + "headers": { + "Connection": "close", + "X-Request-Id": "3d0f7d5c-c937-4394-8248-2b8e03fcccdb", + "User-Agent": "Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8", + "Host": "httpbin.org" + }, + "origin": "76.104.247.1", + "args": {}, + "url": "http://httpbin.org/get" + } + + > HEAD /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 + + < HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Content-length: 270 + Content-Type: application/json + Date: Tue, 25 Mar 2014 03:53:27 GMT + Server: gunicorn/0.17.4 + Connection: keep-alive + +Mock Adapter +============ + +In addition to using the Mock subscriber, you can use the +``GuzzleHttp\Ring\Client\MockHandler`` as the handler of a client to return the +same response over and over or return the result of a callable function. + +Test Web Server +=============== + +Using mock responses is almost always enough when testing a web service client. +When implementing custom :doc:`HTTP handlers `, you'll need to send +actual HTTP requests in order to sufficiently test the handler. However, a +best practice is to contact a local web server rather than a server over the +internet. + +- Tests are more reliable +- Tests do not require a network connection +- Tests have no external dependencies + +Using the test server +--------------------- + +.. warning:: + + The following functionality is provided to help developers of Guzzle + develop HTTP handlers. There is no promise of backwards compatibility + when it comes to the node.js test server or the ``GuzzleHttp\Tests\Server`` + class. If you are using the test server or ``Server`` class outside of + guzzlehttp/guzzle, then you will need to configure autoloading and + ensure the web server is started manually. + +.. hint:: + + You almost never need to use this test web server. You should only ever + consider using it when developing HTTP handlers. The test web server + is not necessary for mocking requests. For that, please use the + Mock subcribers and History subscriber. + +Guzzle ships with a node.js test server that receives requests and returns +responses from a queue. The test server exposes a simple API that is used to +enqueue responses and inspect the requests that it has received. + +Any operation on the ``Server`` object will ensure that +the server is running and wait until it is able to receive requests before +returning. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Tests\Server; + + // Start the server and queue a response + Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); + + $client = new Client(['base_url' => Server::$url]); + echo $client->get('/foo')->getStatusCode(); + // 200 + +``GuzzleHttp\Tests\Server`` provides a static interface to the test server. You +can queue an HTTP response or an array of responses by calling +``Server::enqueue()``. This method accepts a string representing an HTTP +response message, a ``GuzzleHttp\Message\ResponseInterface``, or an array of +HTTP message strings / ``GuzzleHttp\Message\ResponseInterface`` objects. + +.. code-block:: php + + // Queue single response + Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); + + // Clear the queue and queue an array of responses + Server::enqueue([ + "HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n", + "HTTP/1.1 404 Not Found\r\n\Content-Length: 0r\n\r\n" + ]); + +When a response is queued on the test server, the test server will remove any +previously queued responses. As the server receives requests, queued responses +are dequeued and returned to the request. When the queue is empty, the server +will return a 500 response. + +You can inspect the requests that the server has retrieved by calling +``Server::received()``. This method accepts an optional ``$hydrate`` parameter +that specifies if you are retrieving an array of HTTP requests as strings or an +array of ``GuzzleHttp\Message\RequestInterface`` objects. + +.. code-block:: php + + foreach (Server::received() as $response) { + echo $response; + } + +You can clear the list of received requests from the web server using the +``Server::flush()`` method. + +.. code-block:: php + + Server::flush(); + echo count(Server::received()); + // 0 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/phpunit.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/phpunit.xml.dist new file mode 100755 index 000000000..500cd53a0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + + tests + + + + + src + + src/ + + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/BatchResults.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/BatchResults.php new file mode 100755 index 000000000..e5af433dd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/BatchResults.php @@ -0,0 +1,148 @@ +hash = $hash; + } + + /** + * Get the keys that are available on the batch result. + * + * @return array + */ + public function getKeys() + { + return iterator_to_array($this->hash); + } + + /** + * Gets a result from the container for the given object. When getting + * results for a batch of requests, provide the request object. + * + * @param object $forObject Object to retrieve the result for. + * + * @return mixed|null + */ + public function getResult($forObject) + { + return isset($this->hash[$forObject]) ? $this->hash[$forObject] : null; + } + + /** + * Get an array of successful results. + * + * @return array + */ + public function getSuccessful() + { + $results = []; + foreach ($this->hash as $key) { + if (!($this->hash[$key] instanceof \Exception)) { + $results[] = $this->hash[$key]; + } + } + + return $results; + } + + /** + * Get an array of failed results. + * + * @return array + */ + public function getFailures() + { + $results = []; + foreach ($this->hash as $key) { + if ($this->hash[$key] instanceof \Exception) { + $results[] = $this->hash[$key]; + } + } + + return $results; + } + + /** + * Allows iteration over all batch result values. + * + * @return \ArrayIterator + */ + public function getIterator() + { + $results = []; + foreach ($this->hash as $key) { + $results[] = $this->hash[$key]; + } + + return new \ArrayIterator($results); + } + + /** + * Counts the number of elements in the batch result. + * + * @return int + */ + public function count() + { + return count($this->hash); + } + + /** + * Checks if the batch contains a specific numerical array index. + * + * @param int $key Index to access + * + * @return bool + */ + public function offsetExists($key) + { + return $key < count($this->hash); + } + + /** + * Allows access of the batch using a numerical array index. + * + * @param int $key Index to access. + * + * @return mixed|null + */ + public function offsetGet($key) + { + $i = -1; + foreach ($this->hash as $obj) { + if ($key === ++$i) { + return $this->hash[$obj]; + } + } + + return null; + } + + public function offsetUnset($key) + { + throw new \RuntimeException('Not implemented'); + } + + public function offsetSet($key, $value) + { + throw new \RuntimeException('Not implemented'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Client.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Client.php new file mode 100755 index 000000000..b5ed11ff0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Client.php @@ -0,0 +1,352 @@ + [ + * 'http://www.foo.com/{version}/', + * ['version' => '123'] + * ], + * 'defaults' => [ + * 'timeout' => 10, + * 'allow_redirects' => false, + * 'proxy' => '192.168.16.1:10' + * ] + * ]); + * + * @param array $config Client configuration settings + * - base_url: Base URL of the client that is merged into relative URLs. + * Can be a string or an array that contains a URI template followed + * by an associative array of expansion variables to inject into the + * URI template. + * - handler: callable RingPHP handler used to transfer requests + * - message_factory: Factory used to create request and response object + * - defaults: Default request options to apply to each request + * - emitter: Event emitter used for request events + * - fsm: (internal use only) The request finite state machine. A + * function that accepts a transaction and optional final state. The + * function is responsible for transitioning a request through its + * lifecycle events. + */ + public function __construct(array $config = []) + { + $this->configureBaseUrl($config); + $this->configureDefaults($config); + + if (isset($config['emitter'])) { + $this->emitter = $config['emitter']; + } + + $this->messageFactory = isset($config['message_factory']) + ? $config['message_factory'] + : new MessageFactory(); + + if (isset($config['fsm'])) { + $this->fsm = $config['fsm']; + } else { + if (isset($config['handler'])) { + $handler = $config['handler']; + } elseif (isset($config['adapter'])) { + $handler = $config['adapter']; + } else { + $handler = Utils::getDefaultHandler(); + } + $this->fsm = new RequestFsm($handler, $this->messageFactory); + } + } + + public function getDefaultOption($keyOrPath = null) + { + return $keyOrPath === null + ? $this->defaults + : Utils::getPath($this->defaults, $keyOrPath); + } + + public function setDefaultOption($keyOrPath, $value) + { + Utils::setPath($this->defaults, $keyOrPath, $value); + } + + public function getBaseUrl() + { + return (string) $this->baseUrl; + } + + public function createRequest($method, $url = null, array $options = []) + { + $options = $this->mergeDefaults($options); + // Use a clone of the client's emitter + $options['config']['emitter'] = clone $this->getEmitter(); + $url = $url || (is_string($url) && strlen($url)) + ? $this->buildUrl($url) + : (string) $this->baseUrl; + + return $this->messageFactory->createRequest($method, $url, $options); + } + + public function get($url = null, $options = []) + { + return $this->send($this->createRequest('GET', $url, $options)); + } + + public function head($url = null, array $options = []) + { + return $this->send($this->createRequest('HEAD', $url, $options)); + } + + public function delete($url = null, array $options = []) + { + return $this->send($this->createRequest('DELETE', $url, $options)); + } + + public function put($url = null, array $options = []) + { + return $this->send($this->createRequest('PUT', $url, $options)); + } + + public function patch($url = null, array $options = []) + { + return $this->send($this->createRequest('PATCH', $url, $options)); + } + + public function post($url = null, array $options = []) + { + return $this->send($this->createRequest('POST', $url, $options)); + } + + public function options($url = null, array $options = []) + { + return $this->send($this->createRequest('OPTIONS', $url, $options)); + } + + public function send(RequestInterface $request) + { + $isFuture = $request->getConfig()->get('future'); + $trans = new Transaction($this, $request, $isFuture); + $fn = $this->fsm; + + try { + $fn($trans); + if ($isFuture) { + // Turn the normal response into a future if needed. + return $trans->response instanceof FutureInterface + ? $trans->response + : new FutureResponse(new FulfilledPromise($trans->response)); + } + // Resolve deep futures if this is not a future + // transaction. This accounts for things like retries + // that do not have an immediate side-effect. + while ($trans->response instanceof FutureInterface) { + $trans->response = $trans->response->wait(); + } + return $trans->response; + } catch (\Exception $e) { + if ($isFuture) { + // Wrap the exception in a promise + return new FutureResponse(new RejectedPromise($e)); + } + throw RequestException::wrapException($trans->request, $e); + } + } + + /** + * Get an array of default options to apply to the client + * + * @return array + */ + protected function getDefaultOptions() + { + $settings = [ + 'allow_redirects' => true, + 'exceptions' => true, + 'decode_content' => true, + 'verify' => true + ]; + + // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set + if ($proxy = getenv('HTTP_PROXY')) { + $settings['proxy']['http'] = $proxy; + } + + if ($proxy = getenv('HTTPS_PROXY')) { + $settings['proxy']['https'] = $proxy; + } + + return $settings; + } + + /** + * Expand a URI template and inherit from the base URL if it's relative + * + * @param string|array $url URL or an array of the URI template to expand + * followed by a hash of template varnames. + * @return string + * @throws \InvalidArgumentException + */ + private function buildUrl($url) + { + // URI template (absolute or relative) + if (!is_array($url)) { + return strpos($url, '://') + ? (string) $url + : (string) $this->baseUrl->combine($url); + } + + if (!isset($url[1])) { + throw new \InvalidArgumentException('You must provide a hash of ' + . 'varname options in the second element of a URL array.'); + } + + // Absolute URL + if (strpos($url[0], '://')) { + return Utils::uriTemplate($url[0], $url[1]); + } + + // Combine the relative URL with the base URL + return (string) $this->baseUrl->combine( + Utils::uriTemplate($url[0], $url[1]) + ); + } + + private function configureBaseUrl(&$config) + { + if (!isset($config['base_url'])) { + $this->baseUrl = new Url('', ''); + } elseif (!is_array($config['base_url'])) { + $this->baseUrl = Url::fromString($config['base_url']); + } elseif (count($config['base_url']) < 2) { + throw new \InvalidArgumentException('You must provide a hash of ' + . 'varname options in the second element of a base_url array.'); + } else { + $this->baseUrl = Url::fromString( + Utils::uriTemplate( + $config['base_url'][0], + $config['base_url'][1] + ) + ); + $config['base_url'] = (string) $this->baseUrl; + } + } + + private function configureDefaults($config) + { + if (!isset($config['defaults'])) { + $this->defaults = $this->getDefaultOptions(); + } else { + $this->defaults = array_replace( + $this->getDefaultOptions(), + $config['defaults'] + ); + } + + // Add the default user-agent header + if (!isset($this->defaults['headers'])) { + $this->defaults['headers'] = [ + 'User-Agent' => Utils::getDefaultUserAgent() + ]; + } elseif (!Core::hasHeader($this->defaults, 'User-Agent')) { + // Add the User-Agent header if one was not already set + $this->defaults['headers']['User-Agent'] = Utils::getDefaultUserAgent(); + } + } + + /** + * Merges default options into the array passed by reference. + * + * @param array $options Options to modify by reference + * + * @return array + */ + private function mergeDefaults($options) + { + $defaults = $this->defaults; + + // Case-insensitively merge in default headers if both defaults and + // options have headers specified. + if (!empty($defaults['headers']) && !empty($options['headers'])) { + // Create a set of lowercased keys that are present. + $lkeys = []; + foreach (array_keys($options['headers']) as $k) { + $lkeys[strtolower($k)] = true; + } + // Merge in lowercase default keys when not present in above set. + foreach ($defaults['headers'] as $key => $value) { + if (!isset($lkeys[strtolower($key)])) { + $options['headers'][$key] = $value; + } + } + // No longer need to merge in headers. + unset($defaults['headers']); + } + + $result = array_replace_recursive($defaults, $options); + foreach ($options as $k => $v) { + if ($v === null) { + unset($result[$k]); + } + } + + return $result; + } + + /** + * @deprecated Use {@see GuzzleHttp\Pool} instead. + * @see GuzzleHttp\Pool + */ + public function sendAll($requests, array $options = []) + { + Pool::send($this, $requests, $options); + } + + /** + * @deprecated Use GuzzleHttp\Utils::getDefaultHandler + */ + public static function getDefaultHandler() + { + return Utils::getDefaultHandler(); + } + + /** + * @deprecated Use GuzzleHttp\Utils::getDefaultUserAgent + */ + public static function getDefaultUserAgent() + { + return Utils::getDefaultUserAgent(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ClientInterface.php new file mode 100755 index 000000000..63f0174d6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -0,0 +1,150 @@ +data = $data; + } + + /** + * Create a new collection from an array, validate the keys, and add default + * values where missing + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return self + * @throws \InvalidArgumentException if a parameter is missing + */ + public static function fromConfig( + array $config = [], + array $defaults = [], + array $required = [] + ) { + $data = $config + $defaults; + + if ($missing = array_diff($required, array_keys($data))) { + throw new \InvalidArgumentException( + 'Config is missing the following keys: ' . + implode(', ', $missing)); + } + + return new self($data); + } + + /** + * Removes all key value pairs + */ + public function clear() + { + $this->data = []; + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * + * @return mixed|null Value of the key or NULL + */ + public function get($key) + { + return isset($this->data[$key]) ? $this->data[$key] : null; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + */ + public function set($key, $value) + { + $this->data[$key] = $value; + } + + /** + * Add a value to a key. If a key of the same name has already been added, + * the key value will be converted into an array and the new value will be + * pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } elseif (is_array($this->data[$key])) { + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + */ + public function remove($key) + { + unset($this->data[$key]); + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value + * was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data, true); + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + */ + public function replace(array $data) + { + $this->data = $data; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * @param Collection|array $data Associative array of key value pair data + */ + public function merge($data) + { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + } + + /** + * Overwrite key value pairs in this collection with all of the data from + * an array or collection. + * + * @param array|\Traversable $data Values to override over this config + */ + public function overwriteWith($data) + { + if (is_array($data)) { + $this->data = $data + $this->data; + } elseif ($data instanceof Collection) { + $this->data = $data->toArray() + $this->data; + } else { + foreach ($data as $key => $value) { + $this->data[$key] = $value; + } + } + } + + /** + * Returns a Collection containing all the elements of the collection after + * applying the callback function to each one. + * + * The callable should accept three arguments: + * - (string) $key + * - (string) $value + * - (array) $context + * + * The callable must return a the altered or unaltered value. + * + * @param callable $closure Map function to apply + * @param array $context Context to pass to the callable + * + * @return Collection + */ + public function map(callable $closure, array $context = []) + { + $collection = new static(); + foreach ($this as $key => $value) { + $collection[$key] = $closure($key, $value, $context); + } + + return $collection; + } + + /** + * Iterates over each key value pair in the collection passing them to the + * callable. If the callable returns true, the current value from input is + * returned into the result Collection. + * + * The callable must accept two arguments: + * - (string) $key + * - (string) $value + * + * @param callable $closure Evaluation function + * + * @return Collection + */ + public function filter(callable $closure) + { + $collection = new static(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection[$key] = $value; + } + } + + return $collection; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php new file mode 100755 index 000000000..f8ac7dd35 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -0,0 +1,248 @@ +strictMode = $strictMode; + + foreach ($cookieArray as $cookie) { + if (!($cookie instanceof SetCookie)) { + $cookie = new SetCookie($cookie); + } + $this->setCookie($cookie); + } + } + + /** + * Create a new Cookie jar from an associative array and domain. + * + * @param array $cookies Cookies to create the jar from + * @param string $domain Domain to set the cookies to + * + * @return self + */ + public static function fromArray(array $cookies, $domain) + { + $cookieJar = new self(); + foreach ($cookies as $name => $value) { + $cookieJar->setCookie(new SetCookie([ + 'Domain' => $domain, + 'Name' => $name, + 'Value' => $value, + 'Discard' => true + ])); + } + + return $cookieJar; + } + + /** + * Quote the cookie value if it is not already quoted and it contains + * problematic characters. + * + * @param string $value Value that may or may not need to be quoted + * + * @return string + */ + public static function getCookieValue($value) + { + if (substr($value, 0, 1) !== '"' && + substr($value, -1, 1) !== '"' && + strpbrk($value, ';,') + ) { + $value = '"' . $value . '"'; + } + + return $value; + } + + public function toArray() + { + return array_map(function (SetCookie $cookie) { + return $cookie->toArray(); + }, $this->getIterator()->getArrayCopy()); + } + + public function clear($domain = null, $path = null, $name = null) + { + if (!$domain) { + $this->cookies = []; + return; + } elseif (!$path) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !$cookie->matchesDomain($domain); + } + ); + } elseif (!$name) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !($cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } else { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain, $name) { + return !($cookie->getName() == $name && + $cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } + } + + public function clearSessionCookies() + { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + } + ); + } + + public function setCookie(SetCookie $cookie) + { + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new \RuntimeException('Invalid cookie: ' . $result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, and domain are + // identical. + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is + // not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then + // replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + public function count() + { + return count($this->cookies); + } + + public function getIterator() + { + return new \ArrayIterator(array_values($this->cookies)); + } + + public function extractCookies( + RequestInterface $request, + ResponseInterface $response + ) { + if ($cookieHeader = $response->getHeaderAsArray('Set-Cookie')) { + foreach ($cookieHeader as $cookie) { + $sc = SetCookie::fromString($cookie); + if (!$sc->getDomain()) { + $sc->setDomain($request->getHost()); + } + $this->setCookie($sc); + } + } + } + + public function addCookieHeader(RequestInterface $request) + { + $values = []; + $scheme = $request->getScheme(); + $host = $request->getHost(); + $path = $request->getPath(); + + foreach ($this->cookies as $cookie) { + if ($cookie->matchesPath($path) && + $cookie->matchesDomain($host) && + !$cookie->isExpired() && + (!$cookie->getSecure() || $scheme == 'https') + ) { + $values[] = $cookie->getName() . '=' + . self::getCookieValue($cookie->getValue()); + } + } + + if ($values) { + $request->setHeader('Cookie', implode('; ', $values)); + } + } + + /** + * If a cookie already exists and the server asks to set it again with a + * null value, the cookie must be deleted. + * + * @param SetCookie $cookie + */ + private function removeCookieIfEmpty(SetCookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->clear( + $cookie->getDomain(), + $cookie->getPath(), + $cookie->getName() + ); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php new file mode 100755 index 000000000..4ea8567e8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -0,0 +1,75 @@ +filename = $cookieFile; + + if (file_exists($cookieFile)) { + $this->load($cookieFile); + } + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->save($this->filename); + } + + /** + * Saves the cookies to a file. + * + * @param string $filename File to save + * @throws \RuntimeException if the file cannot be found or created + */ + public function save($filename) + { + $json = []; + foreach ($this as $cookie) { + if ($cookie->getExpires() && !$cookie->getDiscard()) { + $json[] = $cookie->toArray(); + } + } + + if (false === file_put_contents($filename, json_encode($json))) { + // @codeCoverageIgnoreStart + throw new \RuntimeException("Unable to save file {$filename}"); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load cookies from a JSON formatted file. + * + * Old cookies are kept unless overwritten by newly loaded ones. + * + * @param string $filename Cookie file to load. + * @throws \RuntimeException if the file cannot be loaded. + */ + public function load($filename) + { + $json = file_get_contents($filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new \RuntimeException("Unable to load file {$filename}"); + // @codeCoverageIgnoreEnd + } + + $data = Utils::jsonDecode($json, true); + if (is_array($data)) { + foreach (Utils::jsonDecode($json, true) as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie file: {$filename}"); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php new file mode 100755 index 000000000..71a02d56d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -0,0 +1,66 @@ +sessionKey = $sessionKey; + $this->load(); + } + + /** + * Saves cookies to session when shutting down + */ + public function __destruct() + { + $this->save(); + } + + /** + * Save cookies to the client session + */ + public function save() + { + $json = []; + foreach ($this as $cookie) { + if ($cookie->getExpires() && !$cookie->getDiscard()) { + $json[] = $cookie->toArray(); + } + } + + $_SESSION[$this->sessionKey] = json_encode($json); + } + + /** + * Load the contents of the client session into the data array + */ + protected function load() + { + $cookieJar = isset($_SESSION[$this->sessionKey]) + ? $_SESSION[$this->sessionKey] + : null; + + $data = Utils::jsonDecode($cookieJar, true); + if (is_array($data)) { + foreach ($data as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie data"); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php new file mode 100755 index 000000000..ac9a89081 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -0,0 +1,373 @@ + null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false + ]; + + /** @var array Cookie data */ + private $data; + + /** + * Create a new SetCookie object from a string + * + * @param string $cookie Set-Cookie header string + * + * @return self + */ + public static function fromString($cookie) + { + // Create the default return array + $data = self::$defaults; + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return new self($data); + } + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + $value = isset($cookieParts[1]) + ? trim($cookieParts[1], " \n\r\t\0\x0B\"") + : true; + + // Only check for non-cookies when cookies have been found + if (empty($data['Name'])) { + $data['Name'] = $key; + $data['Value'] = $value; + } else { + foreach (array_keys(self::$defaults) as $search) { + if (!strcasecmp($search, $key)) { + $data[$search] = $value; + continue 2; + } + } + $data[$key] = $value; + } + } + + return new self($data); + } + + /** + * @param array $data Array of cookie data provided by a Cookie parser + */ + public function __construct(array $data = []) + { + $this->data = array_replace(self::$defaults, $data); + // Extract the Expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the Expires date + $this->setExpires(time() + $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires($this->getExpires()); + } + } + + public function __toString() + { + $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; + foreach ($this->data as $k => $v) { + if ($k != 'Name' && $k != 'Value' && $v !== null && $v !== false) { + if ($k == 'Expires') { + $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; + } else { + $str .= ($v === true ? $k : "{$k}={$v}") . '; '; + } + } + } + + return rtrim($str, '; '); + } + + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['Name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + */ + public function setName($name) + { + $this->data['Name'] = $name; + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['Value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + */ + public function setValue($value) + { + $this->data['Value'] = $value; + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['Domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + */ + public function setDomain($domain) + { + $this->data['Domain'] = $domain; + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['Path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + */ + public function setPath($path) + { + $this->data['Path'] = $path; + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['Max-Age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + */ + public function setMaxAge($maxAge) + { + $this->data['Max-Age'] = $maxAge; + } + + /** + * The UNIX timestamp when the cookie Expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['Expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + */ + public function setExpires($timestamp) + { + $this->data['Expires'] = is_numeric($timestamp) + ? (int) $timestamp + : strtotime($timestamp); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['Secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + */ + public function setSecure($secure) + { + $this->data['Secure'] = $secure; + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['Discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + */ + public function setDiscard($discard) + { + $this->data['Discard'] = $discard; + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['HttpOnly']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + */ + public function setHttpOnly($httpOnly) + { + $this->data['HttpOnly'] = $httpOnly; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + return !$this->getPath() || 0 === stripos($path, $this->getPath()); + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + return "Cookie name must not cannot invalid characters: =,; \\t\\r\\n\\013\\014"; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name + // in a private network. + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php new file mode 100755 index 000000000..0d2f4dbf0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php @@ -0,0 +1,20 @@ +propagationStopped; + } + + public function stopPropagation() + { + $this->propagationStopped = true; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php new file mode 100755 index 000000000..8a6ee47c8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php @@ -0,0 +1,61 @@ +transaction = $transaction; + } + + /** + * Get the HTTP client associated with the event. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->transaction->client; + } + + /** + * Get the request object + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->transaction->request; + } + + /** + * Get the number of transaction retries. + * + * @return int + */ + public function getRetryCount() + { + return $this->transaction->retries; + } + + /** + * @return Transaction + */ + public function getTransaction() + { + return $this->transaction; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php new file mode 100755 index 000000000..bbbdfaf83 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php @@ -0,0 +1,40 @@ +transaction->state = 'retry'; + + if ($afterDelay) { + $this->transaction->request->getConfig()->set('delay', $afterDelay); + } + + $this->stopPropagation(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php new file mode 100755 index 000000000..3b106df00 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php @@ -0,0 +1,63 @@ +transaction->transferInfo; + } + + return isset($this->transaction->transferInfo[$name]) + ? $this->transaction->transferInfo[$name] + : null; + } + + /** + * Returns true/false if a response is available. + * + * @return bool + */ + public function hasResponse() + { + return !($this->transaction->response instanceof FutureInterface); + } + + /** + * Get the response. + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->hasResponse() ? $this->transaction->response : null; + } + + /** + * Intercept the request and associate a response + * + * @param ResponseInterface $response Response to set + */ + public function intercept(ResponseInterface $response) + { + $this->transaction->response = $response; + $this->transaction->exception = null; + $this->stopPropagation(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php new file mode 100755 index 000000000..f313c3756 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php @@ -0,0 +1,26 @@ +transaction->response = $response; + $this->transaction->exception = null; + $this->stopPropagation(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php new file mode 100755 index 000000000..56cc557e3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher + */ +class Emitter implements EmitterInterface +{ + /** @var array */ + private $listeners = []; + + /** @var array */ + private $sorted = []; + + public function on($eventName, callable $listener, $priority = 0) + { + if ($priority === 'first') { + $priority = isset($this->listeners[$eventName]) + ? max(array_keys($this->listeners[$eventName])) + 1 + : 1; + } elseif ($priority === 'last') { + $priority = isset($this->listeners[$eventName]) + ? min(array_keys($this->listeners[$eventName])) - 1 + : -1; + } + + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + public function once($eventName, callable $listener, $priority = 0) + { + $onceListener = function ( + EventInterface $event, + $eventName + ) use (&$onceListener, $eventName, $listener, $priority) { + $this->removeListener($eventName, $onceListener); + $listener($event, $eventName); + }; + + $this->on($eventName, $onceListener, $priority); + } + + public function removeListener($eventName, callable $listener) + { + if (empty($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset( + $this->listeners[$eventName][$priority][$key], + $this->sorted[$eventName] + ); + } + } + } + + public function listeners($eventName = null) + { + // Return all events in a sorted priority order + if ($eventName === null) { + foreach (array_keys($this->listeners) as $eventName) { + if (empty($this->sorted[$eventName])) { + $this->listeners($eventName); + } + } + return $this->sorted; + } + + // Return the listeners for a specific event, sorted in priority order + if (empty($this->sorted[$eventName])) { + $this->sorted[$eventName] = []; + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName], SORT_NUMERIC); + foreach ($this->listeners[$eventName] as $listeners) { + foreach ($listeners as $listener) { + $this->sorted[$eventName][] = $listener; + } + } + } + } + + return $this->sorted[$eventName]; + } + + public function hasListeners($eventName) + { + return !empty($this->listeners[$eventName]); + } + + public function emit($eventName, EventInterface $event) + { + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners($eventName) as $listener) { + $listener($event, $eventName); + if ($event->isPropagationStopped()) { + break; + } + } + } + + return $event; + } + + public function attach(SubscriberInterface $subscriber) + { + foreach ($subscriber->getEvents() as $eventName => $listeners) { + if (is_array($listeners[0])) { + foreach ($listeners as $listener) { + $this->on( + $eventName, + [$subscriber, $listener[0]], + isset($listener[1]) ? $listener[1] : 0 + ); + } + } else { + $this->on( + $eventName, + [$subscriber, $listeners[0]], + isset($listeners[1]) ? $listeners[1] : 0 + ); + } + } + } + + public function detach(SubscriberInterface $subscriber) + { + foreach ($subscriber->getEvents() as $eventName => $listener) { + $this->removeListener($eventName, [$subscriber, $listener[0]]); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php new file mode 100755 index 000000000..9783efd15 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php @@ -0,0 +1,96 @@ +transaction->exception; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php new file mode 100755 index 000000000..7432134d0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php @@ -0,0 +1,27 @@ +transaction->exception; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php new file mode 100755 index 000000000..97247e84c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php @@ -0,0 +1,23 @@ +emitter) { + $this->emitter = new Emitter(); + } + + return $this->emitter; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php new file mode 100755 index 000000000..407dc92dd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php @@ -0,0 +1,88 @@ +getEmitter(); + foreach ($listeners as $el) { + if ($el['once']) { + $emitter->once($el['name'], $el['fn'], $el['priority']); + } else { + $emitter->on($el['name'], $el['fn'], $el['priority']); + } + } + } + + /** + * Extracts the allowed events from the provided array, and ignores anything + * else in the array. The event listener must be specified as a callable or + * as an array of event listener data ("name", "fn", "priority", "once"). + * + * @param array $source Array containing callables or hashes of data to be + * prepared as event listeners. + * @param array $events Names of events to look for in the provided $source + * array. Other keys are ignored. + * @return array + */ + private function prepareListeners(array $source, array $events) + { + $listeners = []; + foreach ($events as $name) { + if (isset($source[$name])) { + $this->buildListener($name, $source[$name], $listeners); + } + } + + return $listeners; + } + + /** + * Creates a complete event listener definition from the provided array of + * listener data. Also works recursively if more than one listeners are + * contained in the provided array. + * + * @param string $name Name of the event the listener is for. + * @param array|callable $data Event listener data to prepare. + * @param array $listeners Array of listeners, passed by reference. + * + * @throws \InvalidArgumentException if the event data is malformed. + */ + private function buildListener($name, $data, &$listeners) + { + static $defaults = ['priority' => 0, 'once' => false]; + + // If a callable is provided, normalize it to the array format. + if (is_callable($data)) { + $data = ['fn' => $data]; + } + + // Prepare the listener and add it to the array, recursively. + if (isset($data['fn'])) { + $data['name'] = $name; + $listeners[] = $data + $defaults; + } elseif (is_array($data)) { + foreach ($data as $listenerData) { + $this->buildListener($name, $listenerData, $listeners); + } + } else { + throw new \InvalidArgumentException('Each event listener must be a ' + . 'callable or an associative array containing a "fn" key.'); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php new file mode 100755 index 000000000..3fd0de4ac --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php @@ -0,0 +1,51 @@ +downloadSize = $downloadSize; + $this->downloaded = $downloaded; + $this->uploadSize = $uploadSize; + $this->uploaded = $uploaded; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php new file mode 100755 index 000000000..f51d42065 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php @@ -0,0 +1,56 @@ + ['methodName']] + * - ['eventName' => ['methodName', $priority]] + * - ['eventName' => [['methodName'], ['otherMethod']] + * - ['eventName' => [['methodName'], ['otherMethod', $priority]] + * - ['eventName' => [['methodName', $priority], ['otherMethod', $priority]] + * + * @return array + */ + public function getEvents(); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php new file mode 100755 index 000000000..fd78431ea --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php @@ -0,0 +1,7 @@ +response = $response; + } + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php new file mode 100755 index 000000000..3f052d36d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php @@ -0,0 +1,121 @@ +getStatusCode() + : 0; + parent::__construct($message, $code, $previous); + $this->request = $request; + $this->response = $response; + } + + /** + * Wrap non-RequestExceptions with a RequestException + * + * @param RequestInterface $request + * @param \Exception $e + * + * @return RequestException + */ + public static function wrapException(RequestInterface $request, \Exception $e) + { + if ($e instanceof RequestException) { + return $e; + } elseif ($e instanceof ConnectException) { + return new HttpConnectException($e->getMessage(), $request, null, $e); + } else { + return new RequestException($e->getMessage(), $request, null, $e); + } + } + + /** + * Factory method to create a new exception with a normalized error message + * + * @param RequestInterface $request Request + * @param ResponseInterface $response Response received + * @param \Exception $previous Previous exception + * + * @return self + */ + public static function create( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null + ) { + if (!$response) { + return new self('Error completing request', $request, null, $previous); + } + + $level = floor($response->getStatusCode() / 100); + if ($level == '4') { + $label = 'Client error response'; + $className = __NAMESPACE__ . '\\ClientException'; + } elseif ($level == '5') { + $label = 'Server error response'; + $className = __NAMESPACE__ . '\\ServerException'; + } else { + $label = 'Unsuccessful response'; + $className = __CLASS__; + } + + $message = $label . ' [url] ' . $request->getUrl() + . ' [status code] ' . $response->getStatusCode() + . ' [reason phrase] ' . $response->getReasonPhrase(); + + return new $className($message, $request, $response, $previous); + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Check if a response was received + * + * @return bool + */ + public function hasResponse() + { + return $this->response !== null; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php new file mode 100755 index 000000000..7cdd34086 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php @@ -0,0 +1,7 @@ +error = $error; + } + + /** + * Get the associated error + * + * @return \LibXMLError|null + */ + public function getError() + { + return $this->error; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/HasDataTrait.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/HasDataTrait.php new file mode 100755 index 000000000..020dfc9ab --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/HasDataTrait.php @@ -0,0 +1,75 @@ +data); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + public function toArray() + { + return $this->data; + } + + public function count() + { + return count($this->data); + } + + /** + * Get a value from the collection using a path syntax to retrieve nested + * data. + * + * @param string $path Path to traverse and retrieve a value from + * + * @return mixed|null + */ + public function getPath($path) + { + return Utils::getPath($this->data, $path); + } + + /** + * Set a value into a nested array key. Keys will be created as needed to + * set the value. + * + * @param string $path Path to set + * @param mixed $value Value to set at the key + * + * @throws \RuntimeException when trying to setPath using a nested path + * that travels through a scalar value + */ + public function setPath($path, $value) + { + Utils::setPath($this->data, $path, $value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php new file mode 100755 index 000000000..0c675758d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php @@ -0,0 +1,253 @@ +getBody(); + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function getBody() + { + return $this->body; + } + + public function setBody(StreamInterface $body = null) + { + if ($body === null) { + // Setting a null body will remove the body of the request + $this->removeHeader('Content-Length'); + $this->removeHeader('Transfer-Encoding'); + } + + $this->body = $body; + } + + public function addHeader($header, $value) + { + if (is_array($value)) { + $current = array_merge($this->getHeaderAsArray($header), $value); + } else { + $current = $this->getHeaderAsArray($header); + $current[] = (string) $value; + } + + $this->setHeader($header, $current); + } + + public function addHeaders(array $headers) + { + foreach ($headers as $name => $header) { + $this->addHeader($name, $header); + } + } + + public function getHeader($header) + { + $name = strtolower($header); + return isset($this->headers[$name]) + ? implode(', ', $this->headers[$name]) + : ''; + } + + public function getHeaderAsArray($header) + { + $name = strtolower($header); + return isset($this->headers[$name]) ? $this->headers[$name] : []; + } + + public function getHeaders() + { + $headers = []; + foreach ($this->headers as $name => $values) { + $headers[$this->headerNames[$name]] = $values; + } + + return $headers; + } + + public function setHeader($header, $value) + { + $header = trim($header); + $name = strtolower($header); + $this->headerNames[$name] = $header; + + if (is_array($value)) { + foreach ($value as &$v) { + $v = trim($v); + } + $this->headers[$name] = $value; + } else { + $this->headers[$name] = [trim($value)]; + } + } + + public function setHeaders(array $headers) + { + $this->headers = $this->headerNames = []; + foreach ($headers as $key => $value) { + $this->setHeader($key, $value); + } + } + + public function hasHeader($header) + { + return isset($this->headers[strtolower($header)]); + } + + public function removeHeader($header) + { + $name = strtolower($header); + unset($this->headers[$name], $this->headerNames[$name]); + } + + /** + * Parse an array of header values containing ";" separated data into an + * array of associative arrays representing the header key value pair + * data of the header. When a parameter does not contain a value, but just + * contains a key, this function will inject a key with a '' string value. + * + * @param MessageInterface $message That contains the header + * @param string $header Header to retrieve from the message + * + * @return array Returns the parsed header values. + */ + public static function parseHeader(MessageInterface $message, $header) + { + static $trimmed = "\"' \n\t\r"; + $params = $matches = []; + + foreach (self::normalizeHeader($message, $header) as $val) { + $part = []; + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param MessageInterface $message That contains the header + * @param string $header Header to retrieve from the message + * + * @return array Returns the normalized header field values. + */ + public static function normalizeHeader(MessageInterface $message, $header) + { + $h = $message->getHeaderAsArray($header); + for ($i = 0, $total = count($h); $i < $total; $i++) { + if (strpos($h[$i], ',') === false) { + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $h[$i]) as $v) { + $h[] = trim($v); + } + unset($h[$i]); + } + + return $h; + } + + /** + * Gets the start-line and headers of a message as a string + * + * @param MessageInterface $message + * + * @return string + */ + public static function getStartLineAndHeaders(MessageInterface $message) + { + return static::getStartLine($message) + . self::getHeadersAsString($message); + } + + /** + * Gets the headers of a message as a string + * + * @param MessageInterface $message + * + * @return string + */ + public static function getHeadersAsString(MessageInterface $message) + { + $result = ''; + foreach ($message->getHeaders() as $name => $values) { + $result .= "\r\n{$name}: " . implode(', ', $values); + } + + return $result; + } + + /** + * Gets the start line of a message + * + * @param MessageInterface $message + * + * @return string + * @throws \InvalidArgumentException + */ + public static function getStartLine(MessageInterface $message) + { + if ($message instanceof RequestInterface) { + return trim($message->getMethod() . ' ' + . $message->getResource()) + . ' HTTP/' . $message->getProtocolVersion(); + } elseif ($message instanceof ResponseInterface) { + return 'HTTP/' . $message->getProtocolVersion() . ' ' + . $message->getStatusCode() . ' ' + . $message->getReasonPhrase(); + } else { + throw new \InvalidArgumentException('Unknown message type'); + } + } + + /** + * Accepts and modifies the options provided to the message in the + * constructor. + * + * Can be overridden in subclasses as necessary. + * + * @param array $options Options array passed by reference. + */ + protected function handleOptions(array &$options) + { + if (isset($options['protocol_version'])) { + $this->protocolVersion = $options['protocol_version']; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php new file mode 100755 index 000000000..ca42f20f3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php @@ -0,0 +1,24 @@ +then($onFulfilled, $onRejected, $onProgress), + [$future, 'wait'], + [$future, 'cancel'] + ); + } + + public function getStatusCode() + { + return $this->_value->getStatusCode(); + } + + public function setStatusCode($code) + { + $this->_value->setStatusCode($code); + } + + public function getReasonPhrase() + { + return $this->_value->getReasonPhrase(); + } + + public function setReasonPhrase($phrase) + { + $this->_value->setReasonPhrase($phrase); + } + + public function getEffectiveUrl() + { + return $this->_value->getEffectiveUrl(); + } + + public function setEffectiveUrl($url) + { + $this->_value->setEffectiveUrl($url); + } + + public function json(array $config = []) + { + return $this->_value->json($config); + } + + public function xml(array $config = []) + { + return $this->_value->xml($config); + } + + public function __toString() + { + try { + return $this->_value->__toString(); + } catch (\Exception $e) { + trigger_error($e->getMessage(), E_USER_WARNING); + return ''; + } + } + + public function getProtocolVersion() + { + return $this->_value->getProtocolVersion(); + } + + public function setBody(StreamInterface $body = null) + { + $this->_value->setBody($body); + } + + public function getBody() + { + return $this->_value->getBody(); + } + + public function getHeaders() + { + return $this->_value->getHeaders(); + } + + public function getHeader($header) + { + return $this->_value->getHeader($header); + } + + public function getHeaderAsArray($header) + { + return $this->_value->getHeaderAsArray($header); + } + + public function hasHeader($header) + { + return $this->_value->hasHeader($header); + } + + public function removeHeader($header) + { + $this->_value->removeHeader($header); + } + + public function addHeader($header, $value) + { + $this->_value->addHeader($header, $value); + } + + public function addHeaders(array $headers) + { + $this->_value->addHeaders($headers); + } + + public function setHeader($header, $value) + { + $this->_value->setHeader($header, $value); + } + + public function setHeaders(array $headers) + { + $this->_value->setHeaders($headers); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php new file mode 100755 index 000000000..85984e2dd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php @@ -0,0 +1,364 @@ + 1, 'timeout' => 1, 'verify' => 1, 'ssl_key' => 1, + 'cert' => 1, 'proxy' => 1, 'debug' => 1, 'save_to' => 1, 'stream' => 1, + 'expect' => 1, 'future' => 1 + ]; + + /** @var array Default allow_redirects request option settings */ + private static $defaultRedirect = [ + 'max' => 5, + 'strict' => false, + 'referer' => false, + 'protocols' => ['http', 'https'] + ]; + + /** + * @param array $customOptions Associative array of custom request option + * names mapping to functions used to apply + * the option. The function accepts the request + * and the option value to apply. + */ + public function __construct(array $customOptions = []) + { + $this->errorPlugin = new HttpError(); + $this->redirectPlugin = new Redirect(); + $this->customOptions = $customOptions; + } + + public function createResponse( + $statusCode, + array $headers = [], + $body = null, + array $options = [] + ) { + if (null !== $body) { + $body = Stream::factory($body); + } + + return new Response($statusCode, $headers, $body, $options); + } + + public function createRequest($method, $url, array $options = []) + { + // Handle the request protocol version option that needs to be + // specified in the request constructor. + if (isset($options['version'])) { + $options['config']['protocol_version'] = $options['version']; + unset($options['version']); + } + + $request = new Request($method, $url, [], null, + isset($options['config']) ? $options['config'] : []); + + unset($options['config']); + + // Use a POST body by default + if ($method == 'POST' + && !isset($options['body']) + && !isset($options['json']) + ) { + $options['body'] = []; + } + + if ($options) { + $this->applyOptions($request, $options); + } + + return $request; + } + + /** + * Create a request or response object from an HTTP message string + * + * @param string $message Message to parse + * + * @return RequestInterface|ResponseInterface + * @throws \InvalidArgumentException if unable to parse a message + */ + public function fromMessage($message) + { + static $parser; + if (!$parser) { + $parser = new MessageParser(); + } + + // Parse a response + if (strtoupper(substr($message, 0, 4)) == 'HTTP') { + $data = $parser->parseResponse($message); + return $this->createResponse( + $data['code'], + $data['headers'], + $data['body'] === '' ? null : $data['body'], + $data + ); + } + + // Parse a request + if (!($data = ($parser->parseRequest($message)))) { + throw new \InvalidArgumentException('Unable to parse request'); + } + + return $this->createRequest( + $data['method'], + Url::buildUrl($data['request_url']), + [ + 'headers' => $data['headers'], + 'body' => $data['body'] === '' ? null : $data['body'], + 'config' => [ + 'protocol_version' => $data['protocol_version'] + ] + ] + ); + } + + /** + * Apply POST fields and files to a request to attempt to give an accurate + * representation. + * + * @param RequestInterface $request Request to update + * @param array $body Body to apply + */ + protected function addPostData(RequestInterface $request, array $body) + { + static $fields = ['string' => true, 'array' => true, 'NULL' => true, + 'boolean' => true, 'double' => true, 'integer' => true]; + + $post = new PostBody(); + foreach ($body as $key => $value) { + if (isset($fields[gettype($value)])) { + $post->setField($key, $value); + } elseif ($value instanceof PostFileInterface) { + $post->addFile($value); + } else { + $post->addFile(new PostFile($key, $value)); + } + } + + if ($request->getHeader('Content-Type') == 'multipart/form-data') { + $post->forceMultipartUpload(true); + } + + $request->setBody($post); + } + + protected function applyOptions( + RequestInterface $request, + array $options = [] + ) { + $config = $request->getConfig(); + $emitter = $request->getEmitter(); + + foreach ($options as $key => $value) { + + if (isset(self::$configMap[$key])) { + $config[$key] = $value; + continue; + } + + switch ($key) { + + case 'allow_redirects': + + if ($value === false) { + continue; + } + + if ($value === true) { + $value = self::$defaultRedirect; + } elseif (!is_array($value)) { + throw new Iae('allow_redirects must be true, false, or array'); + } else { + // Merge the default settings with the provided settings + $value += self::$defaultRedirect; + } + + $config['redirect'] = $value; + $emitter->attach($this->redirectPlugin); + break; + + case 'decode_content': + + if ($value === false) { + continue; + } + + $config['decode_content'] = true; + if ($value !== true) { + $request->setHeader('Accept-Encoding', $value); + } + break; + + case 'headers': + + if (!is_array($value)) { + throw new Iae('header value must be an array'); + } + foreach ($value as $k => $v) { + $request->setHeader($k, $v); + } + break; + + case 'exceptions': + + if ($value === true) { + $emitter->attach($this->errorPlugin); + } + break; + + case 'body': + + if (is_array($value)) { + $this->addPostData($request, $value); + } elseif ($value !== null) { + $request->setBody(Stream::factory($value)); + } + break; + + case 'auth': + + if (!$value) { + continue; + } + + if (is_array($value)) { + $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; + } else { + $type = strtolower($value); + } + + $config['auth'] = $value; + + if ($type == 'basic') { + $request->setHeader( + 'Authorization', + 'Basic ' . base64_encode("$value[0]:$value[1]") + ); + } elseif ($type == 'digest') { + // @todo: Do not rely on curl + $config->setPath('curl/' . CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + $config->setPath('curl/' . CURLOPT_USERPWD, "$value[0]:$value[1]"); + } + break; + + case 'query': + + if ($value instanceof Query) { + $original = $request->getQuery(); + // Do not overwrite existing query string variables by + // overwriting the object with the query string data passed + // in the URL + $value->overwriteWith($original->toArray()); + $request->setQuery($value); + } elseif (is_array($value)) { + // Do not overwrite existing query string variables + $query = $request->getQuery(); + foreach ($value as $k => $v) { + if (!isset($query[$k])) { + $query[$k] = $v; + } + } + } else { + throw new Iae('query must be an array or Query object'); + } + break; + + case 'cookies': + + if ($value === true) { + static $cookie = null; + if (!$cookie) { + $cookie = new Cookie(); + } + $emitter->attach($cookie); + } elseif (is_array($value)) { + $emitter->attach( + new Cookie(CookieJar::fromArray($value, $request->getHost())) + ); + } elseif ($value instanceof CookieJarInterface) { + $emitter->attach(new Cookie($value)); + } elseif ($value !== false) { + throw new Iae('cookies must be an array, true, or CookieJarInterface'); + } + break; + + case 'events': + + if (!is_array($value)) { + throw new Iae('events must be an array'); + } + + $this->attachListeners($request, + $this->prepareListeners( + $value, + ['before', 'complete', 'error', 'progress', 'end'] + ) + ); + break; + + case 'subscribers': + + if (!is_array($value)) { + throw new Iae('subscribers must be an array'); + } + + foreach ($value as $subscribers) { + $emitter->attach($subscribers); + } + break; + + case 'json': + + $request->setBody(Stream::factory(json_encode($value))); + if (!$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', 'application/json'); + } + break; + + default: + + // Check for custom handler functions. + if (isset($this->customOptions[$key])) { + $fn = $this->customOptions[$key]; + $fn($request, $value); + continue; + } + + throw new Iae("No method can handle the {$key} config key"); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php new file mode 100755 index 000000000..86ae9c7ee --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php @@ -0,0 +1,71 @@ +getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * @return array Returns an associative array of the message's headers. + */ + public function getHeaders(); + + /** + * Retrieve a header by the given case-insensitive name. + * + * @param string $header Case-insensitive header name. + * + * @return string + */ + public function getHeader($header); + + /** + * Retrieves a header by the given case-insensitive name as an array of strings. + * + * @param string $header Case-insensitive header name. + * + * @return string[] + */ + public function getHeaderAsArray($header); + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $header Case-insensitive header name. + * + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader($header); + + /** + * Remove a specific header by case-insensitive name. + * + * @param string $header Case-insensitive header name. + */ + public function removeHeader($header); + + /** + * Appends a header value to any existing values associated with the + * given header name. + * + * @param string $header Header name to add + * @param string $value Value of the header + */ + public function addHeader($header, $value); + + /** + * Merges in an associative array of headers. + * + * Each array key MUST be a string representing the case-insensitive name + * of a header. Each value MUST be either a string or an array of strings. + * For each value, the value is appended to any existing header of the same + * name, or, if a header does not already exist by the given name, then the + * header is added. + * + * @param array $headers Associative array of headers to add to the message + */ + public function addHeaders(array $headers); + + /** + * Sets a header, replacing any existing values of any headers with the + * same case-insensitive name. + * + * The header values MUST be a string or an array of strings. + * + * @param string $header Header name + * @param string|array $value Header value(s) + */ + public function setHeader($header, $value); + + /** + * Sets headers, replacing any headers that have already been set on the + * message. + * + * The array keys MUST be a string. The array values must be either a + * string or an array of strings. + * + * @param array $headers Headers to set. + */ + public function setHeaders(array $headers); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php new file mode 100755 index 000000000..c3cc195e3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php @@ -0,0 +1,171 @@ +parseMessage($message))) { + return false; + } + + // Parse the protocol and protocol version + if (isset($parts['start_line'][2])) { + $startParts = explode('/', $parts['start_line'][2]); + $protocol = strtoupper($startParts[0]); + $version = isset($startParts[1]) ? $startParts[1] : '1.1'; + } else { + $protocol = 'HTTP'; + $version = '1.1'; + } + + $parsed = [ + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => $protocol, + 'protocol_version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ]; + + $parsed['request_url'] = $this->getUrlPartsFromMessage( + (isset($parts['start_line'][1]) ? $parts['start_line'][1] : ''), $parsed); + + return $parsed; + } + + /** + * Parse an HTTP response message into an associative array of parts. + * + * @param string $message HTTP response to parse + * + * @return array|bool Returns false if the message is invalid + */ + public function parseResponse($message) + { + if (!($parts = $this->parseMessage($message))) { + return false; + } + + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return [ + 'protocol' => $protocol, + 'protocol_version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ]; + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array|bool + */ + private function parseMessage($message) + { + if (!$message) { + return false; + } + + $startLine = null; + $headers = []; + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + $startLine = explode(' ', $line, 3); + } elseif (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = [$headers[$key], $value]; + } else { + $headers[$key][] = $value; + } + } + } + + return [ + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ]; + } + + /** + * Create URL parts from HTTP message parts + * + * @param string $requestUrl Associated URL + * @param array $parts HTTP message parts + * + * @return array + */ + private function getUrlPartsFromMessage($requestUrl, array $parts) + { + // Parse the URL information from the message + $urlParts = ['path' => $requestUrl, 'scheme' => 'http']; + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = null; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/Request.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/Request.php new file mode 100755 index 000000000..38714af80 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/Request.php @@ -0,0 +1,195 @@ +setUrl($url); + $this->method = strtoupper($method); + $this->handleOptions($options); + $this->transferOptions = new Collection($options); + $this->addPrepareEvent(); + + if ($body !== null) { + $this->setBody($body); + } + + if ($headers) { + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + } + } + + public function __clone() + { + if ($this->emitter) { + $this->emitter = clone $this->emitter; + } + $this->transferOptions = clone $this->transferOptions; + $this->url = clone $this->url; + } + + public function setUrl($url) + { + $this->url = $url instanceof Url ? $url : Url::fromString($url); + $this->updateHostHeaderFromUrl(); + } + + public function getUrl() + { + return (string) $this->url; + } + + public function setQuery($query) + { + $this->url->setQuery($query); + } + + public function getQuery() + { + return $this->url->getQuery(); + } + + public function setMethod($method) + { + $this->method = strtoupper($method); + } + + public function getMethod() + { + return $this->method; + } + + public function getScheme() + { + return $this->url->getScheme(); + } + + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + } + + public function getPort() + { + return $this->url->getPort(); + } + + public function setPort($port) + { + $this->url->setPort($port); + $this->updateHostHeaderFromUrl(); + } + + public function getHost() + { + return $this->url->getHost(); + } + + public function setHost($host) + { + $this->url->setHost($host); + $this->updateHostHeaderFromUrl(); + } + + public function getPath() + { + return '/' . ltrim($this->url->getPath(), '/'); + } + + public function setPath($path) + { + $this->url->setPath($path); + } + + public function getResource() + { + $resource = $this->getPath(); + if ($query = (string) $this->url->getQuery()) { + $resource .= '?' . $query; + } + + return $resource; + } + + public function getConfig() + { + return $this->transferOptions; + } + + protected function handleOptions(array &$options) + { + parent::handleOptions($options); + // Use a custom emitter if one is specified, and remove it from + // options that are exposed through getConfig() + if (isset($options['emitter'])) { + $this->emitter = $options['emitter']; + unset($options['emitter']); + } + } + + /** + * Adds a subscriber that ensures a request's body is prepared before + * sending. + */ + private function addPrepareEvent() + { + static $subscriber; + if (!$subscriber) { + $subscriber = new Prepare(); + } + + $this->getEmitter()->attach($subscriber); + } + + private function updateHostHeaderFromUrl() + { + $port = $this->url->getPort(); + $scheme = $this->url->getScheme(); + if ($host = $this->url->getHost()) { + if (($port == 80 && $scheme == 'http') || + ($port == 443 && $scheme == 'https') + ) { + $this->setHeader('Host', $host); + } else { + $this->setHeader('Host', "{$host}:{$port}"); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php new file mode 100755 index 000000000..f6a69d1e1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php @@ -0,0 +1,136 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Reserved for WebDAV advanced collections expired proposal', + 426 => 'Upgrade required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ]; + + /** @var string The reason phrase of the response (human readable code) */ + private $reasonPhrase; + + /** @var string The status code of the response */ + private $statusCode; + + /** @var string The effective URL that returned this response */ + private $effectiveUrl; + + /** + * @param int|string $statusCode The response status code (e.g. 200) + * @param array $headers The response headers + * @param StreamInterface $body The body of the response + * @param array $options Response message options + * - reason_phrase: Set a custom reason phrase + * - protocol_version: Set a custom protocol version + */ + public function __construct( + $statusCode, + array $headers = [], + StreamInterface $body = null, + array $options = [] + ) { + $this->statusCode = (int) $statusCode; + $this->handleOptions($options); + + // Assume a reason phrase if one was not applied as an option + if (!$this->reasonPhrase && + isset(self::$statusTexts[$this->statusCode]) + ) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } + + if ($headers) { + $this->setHeaders($headers); + } + + if ($body) { + $this->setBody($body); + } + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function setStatusCode($code) + { + return $this->statusCode = (int) $code; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + public function setReasonPhrase($phrase) + { + return $this->reasonPhrase = $phrase; + } + + public function json(array $config = []) + { + try { + return Utils::jsonDecode( + (string) $this->getBody(), + isset($config['object']) ? !$config['object'] : true, + 512, + isset($config['big_int_strings']) ? JSON_BIGINT_AS_STRING : 0 + ); + } catch (\InvalidArgumentException $e) { + throw new ParseException( + $e->getMessage(), + $this + ); + } + } + + public function xml(array $config = []) + { + $disableEntities = libxml_disable_entity_loader(true); + $internalErrors = libxml_use_internal_errors(true); + + try { + // Allow XML to be retrieved even if there is no response body + $xml = new \SimpleXMLElement( + (string) $this->getBody() ?: '', + isset($config['libxml_options']) ? $config['libxml_options'] : LIBXML_NONET, + false, + isset($config['ns']) ? $config['ns'] : '', + isset($config['ns_is_prefix']) ? $config['ns_is_prefix'] : false + ); + libxml_disable_entity_loader($disableEntities); + libxml_use_internal_errors($internalErrors); + } catch (\Exception $e) { + libxml_disable_entity_loader($disableEntities); + libxml_use_internal_errors($internalErrors); + throw new XmlParseException( + 'Unable to parse response body into XML: ' . $e->getMessage(), + $this, + $e, + (libxml_get_last_error()) ?: null + ); + } + + return $xml; + } + + public function getEffectiveUrl() + { + return $this->effectiveUrl; + } + + public function setEffectiveUrl($url) + { + $this->effectiveUrl = $url; + } + + /** + * Accepts and modifies the options provided to the response in the + * constructor. + * + * @param array $options Options array passed by reference. + */ + protected function handleOptions(array &$options = []) + { + parent::handleOptions($options); + if (isset($options['reason_phrase'])) { + $this->reasonPhrase = $options['reason_phrase']; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php new file mode 100755 index 000000000..c0ae9be93 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php @@ -0,0 +1,111 @@ + 'text/vnd.in3d.3dml', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'application' => 'application/x-ms-application', + 'apr' => 'application/vnd.lotus-approach', + 'asa' => 'text/plain', + 'asax' => 'application/octet-stream', + 'asc' => 'application/pgp-signature', + 'ascx' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'ashx' => 'text/plain', + 'asm' => 'text/x-asm', + 'asmx' => 'text/plain', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asp' => 'text/plain', + 'aspx' => 'text/plain', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'axd' => 'text/plain', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfc' => 'application/x-coldfusion', + 'cfm' => 'application/x-coldfusion', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'cryptonote' => 'application/vnd.rig.cryptonote', + 'cs' => 'text/plain', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxt' => 'application/vnd.geonext', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hta' => 'application/octet-stream', + 'htc' => 'text/html', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/octet-stream', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/octet-stream', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/octet-stream', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/mp4', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsf' => 'application/vnd.lotus-notes', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'text/x-php', + 'phps' => 'application/x-httpd-phps', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rb' => 'text/plain', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'resx' => 'text/xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sig' => 'application/pgp-signature', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'src' => 'application/x-wais-source', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'image/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvx' => 'application/vnd.dece.unspecified', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-ms-wmz', + 'woff' => 'application/x-font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'yaml' => 'text/yaml', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'yml' => 'text/yaml', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml' + ); + + /** + * Get a singleton instance of the class + * + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get a mimetype value from a file extension + * + * @param string $extension File extension + * + * @return string|null + * + */ + public function fromExtension($extension) + { + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) + ? $this->mimetypes[$extension] + : null; + } + + /** + * Get a mimetype from a filename + * + * @param string $filename Filename to generate a mimetype from + * + * @return string|null + */ + public function fromFilename($filename) + { + return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Pool.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Pool.php new file mode 100755 index 000000000..7b9d83a4e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Pool.php @@ -0,0 +1,333 @@ +client = $client; + $this->iter = $this->coerceIterable($requests); + $this->deferred = new Deferred(); + $this->promise = $this->deferred->promise(); + $this->poolSize = isset($options['pool_size']) + ? $options['pool_size'] : 25; + $this->eventListeners = $this->prepareListeners( + $options, + ['before', 'complete', 'error', 'end'] + ); + } + + /** + * Sends multiple requests in parallel and returns an array of responses + * and exceptions that uses the same ordering as the provided requests. + * + * IMPORTANT: This method keeps every request and response in memory, and + * as such, is NOT recommended when sending a large number or an + * indeterminate number of requests concurrently. + * + * @param ClientInterface $client Client used to send the requests + * @param array|\Iterator $requests Requests to send in parallel + * @param array $options Passes through the options available in + * {@see GuzzleHttp\Pool::__construct} + * + * @return BatchResults Returns a container for the results. + * @throws \InvalidArgumentException if the event format is incorrect. + */ + public static function batch( + ClientInterface $client, + $requests, + array $options = [] + ) { + $hash = new \SplObjectStorage(); + foreach ($requests as $request) { + $hash->attach($request); + } + + // In addition to the normally run events when requests complete, add + // and event to continuously track the results of transfers in the hash. + (new self($client, $requests, RequestEvents::convertEventArray( + $options, + ['end'], + [ + 'priority' => RequestEvents::LATE, + 'fn' => function (EndEvent $e) use ($hash) { + $hash[$e->getRequest()] = $e->getException() + ? $e->getException() + : $e->getResponse(); + } + ] + )))->wait(); + + return new BatchResults($hash); + } + + /** + * Creates a Pool and immediately sends the requests. + * + * @param ClientInterface $client Client used to send the requests + * @param array|\Iterator $requests Requests to send in parallel + * @param array $options Passes through the options available in + * {@see GuzzleHttp\Pool::__construct} + */ + public static function send( + ClientInterface $client, + $requests, + array $options = [] + ) { + $pool = new self($client, $requests, $options); + $pool->wait(); + } + + private function getPoolSize() + { + return is_callable($this->poolSize) + ? call_user_func($this->poolSize, count($this->waitQueue)) + : $this->poolSize; + } + + /** + * Add as many requests as possible up to the current pool limit. + */ + private function addNextRequests() + { + $limit = max($this->getPoolSize() - count($this->waitQueue), 0); + while ($limit--) { + if (!$this->addNextRequest()) { + break; + } + } + } + + public function wait() + { + if ($this->isRealized) { + return false; + } + + // Seed the pool with N number of requests. + $this->addNextRequests(); + + // Stop if the pool was cancelled while transferring requests. + if ($this->isRealized) { + return false; + } + + // Wait on any outstanding FutureResponse objects. + while ($response = array_pop($this->waitQueue)) { + try { + $response->wait(); + } catch (\Exception $e) { + // Eat exceptions because they should be handled asynchronously + } + $this->addNextRequests(); + } + + // Clean up no longer needed state. + $this->isRealized = true; + $this->waitQueue = $this->eventListeners = []; + $this->client = $this->iter = null; + $this->deferred->resolve(true); + + return true; + } + + /** + * {@inheritdoc} + * + * Attempt to cancel all outstanding requests (requests that are queued for + * dereferencing). Returns true if all outstanding requests can be + * cancelled. + * + * @return bool + */ + public function cancel() + { + if ($this->isRealized) { + return false; + } + + $success = $this->isRealized = true; + foreach ($this->waitQueue as $response) { + if (!$response->cancel()) { + $success = false; + } + } + + return $success; + } + + /** + * Returns a promise that is invoked when the pool completed. There will be + * no passed value. + * + * {@inheritdoc} + */ + public function then( + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return $this->promise->then($onFulfilled, $onRejected, $onProgress); + } + + public function promise() + { + return $this->promise; + } + + private function coerceIterable($requests) + { + if ($requests instanceof \Iterator) { + return $requests; + } elseif (is_array($requests)) { + return new \ArrayIterator($requests); + } + + throw new \InvalidArgumentException('Expected Iterator or array. ' + . 'Found ' . Core::describeType($requests)); + } + + /** + * Adds the next request to pool and tracks what requests need to be + * dereferenced when completing the pool. + */ + private function addNextRequest() + { + add_next: + + if ($this->isRealized || !$this->iter || !$this->iter->valid()) { + return false; + } + + $request = $this->iter->current(); + $this->iter->next(); + + if (!($request instanceof RequestInterface)) { + throw new \InvalidArgumentException(sprintf( + 'All requests in the provided iterator must implement ' + . 'RequestInterface. Found %s', + Core::describeType($request) + )); + } + + // Be sure to use "lazy" futures, meaning they do not send right away. + $request->getConfig()->set('future', 'lazy'); + $hash = spl_object_hash($request); + $this->attachListeners($request, $this->eventListeners); + $request->getEmitter()->on('before', [$this, '_trackRetries'], RequestEvents::EARLY); + $response = $this->client->send($request); + $this->waitQueue[$hash] = $response; + $promise = $response->promise(); + + // Don't recursively call itself for completed or rejected responses. + if ($promise instanceof FulfilledPromise + || $promise instanceof RejectedPromise + ) { + try { + $this->finishResponse($request, $response->wait(), $hash); + } catch (\Exception $e) { + $this->finishResponse($request, $e, $hash); + } + goto add_next; + } + + // Use this function for both resolution and rejection. + $thenFn = function ($value) use ($request, $hash) { + $this->finishResponse($request, $value, $hash); + if (!$request->getConfig()->get('_pool_retries')) { + $this->addNextRequests(); + } + }; + + $promise->then($thenFn, $thenFn); + + return true; + } + + public function _trackRetries(BeforeEvent $e) + { + $e->getRequest()->getConfig()->set('_pool_retries', $e->getRetryCount()); + } + + private function finishResponse($request, $value, $hash) + { + unset($this->waitQueue[$hash]); + $result = $value instanceof ResponseInterface + ? ['request' => $request, 'response' => $value, 'error' => null] + : ['request' => $request, 'response' => null, 'error' => $value]; + $this->deferred->notify($result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php new file mode 100755 index 000000000..1149e6235 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php @@ -0,0 +1,109 @@ +boundary = $boundary ?: uniqid(); + $this->stream = $this->createStream($fields, $files); + } + + /** + * Get the boundary + * + * @return string + */ + public function getBoundary() + { + return $this->boundary; + } + + public function isWritable() + { + return false; + } + + /** + * Get the string needed to transfer a POST field + */ + private function getFieldString($name, $value) + { + return sprintf( + "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", + $this->boundary, + $name, + $value + ); + } + + /** + * Get the headers needed before transferring the content of a POST file + */ + private function getFileHeaders(PostFileInterface $file) + { + $headers = ''; + foreach ($file->getHeaders() as $key => $value) { + $headers .= "{$key}: {$value}\r\n"; + } + + return "--{$this->boundary}\r\n" . trim($headers) . "\r\n\r\n"; + } + + /** + * Create the aggregate stream that will be used to upload the POST data + */ + protected function createStream(array $fields, array $files) + { + $stream = new AppendStream(); + + foreach ($fields as $name => $fieldValues) { + foreach ((array) $fieldValues as $value) { + $stream->addStream( + Stream::factory($this->getFieldString($name, $value)) + ); + } + } + + foreach ($files as $file) { + + if (!$file instanceof PostFileInterface) { + throw new \InvalidArgumentException('All POST fields must ' + . 'implement PostFieldInterface'); + } + + $stream->addStream( + Stream::factory($this->getFileHeaders($file)) + ); + $stream->addStream($file->getContent()); + $stream->addStream(Stream::factory("\r\n")); + } + + // Add the trailing boundary with CRLF + $stream->addStream(Stream::factory("--{$this->boundary}--\r\n")); + + return $stream; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBody.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBody.php new file mode 100755 index 000000000..ed14d1f70 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBody.php @@ -0,0 +1,287 @@ +files || $this->forceMultipart) { + $request->setHeader( + 'Content-Type', + 'multipart/form-data; boundary=' . $this->getBody()->getBoundary() + ); + } elseif ($this->fields && !$request->hasHeader('Content-Type')) { + $request->setHeader( + 'Content-Type', + 'application/x-www-form-urlencoded' + ); + } + + if ($size = $this->getSize()) { + $request->setHeader('Content-Length', $size); + } + } + + public function forceMultipartUpload($force) + { + $this->forceMultipart = $force; + } + + public function setAggregator(callable $aggregator) + { + $this->aggregator = $aggregator; + } + + public function setField($name, $value) + { + $this->fields[$name] = $value; + $this->mutate(); + } + + public function replaceFields(array $fields) + { + $this->fields = $fields; + $this->mutate(); + } + + public function getField($name) + { + return isset($this->fields[$name]) ? $this->fields[$name] : null; + } + + public function removeField($name) + { + unset($this->fields[$name]); + $this->mutate(); + } + + public function getFields($asString = false) + { + if (!$asString) { + return $this->fields; + } + + $query = new Query($this->fields); + $query->setEncodingType(Query::RFC1738); + $query->setAggregator($this->getAggregator()); + + return (string) $query; + } + + public function hasField($name) + { + return isset($this->fields[$name]); + } + + public function getFile($name) + { + foreach ($this->files as $file) { + if ($file->getName() == $name) { + return $file; + } + } + + return null; + } + + public function getFiles() + { + return $this->files; + } + + public function addFile(PostFileInterface $file) + { + $this->files[] = $file; + $this->mutate(); + } + + public function clearFiles() + { + $this->files = []; + $this->mutate(); + } + + /** + * Returns the numbers of fields + files + * + * @return int + */ + public function count() + { + return count($this->files) + count($this->fields); + } + + public function __toString() + { + return (string) $this->getBody(); + } + + public function getContents($maxLength = -1) + { + return $this->getBody()->getContents(); + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->detached = true; + $this->fields = $this->files = []; + + if ($this->body) { + $this->body->close(); + $this->body = null; + } + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function eof() + { + return $this->getBody()->eof(); + } + + public function tell() + { + return $this->body ? $this->body->tell() : 0; + } + + public function isSeekable() + { + return true; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function getSize() + { + return $this->getBody()->getSize(); + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->getBody()->seek($offset, $whence); + } + + public function read($length) + { + return $this->getBody()->read($length); + } + + public function write($string) + { + return false; + } + + public function getMetadata($key = null) + { + return $key ? null : []; + } + + /** + * Return a stream object that is built from the POST fields and files. + * + * If one has already been created, the previously created stream will be + * returned. + */ + private function getBody() + { + if ($this->body) { + return $this->body; + } elseif ($this->files || $this->forceMultipart) { + return $this->body = $this->createMultipart(); + } elseif ($this->fields) { + return $this->body = $this->createUrlEncoded(); + } else { + return $this->body = Stream::factory(); + } + } + + /** + * Get the aggregator used to join multi-valued field parameters + * + * @return callable + */ + final protected function getAggregator() + { + if (!$this->aggregator) { + $this->aggregator = Query::phpAggregator(); + } + + return $this->aggregator; + } + + /** + * Creates a multipart/form-data body stream + * + * @return MultipartBody + */ + private function createMultipart() + { + // Flatten the nested query string values using the correct aggregator + return new MultipartBody( + call_user_func($this->getAggregator(), $this->fields), + $this->files + ); + } + + /** + * Creates an application/x-www-form-urlencoded stream body + * + * @return StreamInterface + */ + private function createUrlEncoded() + { + return Stream::factory($this->getFields(true)); + } + + /** + * Get rid of any cached data + */ + private function mutate() + { + $this->body = null; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php new file mode 100755 index 000000000..c2ec9a62c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php @@ -0,0 +1,109 @@ +headers = $headers; + $this->name = $name; + $this->prepareContent($content); + $this->prepareFilename($filename); + $this->prepareDefaultHeaders(); + } + + public function getName() + { + return $this->name; + } + + public function getFilename() + { + return $this->filename; + } + + public function getContent() + { + return $this->content; + } + + public function getHeaders() + { + return $this->headers; + } + + /** + * Prepares the contents of a POST file. + * + * @param mixed $content Content of the POST file + */ + private function prepareContent($content) + { + $this->content = $content; + + if (!($this->content instanceof StreamInterface)) { + $this->content = Stream::factory($this->content); + } elseif ($this->content instanceof MultipartBody) { + if (!$this->hasHeader('Content-Disposition')) { + $disposition = 'form-data; name="' . $this->name .'"'; + $this->headers['Content-Disposition'] = $disposition; + } + + if (!$this->hasHeader('Content-Type')) { + $this->headers['Content-Type'] = sprintf( + "multipart/form-data; boundary=%s", + $this->content->getBoundary() + ); + } + } + } + + /** + * Applies a file name to the POST file based on various checks. + * + * @param string|null $filename Filename to apply (or null to guess) + */ + private function prepareFilename($filename) + { + $this->filename = $filename; + + if (!$this->filename) { + $this->filename = $this->content->getMetadata('uri'); + } + + if (!$this->filename || substr($this->filename, 0, 6) === 'php://') { + $this->filename = $this->name; + } + } + + /** + * Applies default Content-Disposition and Content-Type headers if needed. + */ + private function prepareDefaultHeaders() + { + // Set a default content-disposition header if one was no provided + if (!$this->hasHeader('Content-Disposition')) { + $this->headers['Content-Disposition'] = sprintf( + 'form-data; name="%s"; filename="%s"', + $this->name, + basename($this->filename) + ); + } + + // Set a default Content-Type if one was not supplied + if (!$this->hasHeader('Content-Type')) { + $this->headers['Content-Type'] = Mimetypes::getInstance() + ->fromFilename($this->filename) ?: 'text/plain'; + } + } + + /** + * Check if a specific header exists on the POST file by name. + * + * @param string $name Case-insensitive header to check + * + * @return bool + */ + private function hasHeader($name) + { + return isset(array_change_key_case($this->headers)[strtolower($name)]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php new file mode 100755 index 000000000..2e816c088 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php @@ -0,0 +1,41 @@ +setEncodingType($urlEncoding); + } + + $qp->parseInto($q, $query, $urlEncoding); + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + */ + public function __toString() + { + if (!$this->data) { + return ''; + } + + // The default aggregator is statically cached + static $defaultAggregator; + + if (!$this->aggregator) { + if (!$defaultAggregator) { + $defaultAggregator = self::phpAggregator(); + } + $this->aggregator = $defaultAggregator; + } + + $result = ''; + $aggregator = $this->aggregator; + $encoder = $this->encoding; + + foreach ($aggregator($this->data) as $key => $values) { + foreach ($values as $value) { + if ($result) { + $result .= '&'; + } + $result .= $encoder($key); + if ($value !== null) { + $result .= '=' . $encoder($value); + } + } + } + + return $result; + } + + /** + * Controls how multi-valued query string parameters are aggregated into a + * string. + * + * $query->setAggregator($query::duplicateAggregator()); + * + * @param callable $aggregator Callable used to convert a deeply nested + * array of query string variables into a flattened array of key value + * pairs. The callable accepts an array of query data and returns a + * flattened array of key value pairs where each value is an array of + * strings. + */ + public function setAggregator(callable $aggregator) + { + $this->aggregator = $aggregator; + } + + /** + * Specify how values are URL encoded + * + * @param string|bool $type One of 'RFC1738', 'RFC3986', or false to disable encoding + * + * @throws \InvalidArgumentException + */ + public function setEncodingType($type) + { + switch ($type) { + case self::RFC3986: + $this->encoding = 'rawurlencode'; + break; + case self::RFC1738: + $this->encoding = 'urlencode'; + break; + case false: + $this->encoding = function ($v) { return $v; }; + break; + default: + throw new \InvalidArgumentException('Invalid URL encoding type'); + } + } + + /** + * Query string aggregator that does not aggregate nested query string + * values and allows duplicates in the resulting array. + * + * Example: http://test.com?q=1&q=2 + * + * @return callable + */ + public static function duplicateAggregator() + { + return function (array $data) { + return self::walkQuery($data, '', function ($key, $prefix) { + return is_int($key) ? $prefix : "{$prefix}[{$key}]"; + }); + }; + } + + /** + * Aggregates nested query string variables using the same technique as + * ``http_build_query()``. + * + * @param bool $numericIndices Pass false to not include numeric indices + * when multi-values query string parameters are present. + * + * @return callable + */ + public static function phpAggregator($numericIndices = true) + { + return function (array $data) use ($numericIndices) { + return self::walkQuery( + $data, + '', + function ($key, $prefix) use ($numericIndices) { + return !$numericIndices && is_int($key) + ? "{$prefix}[]" + : "{$prefix}[{$key}]"; + } + ); + }; + } + + /** + * Easily create query aggregation functions by providing a key prefix + * function to this query string array walker. + * + * @param array $query Query string to walk + * @param string $keyPrefix Key prefix (start with '') + * @param callable $prefixer Function used to create a key prefix + * + * @return array + */ + public static function walkQuery(array $query, $keyPrefix, callable $prefixer) + { + $result = []; + foreach ($query as $key => $value) { + if ($keyPrefix) { + $key = $prefixer($key, $keyPrefix); + } + if (is_array($value)) { + $result += self::walkQuery($value, $key, $prefixer); + } elseif (isset($result[$key])) { + $result[$key][] = $value; + } else { + $result[$key] = array($value); + } + } + + return $result; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/QueryParser.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/QueryParser.php new file mode 100755 index 000000000..90727cc6c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/QueryParser.php @@ -0,0 +1,163 @@ +duplicates = false; + $this->numericIndices = true; + $decoder = self::getDecoder($urlEncoding); + + foreach (explode('&', $str) as $kvp) { + + $parts = explode('=', $kvp, 2); + $key = $decoder($parts[0]); + $value = isset($parts[1]) ? $decoder($parts[1]) : null; + + // Special handling needs to be taken for PHP nested array syntax + if (strpos($key, '[') !== false) { + $this->parsePhpValue($key, $value, $result); + continue; + } + + if (!isset($result[$key])) { + $result[$key] = $value; + } else { + $this->duplicates = true; + if (!is_array($result[$key])) { + $result[$key] = [$result[$key]]; + } + $result[$key][] = $value; + } + } + + $query->replace($result); + + if (!$this->numericIndices) { + $query->setAggregator(Query::phpAggregator(false)); + } elseif ($this->duplicates) { + $query->setAggregator(Query::duplicateAggregator()); + } + } + + /** + * Returns a callable that is used to URL decode query keys and values. + * + * @param string|bool $type One of true, false, RFC3986, and RFC1738 + * + * @return callable|string + */ + private static function getDecoder($type) + { + if ($type === true) { + return function ($value) { + return rawurldecode(str_replace('+', ' ', $value)); + }; + } elseif ($type == Query::RFC3986) { + return 'rawurldecode'; + } elseif ($type == Query::RFC1738) { + return 'urldecode'; + } else { + return function ($str) { return $str; }; + } + } + + /** + * Parses a PHP style key value pair. + * + * @param string $key Key to parse (e.g., "foo[a][b]") + * @param string|null $value Value to set + * @param array $result Result to modify by reference + */ + private function parsePhpValue($key, $value, array &$result) + { + $node =& $result; + $keyBuffer = ''; + + for ($i = 0, $t = strlen($key); $i < $t; $i++) { + switch ($key[$i]) { + case '[': + if ($keyBuffer) { + $this->prepareNode($node, $keyBuffer); + $node =& $node[$keyBuffer]; + $keyBuffer = ''; + } + break; + case ']': + $k = $this->cleanKey($node, $keyBuffer); + $this->prepareNode($node, $k); + $node =& $node[$k]; + $keyBuffer = ''; + break; + default: + $keyBuffer .= $key[$i]; + break; + } + } + + if (isset($node)) { + $this->duplicates = true; + $node[] = $value; + } else { + $node = $value; + } + } + + /** + * Prepares a value in the array at the given key. + * + * If the key already exists, the key value is converted into an array. + * + * @param array $node Result node to modify + * @param string $key Key to add or modify in the node + */ + private function prepareNode(&$node, $key) + { + if (!isset($node[$key])) { + $node[$key] = null; + } elseif (!is_array($node[$key])) { + $node[$key] = [$node[$key]]; + } + } + + /** + * Returns the appropriate key based on the node and key. + */ + private function cleanKey($node, $key) + { + if ($key === '') { + $key = $node ? (string) count($node) : 0; + // Found a [] key, so track this to ensure that we disable numeric + // indexing of keys in the resolved query aggregator. + $this->numericIndices = false; + } + + return $key; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RequestFsm.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RequestFsm.php new file mode 100755 index 000000000..b37c190d4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RequestFsm.php @@ -0,0 +1,153 @@ +mf = $messageFactory; + $this->maxTransitions = $maxTransitions; + $this->handler = $handler; + } + + /** + * Runs the state machine until a terminal state is entered or the + * optionally supplied $finalState is entered. + * + * @param Transaction $trans Transaction being transitioned. + * + * @throws \Exception if a terminal state throws an exception. + */ + public function __invoke(Transaction $trans) + { + $trans->_transitionCount = 0; + + if (!$trans->state) { + $trans->state = 'before'; + } + + transition: + + if (++$trans->_transitionCount > $this->maxTransitions) { + throw new StateException("Too many state transitions were " + . "encountered ({$trans->_transitionCount}). This likely " + . "means that a combination of event listeners are in an " + . "infinite loop."); + } + + switch ($trans->state) { + case 'before': goto before; + case 'complete': goto complete; + case 'error': goto error; + case 'retry': goto retry; + case 'send': goto send; + case 'end': goto end; + default: throw new StateException("Invalid state: {$trans->state}"); + } + + before: { + try { + $trans->request->getEmitter()->emit('before', new BeforeEvent($trans)); + $trans->state = 'send'; + if ((bool) $trans->response) { + $trans->state = 'complete'; + } + } catch (\Exception $e) { + $trans->state = 'error'; + $trans->exception = $e; + } + goto transition; + } + + complete: { + try { + if ($trans->response instanceof FutureInterface) { + // Futures will have their own end events emitted when + // dereferenced. + return; + } + $trans->state = 'end'; + $trans->response->setEffectiveUrl($trans->request->getUrl()); + $trans->request->getEmitter()->emit('complete', new CompleteEvent($trans)); + } catch (\Exception $e) { + $trans->state = 'error'; + $trans->exception = $e; + } + goto transition; + } + + error: { + try { + // Convert non-request exception to a wrapped exception + $trans->exception = RequestException::wrapException( + $trans->request, $trans->exception + ); + $trans->state = 'end'; + $trans->request->getEmitter()->emit('error', new ErrorEvent($trans)); + // An intercepted request (not retried) transitions to complete + if (!$trans->exception && $trans->state !== 'retry') { + $trans->state = 'complete'; + } + } catch (\Exception $e) { + $trans->state = 'end'; + $trans->exception = $e; + } + goto transition; + } + + retry: { + $trans->retries++; + $trans->response = null; + $trans->exception = null; + $trans->state = 'before'; + goto transition; + } + + send: { + $fn = $this->handler; + $trans->response = FutureResponse::proxy( + $fn(RingBridge::prepareRingRequest($trans)), + function ($value) use ($trans) { + RingBridge::completeRingResponse($trans, $value, $this->mf, $this); + $this($trans); + return $trans->response; + } + ); + return; + } + + end: { + $trans->request->getEmitter()->emit('end', new EndEvent($trans)); + // Throw exceptions in the terminal event if the exception + // was not handled by an "end" event listener. + if ($trans->exception) { + if (!($trans->exception instanceof RequestException)) { + $trans->exception = RequestException::wrapException( + $trans->request, $trans->exception + ); + } + throw $trans->exception; + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RingBridge.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RingBridge.php new file mode 100755 index 000000000..bc6841d42 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/RingBridge.php @@ -0,0 +1,165 @@ +getConfig()->toArray(); + $url = $request->getUrl(); + // No need to calculate the query string twice (in URL and query). + $qs = ($pos = strpos($url, '?')) ? substr($url, $pos + 1) : null; + + return [ + 'scheme' => $request->getScheme(), + 'http_method' => $request->getMethod(), + 'url' => $url, + 'uri' => $request->getPath(), + 'headers' => $request->getHeaders(), + 'body' => $request->getBody(), + 'version' => $request->getProtocolVersion(), + 'client' => $options, + 'query_string' => $qs, + 'future' => isset($options['future']) ? $options['future'] : false + ]; + } + + /** + * Creates a Ring request from a request object AND prepares the callbacks. + * + * @param Transaction $trans Transaction to update. + * + * @return array Converted Guzzle Ring request. + */ + public static function prepareRingRequest(Transaction $trans) + { + // Clear out the transaction state when initiating. + $trans->exception = null; + $request = self::createRingRequest($trans->request); + + // Emit progress events if any progress listeners are registered. + if ($trans->request->getEmitter()->hasListeners('progress')) { + $emitter = $trans->request->getEmitter(); + $request['client']['progress'] = function ($a, $b, $c, $d) use ($trans, $emitter) { + $emitter->emit('progress', new ProgressEvent($trans, $a, $b, $c, $d)); + }; + } + + return $request; + } + + /** + * Handles the process of processing a response received from a ring + * handler. The created response is added to the transaction, and the + * transaction stat is set appropriately. + * + * @param Transaction $trans Owns request and response. + * @param array $response Ring response array + * @param MessageFactoryInterface $messageFactory Creates response objects. + */ + public static function completeRingResponse( + Transaction $trans, + array $response, + MessageFactoryInterface $messageFactory + ) { + $trans->state = 'complete'; + $trans->transferInfo = isset($response['transfer_stats']) + ? $response['transfer_stats'] : []; + + if (!empty($response['status'])) { + $options = []; + if (isset($response['version'])) { + $options['protocol_version'] = $response['version']; + } + if (isset($response['reason'])) { + $options['reason_phrase'] = $response['reason']; + } + $trans->response = $messageFactory->createResponse( + $response['status'], + isset($response['headers']) ? $response['headers'] : [], + isset($response['body']) ? $response['body'] : null, + $options + ); + if (isset($response['effective_url'])) { + $trans->response->setEffectiveUrl($response['effective_url']); + } + } elseif (empty($response['error'])) { + // When nothing was returned, then we need to add an error. + $response['error'] = self::getNoRingResponseException($trans->request); + } + + if (isset($response['error'])) { + $trans->state = 'error'; + $trans->exception = $response['error']; + } + } + + /** + * Creates a Guzzle request object using a ring request array. + * + * @param array $request Ring request + * + * @return Request + * @throws \InvalidArgumentException for incomplete requests. + */ + public static function fromRingRequest(array $request) + { + $options = []; + if (isset($request['version'])) { + $options['protocol_version'] = $request['version']; + } + + if (!isset($request['http_method'])) { + throw new \InvalidArgumentException('No http_method'); + } + + return new Request( + $request['http_method'], + Core::url($request), + isset($request['headers']) ? $request['headers'] : [], + isset($request['body']) ? Stream::factory($request['body']) : null, + $options + ); + } + + /** + * Get an exception that can be used when a RingPHP handler does not + * populate a response. + * + * @param RequestInterface $request + * + * @return RequestException + */ + public static function getNoRingResponseException(RequestInterface $request) + { + $message = <<cookieJar = $cookieJar ?: new CookieJar(); + } + + public function getEvents() + { + // Fire the cookie plugin complete event before redirecting + return [ + 'before' => ['onBefore'], + 'complete' => ['onComplete', RequestEvents::REDIRECT_RESPONSE + 10] + ]; + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + public function onBefore(BeforeEvent $event) + { + $this->cookieJar->addCookieHeader($event->getRequest()); + } + + public function onComplete(CompleteEvent $event) + { + $this->cookieJar->extractCookies( + $event->getRequest(), + $event->getResponse() + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/History.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/History.php new file mode 100755 index 000000000..5cf06119f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/History.php @@ -0,0 +1,172 @@ +limit = $limit; + } + + public function getEvents() + { + return [ + 'complete' => ['onComplete', RequestEvents::EARLY], + 'error' => ['onError', RequestEvents::EARLY], + ]; + } + + /** + * Convert to a string that contains all request and response headers + * + * @return string + */ + public function __toString() + { + $lines = array(); + foreach ($this->transactions as $entry) { + $response = isset($entry['response']) ? $entry['response'] : ''; + $lines[] = '> ' . trim($entry['sent_request']) + . "\n\n< " . trim($response) . "\n"; + } + + return implode("\n", $lines); + } + + public function onComplete(CompleteEvent $event) + { + $this->add($event->getRequest(), $event->getResponse()); + } + + public function onError(ErrorEvent $event) + { + // Only track when no response is present, meaning this didn't ever + // emit a complete event + if (!$event->getResponse()) { + $this->add($event->getRequest()); + } + } + + /** + * Returns an Iterator that yields associative array values where each + * associative array contains the following key value pairs: + * + * - request: Representing the actual request that was received. + * - sent_request: A clone of the request that will not be mutated. + * - response: The response that was received (if available). + * + * @return \Iterator + */ + public function getIterator() + { + return new \ArrayIterator($this->transactions); + } + + /** + * Get all of the requests sent through the plugin. + * + * Requests can be modified after they are logged by the history + * subscriber. By default this method will return the actual request + * instances that were received. Pass true to this method if you wish to + * get copies of the requests that represent the request state when it was + * initially logged by the history subscriber. + * + * @param bool $asSent Set to true to get clones of the requests that have + * not been mutated since the request was received by + * the history subscriber. + * + * @return RequestInterface[] + */ + public function getRequests($asSent = false) + { + return array_map(function ($t) use ($asSent) { + return $asSent ? $t['sent_request'] : $t['request']; + }, $this->transactions); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->transactions); + } + + /** + * Get the last request sent. + * + * Requests can be modified after they are logged by the history + * subscriber. By default this method will return the actual request + * instance that was received. Pass true to this method if you wish to get + * a copy of the request that represents the request state when it was + * initially logged by the history subscriber. + * + * @param bool $asSent Set to true to get a clone of the last request that + * has not been mutated since the request was received + * by the history subscriber. + * + * @return RequestInterface + */ + public function getLastRequest($asSent = false) + { + return $asSent + ? end($this->transactions)['sent_request'] + : end($this->transactions)['request']; + } + + /** + * Get the last response in the history + * + * @return ResponseInterface|null + */ + public function getLastResponse() + { + return end($this->transactions)['response']; + } + + /** + * Clears the history + */ + public function clear() + { + $this->transactions = array(); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * @param ResponseInterface $response Response of the request + */ + private function add( + RequestInterface $request, + ResponseInterface $response = null + ) { + $this->transactions[] = [ + 'request' => $request, + 'sent_request' => clone $request, + 'response' => $response + ]; + if (count($this->transactions) > $this->limit) { + array_shift($this->transactions); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php new file mode 100755 index 000000000..ed9de5bcc --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php @@ -0,0 +1,36 @@ + ['onComplete', RequestEvents::VERIFY_RESPONSE]]; + } + + /** + * Throw a RequestException on an HTTP protocol error + * + * @param CompleteEvent $event Emitted event + * @throws RequestException + */ + public function onComplete(CompleteEvent $event) + { + $code = (string) $event->getResponse()->getStatusCode(); + // Throw an exception for an unsuccessful response + if ($code[0] >= 4) { + throw RequestException::create( + $event->getRequest(), + $event->getResponse() + ); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php new file mode 100755 index 000000000..2af4d3758 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php @@ -0,0 +1,147 @@ +factory = new MessageFactory(); + $this->readBodies = $readBodies; + $this->addMultiple($items); + } + + public function getEvents() + { + // Fire the event last, after signing + return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST - 10]]; + } + + /** + * @throws \OutOfBoundsException|\Exception + */ + public function onBefore(BeforeEvent $event) + { + if (!$item = array_shift($this->queue)) { + throw new \OutOfBoundsException('Mock queue is empty'); + } elseif ($item instanceof RequestException) { + throw $item; + } + + // Emulate reading a response body + $request = $event->getRequest(); + if ($this->readBodies && $request->getBody()) { + while (!$request->getBody()->eof()) { + $request->getBody()->read(8096); + } + } + + $saveTo = $event->getRequest()->getConfig()->get('save_to'); + + if (null !== $saveTo) { + $body = $item->getBody(); + + if (is_resource($saveTo)) { + fwrite($saveTo, $body); + } elseif (is_string($saveTo)) { + file_put_contents($saveTo, $body); + } elseif ($saveTo instanceof StreamInterface) { + $saveTo->write($body); + } + } + + $event->intercept($item); + } + + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|ResponseInterface $response Response or path to response file + * + * @return self + * @throws \InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (is_string($response)) { + $response = file_exists($response) + ? $this->factory->fromMessage(file_get_contents($response)) + : $this->factory->fromMessage($response); + } elseif (!($response instanceof ResponseInterface)) { + throw new \InvalidArgumentException('Response must a message ' + . 'string, response object, or path to a file'); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Add an exception to the end of the queue + * + * @param RequestException $e Exception to throw when the request is executed + * + * @return self + */ + public function addException(RequestException $e) + { + $this->queue[] = $e; + + return $this; + } + + /** + * Add multiple items to the queue + * + * @param array $items Items to add + */ + public function addMultiple(array $items) + { + foreach ($items as $item) { + if ($item instanceof RequestException) { + $this->addException($item); + } else { + $this->addResponse($item); + } + } + } + + /** + * Clear the queue + */ + public function clearQueue() + { + $this->queue = []; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php new file mode 100755 index 000000000..b5ed4e260 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php @@ -0,0 +1,130 @@ + ['onBefore', RequestEvents::PREPARE_REQUEST]]; + } + + public function onBefore(BeforeEvent $event) + { + $request = $event->getRequest(); + + // Set the appropriate Content-Type for a request if one is not set and + // there are form fields + if (!($body = $request->getBody())) { + return; + } + + $this->addContentLength($request, $body); + + if ($body instanceof AppliesHeadersInterface) { + // Synchronize the body with the request headers + $body->applyRequestHeaders($request); + } elseif (!$request->hasHeader('Content-Type')) { + $this->addContentType($request, $body); + } + + $this->addExpectHeader($request, $body); + } + + private function addContentType( + RequestInterface $request, + StreamInterface $body + ) { + if (!($uri = $body->getMetadata('uri'))) { + return; + } + + // Guess the content-type based on the stream's "uri" metadata value. + // The file extension is used to determine the appropriate mime-type. + if ($contentType = Mimetypes::getInstance()->fromFilename($uri)) { + $request->setHeader('Content-Type', $contentType); + } + } + + private function addContentLength( + RequestInterface $request, + StreamInterface $body + ) { + // Set the Content-Length header if it can be determined, and never + // send a Transfer-Encoding: chunked and Content-Length header in + // the same request. + if ($request->hasHeader('Content-Length')) { + // Remove transfer-encoding if content-length is set. + $request->removeHeader('Transfer-Encoding'); + return; + } + + if ($request->hasHeader('Transfer-Encoding')) { + return; + } + + if (null !== ($size = $body->getSize())) { + $request->setHeader('Content-Length', $size); + $request->removeHeader('Transfer-Encoding'); + } elseif ('1.1' == $request->getProtocolVersion()) { + // Use chunked Transfer-Encoding if there is no determinable + // content-length header and we're using HTTP/1.1. + $request->setHeader('Transfer-Encoding', 'chunked'); + $request->removeHeader('Content-Length'); + } + } + + private function addExpectHeader( + RequestInterface $request, + StreamInterface $body + ) { + // Determine if the Expect header should be used + if ($request->hasHeader('Expect')) { + return; + } + + $expect = $request->getConfig()['expect']; + + // Return if disabled or if you're not using HTTP/1.1 + if ($expect === false || $request->getProtocolVersion() !== '1.1') { + return; + } + + // The expect header is unconditionally enabled + if ($expect === true) { + $request->setHeader('Expect', '100-Continue'); + return; + } + + // By default, send the expect header when the payload is > 1mb + if ($expect === null) { + $expect = 1048576; + } + + // Always add if the body cannot be rewound, the size cannot be + // determined, or the size is greater than the cutoff threshold + $size = $body->getSize(); + if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { + $request->setHeader('Expect', '100-Continue'); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php new file mode 100755 index 000000000..ff992268b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php @@ -0,0 +1,176 @@ + ['onComplete', RequestEvents::REDIRECT_RESPONSE]]; + } + + /** + * Rewind the entity body of the request if needed + * + * @param RequestInterface $redirectRequest + * @throws CouldNotRewindStreamException + */ + public static function rewindEntityBody(RequestInterface $redirectRequest) + { + // Rewind the entity body of the request if needed + if ($body = $redirectRequest->getBody()) { + // Only rewind the body if some of it has been read already, and + // throw an exception if the rewind fails + if ($body->tell() && !$body->seek(0)) { + throw new CouldNotRewindStreamException( + 'Unable to rewind the non-seekable request body after redirecting', + $redirectRequest + ); + } + } + } + + /** + * Called when a request receives a redirect response + * + * @param CompleteEvent $event Event emitted + * @throws TooManyRedirectsException + */ + public function onComplete(CompleteEvent $event) + { + $response = $event->getResponse(); + + if (substr($response->getStatusCode(), 0, 1) != '3' + || !$response->hasHeader('Location') + ) { + return; + } + + $request = $event->getRequest(); + $config = $request->getConfig(); + + // Increment the redirect and initialize the redirect state. + if ($redirectCount = $config['redirect_count']) { + $config['redirect_count'] = ++$redirectCount; + } else { + $config['redirect_scheme'] = $request->getScheme(); + $config['redirect_count'] = $redirectCount = 1; + } + + $max = $config->getPath('redirect/max') ?: 5; + + if ($redirectCount > $max) { + throw new TooManyRedirectsException( + "Will not follow more than {$redirectCount} redirects", + $request + ); + } + + $this->modifyRedirectRequest($request, $response); + $event->retry(); + } + + private function modifyRedirectRequest( + RequestInterface $request, + ResponseInterface $response + ) { + $config = $request->getConfig(); + $protocols = $config->getPath('redirect/protocols') ?: ['http', 'https']; + + // Use a GET request if this is an entity enclosing request and we are + // not forcing RFC compliance, but rather emulating what all browsers + // would do. + $statusCode = $response->getStatusCode(); + if ($statusCode == 303 || + ($statusCode <= 302 && $request->getBody() && !$config->getPath('redirect/strict')) + ) { + $request->setMethod('GET'); + $request->setBody(null); + } + + $previousUrl = $request->getUrl(); + $this->setRedirectUrl($request, $response, $protocols); + $this->rewindEntityBody($request); + + // Add the Referer header if it is told to do so and only + // add the header if we are not redirecting from https to http. + if ($config->getPath('redirect/referer') + && ($request->getScheme() == 'https' || $request->getScheme() == $config['redirect_scheme']) + ) { + $url = Url::fromString($previousUrl); + $url->setUsername(null); + $url->setPassword(null); + $request->setHeader('Referer', (string) $url); + } else { + $request->removeHeader('Referer'); + } + } + + /** + * Set the appropriate URL on the request based on the location header + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param array $protocols + */ + private function setRedirectUrl( + RequestInterface $request, + ResponseInterface $response, + array $protocols + ) { + $location = $response->getHeader('Location'); + $location = Url::fromString($location); + + // Combine location with the original URL if it is not absolute. + if (!$location->isAbsolute()) { + $originalUrl = Url::fromString($request->getUrl()); + // Remove query string parameters and just take what is present on + // the redirect Location header + $originalUrl->getQuery()->clear(); + $location = $originalUrl->combine($location); + } + + // Ensure that the redirect URL is allowed based on the protocols. + if (!in_array($location->getScheme(), $protocols)) { + throw new BadResponseException( + sprintf( + 'Redirect URL, %s, does not use one of the allowed redirect protocols: %s', + $location, + implode(', ', $protocols) + ), + $request, + $response + ); + } + + $request->setUrl($location); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php new file mode 100755 index 000000000..d57c0229a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php @@ -0,0 +1,15 @@ +client = $client; + $this->request = $request; + $this->_future = $future; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/UriTemplate.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/UriTemplate.php new file mode 100755 index 000000000..55dfeb5a4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/UriTemplate.php @@ -0,0 +1,241 @@ + array('prefix' => '', 'joiner' => ',', 'query' => false), + '+' => array('prefix' => '', 'joiner' => ',', 'query' => false), + '#' => array('prefix' => '#', 'joiner' => ',', 'query' => false), + '.' => array('prefix' => '.', 'joiner' => '.', 'query' => false), + '/' => array('prefix' => '/', 'joiner' => '/', 'query' => false), + ';' => array('prefix' => ';', 'joiner' => ';', 'query' => true), + '?' => array('prefix' => '?', 'joiner' => '&', 'query' => true), + '&' => array('prefix' => '&', 'joiner' => '&', 'query' => true) + ); + + /** @var array Delimiters */ + private static $delims = array(':', '/', '?', '#', '[', ']', '@', '!', '$', + '&', '\'', '(', ')', '*', '+', ',', ';', '='); + + /** @var array Percent encoded delimiters */ + private static $delimsPct = array('%3A', '%2F', '%3F', '%23', '%5B', '%5D', + '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D'); + + public function expand($template, array $variables) + { + if (false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback( + '/\{([^\}]+)\}/', + [$this, 'expandMatch'], + $this->template + ); + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + $result = array(); + + if (isset(self::$operatorHash[$expression[0]])) { + $result['operator'] = $expression[0]; + $expression = substr($expression, 1); + } else { + $result['operator'] = ''; + } + + foreach (explode(',', $expression) as $value) { + $value = trim($value); + $varspec = array(); + if ($colonPos = strpos($value, ':')) { + $varspec['value'] = substr($value, 0, $colonPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $colonPos + 1); + } elseif (substr($value, -1) == '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $result['values'][] = $varspec; + } + + return $result; + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = array('+' => '%20', '%7e' => '~'); + + $replacements = array(); + $parsed = self::parseExpression($matches[1]); + $prefix = self::$operatorHash[$parsed['operator']]['prefix']; + $joiner = self::$operatorHash[$parsed['operator']]['joiner']; + $useQuery = self::$operatorHash[$parsed['operator']]['query']; + + foreach ($parsed['values'] as $value) { + + if (!isset($this->variables[$value['value']])) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQuery = $useQuery; + $expanded = ''; + + if (is_array($variable)) { + + $isAssoc = $this->isAssoc($variable); + $kvp = array(); + foreach ($variable as $key => $var) { + + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] == '+' || + $parsed['operator'] == '#' + ) { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] == '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested + // structures. + $var = strtr( + http_build_query([$key => $var]), + $rfc1738to3986 + ); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQuery) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQuery = false; + } elseif ($value['modifier'] == '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode + // modifier with an associative array. + $actuallyUseQuery = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the + // explode modifier is not set, then the result must be + // a comma separated list of keys followed by their + // respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + + } else { + if ($value['modifier'] == ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQuery) { + if (!$expanded && $joiner != '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative. + * + * This makes the assumption that input arrays are sequences or hashes. + * This assumption is a tradeoff for accuracy in favor of speed, but it + * should work in almost every case where input is supplied for a URI + * template. + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return $array && array_keys($array)[0] !== 0; + } + + /** + * Removes percent encoding on reserved characters (used with + and # + * modifiers). + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Url.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Url.php new file mode 100755 index 000000000..637f60c2a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Url.php @@ -0,0 +1,595 @@ + 80, 'https' => 443, 'ftp' => 21]; + private static $pathPattern = '/[^a-zA-Z0-9\-\._~!\$&\'\(\)\*\+,;=%:@\/]+|%(?![A-Fa-f0-9]{2})/'; + private static $queryPattern = '/[^a-zA-Z0-9\-\._~!\$\'\(\)\*\+,;%:@\/\?=&]+|%(?![A-Fa-f0-9]{2})/'; + /** @var Query|string Query part of the URL */ + private $query; + + /** + * Factory method to create a new URL from a URL string + * + * @param string $url Full URL used to create a Url object + * + * @return Url + * @throws \InvalidArgumentException + */ + public static function fromString($url) + { + static $defaults = ['scheme' => null, 'host' => null, + 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null]; + + if (false === ($parts = parse_url($url))) { + throw new \InvalidArgumentException('Unable to parse malformed ' + . 'url: ' . $url); + } + + $parts += $defaults; + + // Convert the query string into a Query object + if ($parts['query'] || 0 !== strlen($parts['query'])) { + $parts['query'] = Query::fromString($parts['query']); + } + + return new static($parts['scheme'], $parts['host'], $parts['user'], + $parts['pass'], $parts['port'], $parts['path'], $parts['query'], + $parts['fragment']); + } + + /** + * Build a URL from parse_url parts. The generated URL will be a relative + * URL if a scheme or host are not provided. + * + * @param array $parts Array of parse_url parts + * + * @return string + */ + public static function buildUrl(array $parts) + { + $url = $scheme = ''; + + if (!empty($parts['scheme'])) { + $scheme = $parts['scheme']; + $url .= $scheme . ':'; + } + + if (!empty($parts['host'])) { + $url .= '//'; + if (isset($parts['user'])) { + $url .= $parts['user']; + if (isset($parts['pass'])) { + $url .= ':' . $parts['pass']; + } + $url .= '@'; + } + + $url .= $parts['host']; + + // Only include the port if it is not the default port of the scheme + if (isset($parts['port']) && + (!isset(self::$defaultPorts[$scheme]) || + $parts['port'] != self::$defaultPorts[$scheme]) + ) { + $url .= ':' . $parts['port']; + } + } + + // Add the path component if present + if (isset($parts['path']) && strlen($parts['path'])) { + // Always ensure that the path begins with '/' if set and something + // is before the path + if (!empty($parts['host']) && $parts['path'][0] != '/') { + $url .= '/'; + } + $url .= $parts['path']; + } + + // Add the query string if present + if (isset($parts['query'])) { + $queryStr = (string) $parts['query']; + if ($queryStr || $queryStr === '0') { + $url .= '?' . $queryStr; + } + } + + // Ensure that # is only added to the url if fragment contains anything. + if (isset($parts['fragment'])) { + $url .= '#' . $parts['fragment']; + } + + return $url; + } + + /** + * Create a new URL from URL parts + * + * @param string $scheme Scheme of the URL + * @param string $host Host of the URL + * @param string $username Username of the URL + * @param string $password Password of the URL + * @param int $port Port of the URL + * @param string $path Path of the URL + * @param Query|array|string $query Query string of the URL + * @param string $fragment Fragment of the URL + */ + public function __construct( + $scheme, + $host, + $username = null, + $password = null, + $port = null, + $path = null, + $query = null, + $fragment = null + ) { + $this->scheme = strtolower($scheme); + $this->host = $host; + $this->port = $port; + $this->username = $username; + $this->password = $password; + $this->fragment = $fragment; + + if ($query) { + $this->setQuery($query); + } + + $this->setPath($path); + } + + /** + * Clone the URL + */ + public function __clone() + { + if ($this->query instanceof Query) { + $this->query = clone $this->query; + } + } + + /** + * Returns the URL as a URL string + * + * @return string + */ + public function __toString() + { + return static::buildUrl($this->getParts()); + } + + /** + * Get the parts of the URL as an array + * + * @return array + */ + public function getParts() + { + return array( + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->path, + 'query' => $this->query, + 'fragment' => $this->fragment, + ); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) + * + * @return Url + */ + public function setHost($host) + { + if (strpos($host, ':') === false) { + $this->host = $host; + } else { + list($host, $port) = explode(':', $host); + $this->host = $host; + $this->setPort($port); + } + } + + /** + * Get the host part of the URL + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the scheme part of the URL (http, https, ftp, etc.) + * + * @param string $scheme Scheme to set + */ + public function setScheme($scheme) + { + // Remove the default port if one is specified + if ($this->port + && isset(self::$defaultPorts[$this->scheme]) + && self::$defaultPorts[$this->scheme] == $this->port + ) { + $this->port = null; + } + + $this->scheme = strtolower($scheme); + } + + /** + * Get the scheme part of the URL + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Set the port part of the URL + * + * @param int $port Port to set + */ + public function setPort($port) + { + $this->port = $port; + } + + /** + * Get the port part of the URl. + * + * If no port was set, this method will return the default port for the + * scheme of the URI. + * + * @return int|null + */ + public function getPort() + { + if ($this->port) { + return $this->port; + } elseif (isset(self::$defaultPorts[$this->scheme])) { + return self::$defaultPorts[$this->scheme]; + } + + return null; + } + + /** + * Set the path part of the URL. + * + * The provided URL is URL encoded as necessary. + * + * @param string $path Path string to set + */ + public function setPath($path) + { + $this->path = self::encodePath($path); + } + + /** + * Removes dot segments from a URL + * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 + */ + public function removeDotSegments() + { + static $noopPaths = ['' => true, '/' => true, '*' => true]; + static $ignoreSegments = ['.' => true, '..' => true]; + + if (isset($noopPaths[$this->path])) { + return; + } + + $results = []; + $segments = $this->getPathSegments(); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif (!isset($ignoreSegments[$segment])) { + $results[] = $segment; + } + } + + $newPath = implode('/', $results); + + // Add the leading slash if necessary + if (substr($this->path, 0, 1) === '/' && + substr($newPath, 0, 1) !== '/' + ) { + $newPath = '/' . $newPath; + } + + // Add the trailing slash if necessary + if ($newPath != '/' && isset($ignoreSegments[end($segments)])) { + $newPath .= '/'; + } + + $this->path = $newPath; + } + + /** + * Add a relative path to the currently set path. + * + * @param string $relativePath Relative path to add + */ + public function addPath($relativePath) + { + if ($relativePath != '/' && + is_string($relativePath) && + strlen($relativePath) > 0 + ) { + // Add a leading slash if needed + if ($relativePath[0] !== '/' && + substr($this->path, -1, 1) !== '/' + ) { + $relativePath = '/' . $relativePath; + } + + $this->setPath($this->path . $relativePath); + } + } + + /** + * Get the path part of the URL + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get the path segments of the URL as an array + * + * @return array + */ + public function getPathSegments() + { + return explode('/', $this->path); + } + + /** + * Set the password part of the URL + * + * @param string $password Password to set + */ + public function setPassword($password) + { + $this->password = $password; + } + + /** + * Get the password part of the URL + * + * @return null|string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the username part of the URL + * + * @param string $username Username to set + */ + public function setUsername($username) + { + $this->username = $username; + } + + /** + * Get the username part of the URl + * + * @return null|string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the query part of the URL as a Query object + * + * @return Query + */ + public function getQuery() + { + // Convert the query string to a query object if not already done. + if (!$this->query instanceof Query) { + $this->query = $this->query === null + ? new Query() + : Query::fromString($this->query); + } + + return $this->query; + } + + /** + * Set the query part of the URL. + * + * You may provide a query string as a string and pass $rawString as true + * to provide a query string that is not parsed until a call to getQuery() + * is made. Setting a raw query string will still encode invalid characters + * in a query string. + * + * @param Query|string|array $query Query string value to set. Can + * be a string that will be parsed into a Query object, an array + * of key value pairs, or a Query object. + * @param bool $rawString Set to true when providing a raw query string. + * + * @throws \InvalidArgumentException + */ + public function setQuery($query, $rawString = false) + { + if ($query instanceof Query) { + $this->query = $query; + } elseif (is_string($query)) { + if (!$rawString) { + $this->query = Query::fromString($query); + } else { + // Ensure the query does not have illegal characters. + $this->query = preg_replace_callback( + self::$queryPattern, + [__CLASS__, 'encodeMatch'], + $query + ); + } + + } elseif (is_array($query)) { + $this->query = new Query($query); + } else { + throw new \InvalidArgumentException('Query must be a Query, ' + . 'array, or string. Got ' . Core::describeType($query)); + } + } + + /** + * Get the fragment part of the URL + * + * @return null|string + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * Set the fragment part of the URL + * + * @param string $fragment Fragment to set + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + } + + /** + * Check if this is an absolute URL + * + * @return bool + */ + public function isAbsolute() + { + return $this->scheme && $this->host; + } + + /** + * Combine the URL with another URL and return a new URL instance. + * + * Follows the rules specific in RFC 3986 section 5.4. + * + * @param string $url Relative URL to combine with + * + * @return Url + * @throws \InvalidArgumentException + * @link http://tools.ietf.org/html/rfc3986#section-5.4 + */ + public function combine($url) + { + $url = static::fromString($url); + + // Use the more absolute URL as the base URL + if (!$this->isAbsolute() && $url->isAbsolute()) { + $url = $url->combine($this); + } + + $parts = $url->getParts(); + + // Passing a URL with a scheme overrides everything + if ($parts['scheme']) { + return clone $url; + } + + // Setting a host overrides the entire rest of the URL + if ($parts['host']) { + return new static( + $this->scheme, + $parts['host'], + $parts['user'], + $parts['pass'], + $parts['port'], + $parts['path'], + $parts['query'] instanceof Query + ? clone $parts['query'] + : $parts['query'], + $parts['fragment'] + ); + } + + if (!$parts['path'] && $parts['path'] !== '0') { + // The relative URL has no path, so check if it is just a query + $path = $this->path ?: ''; + $query = $parts['query'] ?: $this->query; + } else { + $query = $parts['query']; + if ($parts['path'][0] == '/' || !$this->path) { + // Overwrite the existing path if the rel path starts with "/" + $path = $parts['path']; + } else { + // If the relative URL does not have a path or the base URL + // path does not end in a "/" then overwrite the existing path + // up to the last "/" + $path = substr($this->path, 0, strrpos($this->path, '/') + 1) . $parts['path']; + } + } + + $result = new self( + $this->scheme, + $this->host, + $this->username, + $this->password, + $this->port, + $path, + $query instanceof Query ? clone $query : $query, + $parts['fragment'] + ); + + if ($path) { + $result->removeDotSegments(); + } + + return $result; + } + + /** + * Encodes the path part of a URL without double-encoding percent-encoded + * key value pairs. + * + * @param string $path Path to encode + * + * @return string + */ + public static function encodePath($path) + { + static $cb = [__CLASS__, 'encodeMatch']; + return preg_replace_callback(self::$pathPattern, $cb, $path); + } + + private static function encodeMatch(array $match) + { + return rawurlencode($match[0]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Utils.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Utils.php new file mode 100755 index 000000000..1c8966106 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/src/Utils.php @@ -0,0 +1,211 @@ +expand($template, $variables); + } + + /** + * Wrapper for JSON decode that implements error detection with helpful + * error messages. + * + * @param string $json JSON data to parse + * @param bool $assoc When true, returned objects will be converted + * into associative arrays. + * @param int $depth User specified recursion depth. + * @param int $options Bitmask of JSON decode options. + * + * @return mixed + * @throws \InvalidArgumentException if the JSON cannot be parsed. + * @link http://www.php.net/manual/en/function.json-decode.php + */ + public static function jsonDecode($json, $assoc = false, $depth = 512, $options = 0) + { + static $jsonErrors = [ + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' + ]; + + $data = \json_decode($json, $assoc, $depth, $options); + + if (JSON_ERROR_NONE !== json_last_error()) { + $last = json_last_error(); + throw new \InvalidArgumentException( + 'Unable to parse JSON data: ' + . (isset($jsonErrors[$last]) + ? $jsonErrors[$last] + : 'Unknown error') + ); + } + + return $data; + } + + /** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ + public static function getDefaultUserAgent() + { + static $defaultAgent = ''; + if (!$defaultAgent) { + $defaultAgent = 'Guzzle/' . ClientInterface::VERSION; + if (extension_loaded('curl')) { + $defaultAgent .= ' curl/' . curl_version()['version']; + } + $defaultAgent .= ' PHP/' . PHP_VERSION; + } + + return $defaultAgent; + } + + /** + * Create a default handler to use based on the environment + * + * @throws \RuntimeException if no viable Handler is available. + */ + public static function getDefaultHandler() + { + $default = $future = null; + + if (extension_loaded('curl')) { + $config = [ + 'select_timeout' => getenv('GUZZLE_CURL_SELECT_TIMEOUT') ?: 1 + ]; + if ($maxHandles = getenv('GUZZLE_CURL_MAX_HANDLES')) { + $config['max_handles'] = $maxHandles; + } + if (function_exists('curl_reset')) { + $default = new CurlHandler(); + $future = new CurlMultiHandler($config); + } else { + $default = new CurlMultiHandler($config); + } + } + + if (ini_get('allow_url_fopen')) { + $default = !$default + ? new StreamHandler() + : Middleware::wrapStreaming($default, new StreamHandler()); + } elseif (!$default) { + throw new \RuntimeException('Guzzle requires cURL, the ' + . 'allow_url_fopen ini setting, or a custom HTTP handler.'); + } + + return $future ? Middleware::wrapFuture($default, $future) : $default; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php new file mode 100755 index 000000000..080d44c01 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php @@ -0,0 +1,58 @@ +assertCount(3, $batch); + $this->assertEquals([$a, $b, $c], $batch->getKeys()); + $this->assertEquals([$hash[$c]], $batch->getFailures()); + $this->assertEquals(['1', '2'], $batch->getSuccessful()); + $this->assertEquals('1', $batch->getResult($a)); + $this->assertNull($batch->getResult(new \stdClass())); + $this->assertTrue(isset($batch[0])); + $this->assertFalse(isset($batch[10])); + $this->assertEquals('1', $batch[0]); + $this->assertEquals('2', $batch[1]); + $this->assertNull($batch[100]); + $this->assertInstanceOf('Exception', $batch[2]); + + $results = iterator_to_array($batch); + $this->assertEquals(['1', '2', $hash[$c]], $results); + } + + /** + * @expectedException \RuntimeException + */ + public function testCannotSetByIndex() + { + $hash = new \SplObjectStorage(); + $batch = new BatchResults($hash); + $batch[10] = 'foo'; + } + + /** + * @expectedException \RuntimeException + */ + public function testCannotUnsetByIndex() + { + $hash = new \SplObjectStorage(); + $batch = new BatchResults($hash); + unset($batch[10]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/ClientTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/ClientTest.php new file mode 100755 index 000000000..02db3eb9c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/ClientTest.php @@ -0,0 +1,647 @@ +ma = function () { + throw new \RuntimeException('Should not have been called.'); + }; + } + + public function testUsesDefaultDefaultOptions() + { + $client = new Client(); + $this->assertTrue($client->getDefaultOption('allow_redirects')); + $this->assertTrue($client->getDefaultOption('exceptions')); + $this->assertTrue($client->getDefaultOption('verify')); + } + + public function testUsesProvidedDefaultOptions() + { + $client = new Client([ + 'defaults' => [ + 'allow_redirects' => false, + 'query' => ['foo' => 'bar'] + ] + ]); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + $this->assertTrue($client->getDefaultOption('exceptions')); + $this->assertTrue($client->getDefaultOption('verify')); + $this->assertEquals(['foo' => 'bar'], $client->getDefaultOption('query')); + } + + public function testCanSpecifyBaseUrl() + { + $this->assertSame('', (new Client())->getBaseUrl()); + $this->assertEquals('http://foo', (new Client([ + 'base_url' => 'http://foo' + ]))->getBaseUrl()); + } + + public function testCanSpecifyBaseUrlUriTemplate() + { + $client = new Client(['base_url' => ['http://foo.com/{var}/', ['var' => 'baz']]]); + $this->assertEquals('http://foo.com/baz/', $client->getBaseUrl()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesUriTemplateValue() + { + new Client(['base_url' => ['http://foo.com/']]); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyHandler() + { + $client = new Client(['handler' => function () { + throw new \Exception('Foo'); + }]); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyHandlerAsAdapter() + { + $client = new Client(['adapter' => function () { + throw new \Exception('Foo'); + }]); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyMessageFactory() + { + $factory = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') + ->setMethods(['createRequest']) + ->getMockForAbstractClass(); + $factory->expects($this->once()) + ->method('createRequest') + ->will($this->throwException(new \Exception('Foo'))); + $client = new Client(['message_factory' => $factory]); + $client->get(); + } + + public function testCanSpecifyEmitter() + { + $emitter = $this->getMockBuilder('GuzzleHttp\Event\EmitterInterface') + ->setMethods(['listeners']) + ->getMockForAbstractClass(); + $emitter->expects($this->once()) + ->method('listeners') + ->will($this->returnValue('foo')); + + $client = new Client(['emitter' => $emitter]); + $this->assertEquals('foo', $client->getEmitter()->listeners()); + } + + public function testAddsDefaultUserAgentHeaderWithDefaultOptions() + { + $client = new Client(['defaults' => ['allow_redirects' => false]]); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + $this->assertEquals( + ['User-Agent' => Utils::getDefaultUserAgent()], + $client->getDefaultOption('headers') + ); + } + + public function testAddsDefaultUserAgentHeaderWithoutDefaultOptions() + { + $client = new Client(); + $this->assertEquals( + ['User-Agent' => Utils::getDefaultUserAgent()], + $client->getDefaultOption('headers') + ); + } + + private function getRequestClient() + { + $client = $this->getMockBuilder('GuzzleHttp\Client') + ->setMethods(['send']) + ->getMock(); + $client->expects($this->once()) + ->method('send') + ->will($this->returnArgument(0)); + + return $client; + } + + public function requestMethodProvider() + { + return [ + ['GET', false], + ['HEAD', false], + ['DELETE', false], + ['OPTIONS', false], + ['POST', 'foo'], + ['PUT', 'foo'], + ['PATCH', 'foo'] + ]; + } + + /** + * @dataProvider requestMethodProvider + */ + public function testClientProvidesMethodShortcut($method, $body) + { + $client = $this->getRequestClient(); + if ($body) { + $request = $client->{$method}('http://foo.com', [ + 'headers' => ['X-Baz' => 'Bar'], + 'body' => $body, + 'query' => ['a' => 'b'] + ]); + } else { + $request = $client->{$method}('http://foo.com', [ + 'headers' => ['X-Baz' => 'Bar'], + 'query' => ['a' => 'b'] + ]); + } + $this->assertEquals($method, $request->getMethod()); + $this->assertEquals('Bar', $request->getHeader('X-Baz')); + $this->assertEquals('a=b', $request->getQuery()); + if ($body) { + $this->assertEquals($body, $request->getBody()); + } + } + + public function testClientMergesDefaultOptionsWithRequestOptions() + { + $f = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') + ->setMethods(array('createRequest')) + ->getMockForAbstractClass(); + + $o = null; + // Intercept the creation + $f->expects($this->once()) + ->method('createRequest') + ->will($this->returnCallback( + function ($method, $url, array $options = []) use (&$o) { + $o = $options; + return (new MessageFactory())->createRequest($method, $url, $options); + } + )); + + $client = new Client([ + 'message_factory' => $f, + 'defaults' => [ + 'headers' => ['Foo' => 'Bar'], + 'query' => ['baz' => 'bam'], + 'exceptions' => false + ] + ]); + + $request = $client->createRequest('GET', 'http://foo.com?a=b', [ + 'headers' => ['Hi' => 'there', '1' => 'one'], + 'allow_redirects' => false, + 'query' => ['t' => 1] + ]); + + $this->assertFalse($o['allow_redirects']); + $this->assertFalse($o['exceptions']); + $this->assertEquals('Bar', $request->getHeader('Foo')); + $this->assertEquals('there', $request->getHeader('Hi')); + $this->assertEquals('one', $request->getHeader('1')); + $this->assertEquals('a=b&baz=bam&t=1', $request->getQuery()); + } + + public function testClientMergesDefaultHeadersCaseInsensitively() + { + $client = new Client(['defaults' => ['headers' => ['Foo' => 'Bar']]]); + $request = $client->createRequest('GET', 'http://foo.com?a=b', [ + 'headers' => ['foo' => 'custom', 'user-agent' => 'test'] + ]); + $this->assertEquals('test', $request->getHeader('User-Agent')); + $this->assertEquals('custom', $request->getHeader('Foo')); + } + + public function testCanOverrideDefaultOptionWithNull() + { + $client = new Client(['defaults' => ['proxy' => 'invalid!']]); + $request = $client->createRequest('GET', 'http://foo.com?a=b', [ + 'proxy' => null + ]); + $this->assertFalse($request->getConfig()->hasKey('proxy')); + } + + public function testDoesNotOverwriteExistingUA() + { + $client = new Client(['defaults' => [ + 'headers' => ['User-Agent' => 'test'] + ]]); + $this->assertEquals( + ['User-Agent' => 'test'], + $client->getDefaultOption('headers') + ); + } + + public function testUsesBaseUrlWhenNoUrlIsSet() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/baz?bam=bar', + $client->createRequest('GET')->getUrl() + ); + } + + public function testUsesBaseUrlCombinedWithProvidedUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/bar/bam', + $client->createRequest('GET', 'bar/bam')->getUrl() + ); + } + + public function testFalsyPathsAreCombinedWithBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/0', + $client->createRequest('GET', '0')->getUrl() + ); + } + + public function testUsesBaseUrlCombinedWithProvidedUrlViaUriTemplate() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/bar/123', + $client->createRequest('GET', ['bar/{bam}', ['bam' => '123']])->getUrl() + ); + } + + public function testSettingAbsoluteUrlOverridesBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/foo', + $client->createRequest('GET', '/foo')->getUrl() + ); + } + + public function testSettingAbsoluteUriTemplateOverridesBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://goo.com/1', + $client->createRequest( + 'GET', + ['http://goo.com/{bar}', ['bar' => '1']] + )->getUrl() + ); + } + + public function testCanSetRelativeUrlStartingWithHttp() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $this->assertEquals( + 'http://www.foo.com/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } + + /** + * Test that base URLs ending with a slash are resolved as per RFC3986. + * + * @link http://tools.ietf.org/html/rfc3986#section-5.2.3 + */ + public function testMultipleSubdirectoryWithSlash() + { + $client = new Client(['base_url' => 'http://www.foo.com/bar/bam/']); + $this->assertEquals( + 'http://www.foo.com/bar/bam/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } + + /** + * Test that base URLs ending without a slash are resolved as per RFC3986. + * + * @link http://tools.ietf.org/html/rfc3986#section-5.2.3 + */ + public function testMultipleSubdirectoryNoSlash() + { + $client = new Client(['base_url' => 'http://www.foo.com/bar/bam']); + $this->assertEquals( + 'http://www.foo.com/bar/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } + + public function testClientSendsRequests() + { + $mock = new MockHandler(['status' => 200, 'headers' => []]); + $client = new Client(['handler' => $mock]); + $response = $client->get('http://test.com'); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('http://test.com', $response->getEffectiveUrl()); + } + + public function testSendingRequestCanBeIntercepted() + { + $response = new Response(200); + $client = new Client(['handler' => $this->ma]); + $client->getEmitter()->on( + 'before', + function (BeforeEvent $e) use ($response) { + $e->intercept($response); + } + ); + $this->assertSame($response, $client->get('http://test.com')); + $this->assertEquals('http://test.com', $response->getEffectiveUrl()); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage Argument 1 passed to GuzzleHttp\Message\FutureResponse::proxy() must implement interface GuzzleHttp\Ring\Future\FutureInterface + */ + public function testEnsuresResponseIsPresentAfterSending() + { + $handler = function () {}; + $client = new Client(['handler' => $handler]); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage Waiting did not resolve future + */ + public function testEnsuresResponseIsPresentAfterDereferencing() + { + $deferred = new Deferred(); + $handler = new MockHandler(function () use ($deferred) { + return new FutureArray( + $deferred->promise(), + function () {} + ); + }); + $client = new Client(['handler' => $handler]); + $response = $client->get('http://httpbin.org'); + $response->wait(); + } + + public function testClientHandlesErrorsDuringBeforeSend() + { + $client = new Client(); + $client->getEmitter()->on('before', function ($e) { + throw new \Exception('foo'); + }); + $client->getEmitter()->on('error', function (ErrorEvent $e) { + $e->intercept(new Response(200)); + }); + $this->assertEquals( + 200, + $client->get('http://test.com')->getStatusCode() + ); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage foo + */ + public function testClientHandlesErrorsDuringBeforeSendAndThrowsIfUnhandled() + { + $client = new Client(); + $client->getEmitter()->on('before', function (BeforeEvent $e) { + throw new RequestException('foo', $e->getRequest()); + }); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage foo + */ + public function testClientWrapsExceptions() + { + $client = new Client(); + $client->getEmitter()->on('before', function (BeforeEvent $e) { + throw new \Exception('foo'); + }); + $client->get('http://httpbin.org'); + } + + public function testCanInjectResponseForFutureError() + { + $calledFuture = false; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred, &$calledFuture) { + $calledFuture = true; + $deferred->resolve(['error' => new \Exception('Noo!')]); + } + ); + $mock = new MockHandler($future); + $client = new Client(['handler' => $mock]); + $called = 0; + $response = $client->get('http://localhost:123/foo', [ + 'future' => true, + 'events' => [ + 'error' => function (ErrorEvent $e) use (&$called) { + $called++; + $e->intercept(new Response(200)); + } + ] + ]); + $this->assertEquals(0, $called); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $response); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertTrue($calledFuture); + $this->assertEquals(1, $called); + } + + public function testCanReturnFutureResults() + { + $called = false; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred, &$called) { + $called = true; + $deferred->resolve(['status' => 201, 'headers' => []]); + } + ); + $mock = new MockHandler($future); + $client = new Client(['handler' => $mock]); + $response = $client->get('http://localhost:123/foo', ['future' => true]); + $this->assertFalse($called); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $response); + $this->assertEquals(201, $response->getStatusCode()); + $this->assertTrue($called); + } + + public function testThrowsExceptionsWhenDereferenced() + { + $calledFuture = false; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred, &$calledFuture) { + $calledFuture = true; + $deferred->resolve(['error' => new \Exception('Noop!')]); + } + ); + $client = new Client(['handler' => new MockHandler($future)]); + try { + $res = $client->get('http://localhost:123/foo', ['future' => true]); + $res->wait(); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertEquals(1, $calledFuture); + } + } + + /** + * @expectedExceptionMessage Noo! + * @expectedException \GuzzleHttp\Exception\RequestException + */ + public function testThrowsExceptionsSynchronously() + { + $client = new Client([ + 'handler' => new MockHandler(['error' => new \Exception('Noo!')]) + ]); + $client->get('http://localhost:123/foo'); + } + + public function testCanSetDefaultValues() + { + $client = new Client(['foo' => 'bar']); + $client->setDefaultOption('headers/foo', 'bar'); + $this->assertNull($client->getDefaultOption('foo')); + $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); + } + + public function testSendsAllInParallel() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(200), + new Response(201), + new Response(202), + ])); + $history = new History(); + $client->getEmitter()->attach($history); + + $requests = [ + $client->createRequest('GET', 'http://test.com'), + $client->createRequest('POST', 'http://test.com'), + $client->createRequest('PUT', 'http://test.com') + ]; + + $client->sendAll($requests); + $requests = array_map(function($r) { + return $r->getMethod(); + }, $history->getRequests()); + $this->assertContains('GET', $requests); + $this->assertContains('POST', $requests); + $this->assertContains('PUT', $requests); + } + + public function testCanDisableAuthPerRequest() + { + $client = new Client(['defaults' => ['auth' => 'foo']]); + $request = $client->createRequest('GET', 'http://test.com'); + $this->assertEquals('foo', $request->getConfig()['auth']); + $request = $client->createRequest('GET', 'http://test.com', ['auth' => null]); + $this->assertFalse($request->getConfig()->hasKey('auth')); + } + + public function testUsesProxyEnvironmentVariables() + { + $http = getenv('HTTP_PROXY'); + $https = getenv('HTTPS_PROXY'); + + $client = new Client(); + $this->assertNull($client->getDefaultOption('proxy')); + + putenv('HTTP_PROXY=127.0.0.1'); + $client = new Client(); + $this->assertEquals( + ['http' => '127.0.0.1'], + $client->getDefaultOption('proxy') + ); + + putenv('HTTPS_PROXY=127.0.0.2'); + $client = new Client(); + $this->assertEquals( + ['http' => '127.0.0.1', 'https' => '127.0.0.2'], + $client->getDefaultOption('proxy') + ); + + putenv("HTTP_PROXY=$http"); + putenv("HTTPS_PROXY=$https"); + } + + public function testReturnsFutureForErrorWhenRequested() + { + $client = new Client(['handler' => new MockHandler(['status' => 404])]); + $request = $client->createRequest('GET', 'http://localhost:123/foo', [ + 'future' => true + ]); + $res = $client->send($request); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res); + try { + $res->wait(); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertContains('404', $e->getMessage()); + } + } + + public function testReturnsFutureForResponseWhenRequested() + { + $client = new Client(['handler' => new MockHandler(['status' => 200])]); + $request = $client->createRequest('GET', 'http://localhost:123/foo', [ + 'future' => true + ]); + $res = $client->send($request); + $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res); + $this->assertEquals(200, $res->getStatusCode()); + } + + public function testCanUseUrlWithCustomQuery() + { + $client = new Client(); + $url = Url::fromString('http://foo.com/bar'); + $query = new Query(['baz' => '123%20']); + $query->setEncodingType(false); + $url->setQuery($query); + $r = $client->createRequest('GET', $url); + $this->assertEquals('http://foo.com/bar?baz=123%20', $r->getUrl()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/CollectionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/CollectionTest.php new file mode 100755 index 000000000..8c532aaf5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/CollectionTest.php @@ -0,0 +1,416 @@ +coll = new Collection(); + } + + public function testConstructorCanBeCalledWithNoParams() + { + $this->coll = new Collection(); + $p = $this->coll->toArray(); + $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); + } + + public function testConstructorCanBeCalledWithParams() + { + $testData = array( + 'test' => 'value', + 'test_2' => 'value2' + ); + $this->coll = new Collection($testData); + $this->assertEquals($this->coll->toArray(), $testData); + $this->assertEquals($this->coll->toArray(), $this->coll->toArray()); + } + + public function testImplementsIteratorAggregate() + { + $this->coll->set('key', 'value'); + $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); + $this->assertEquals(1, count($this->coll)); + $total = 0; + foreach ($this->coll as $key => $value) { + $this->assertEquals('key', $key); + $this->assertEquals('value', $value); + $total++; + } + $this->assertEquals(1, $total); + } + + public function testCanAddValuesToExistingKeysByUsingArray() + { + $this->coll->add('test', 'value1'); + $this->assertEquals($this->coll->toArray(), array('test' => 'value1')); + $this->coll->add('test', 'value2'); + $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2'))); + $this->coll->add('test', 'value3'); + $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2', 'value3'))); + } + + public function testHandlesMergingInDisparateDataSources() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + $this->coll->merge($params); + $this->assertEquals($this->coll->toArray(), $params); + $this->coll->merge(new Collection(['test4' => 'hi'])); + $this->assertEquals( + $this->coll->toArray(), + $params + ['test4' => 'hi'] + ); + } + + public function testCanClearAllDataOrSpecificKeys() + { + $this->coll->merge(array( + 'test' => 'value1', + 'test2' => 'value2' + )); + + // Clear a specific parameter by name + $this->coll->remove('test'); + + $this->assertEquals($this->coll->toArray(), array( + 'test2' => 'value2' + )); + + // Clear all parameters + $this->coll->clear(); + + $this->assertEquals($this->coll->toArray(), array()); + } + + public function testProvidesKeys() + { + $this->assertEquals(array(), $this->coll->getKeys()); + $this->coll->merge(array( + 'test1' => 'value1', + 'test2' => 'value2' + )); + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + // Returns the cached array previously returned + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + $this->coll->remove('test1'); + $this->assertEquals(array('test2'), $this->coll->getKeys()); + $this->coll->add('test3', 'value3'); + $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); + } + + public function testChecksIfHasKey() + { + $this->assertFalse($this->coll->hasKey('test')); + $this->coll->add('test', 'value'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->coll->add('test2', 'value2'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->assertEquals(true, $this->coll->hasKey('test2')); + $this->assertFalse($this->coll->hasKey('testing')); + $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); + } + + public function testChecksIfHasValue() + { + $this->assertFalse($this->coll->hasValue('value')); + $this->coll->add('test', 'value'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->coll->add('test2', 'value2'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->assertEquals('test2', $this->coll->hasValue('value2')); + $this->assertFalse($this->coll->hasValue('val')); + } + + public function testImplementsCount() + { + $data = new Collection(); + $this->assertEquals(0, $data->count()); + $data->add('key', 'value'); + $this->assertEquals(1, count($data)); + $data->add('key', 'value2'); + $this->assertEquals(1, count($data)); + $data->add('key_2', 'value3'); + $this->assertEquals(2, count($data)); + } + + public function testAddParamsByMerging() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + + // Add some parameters + $this->coll->merge($params); + + // Add more parameters by merging them in + $this->coll->merge(array( + 'test' => 'another', + 'different_key' => 'new value' + )); + + $this->assertEquals(array( + 'test' => array('value1', 'another'), + 'test2' => 'value2', + 'test3' => array('value3', 'value4'), + 'different_key' => 'new value' + ), $this->coll->toArray()); + } + + public function testAllowsFunctionalFilter() + { + $this->coll->merge(array( + 'fruit' => 'apple', + 'number' => 'ten', + 'prepositions' => array('about', 'above', 'across', 'after'), + 'same_number' => 'ten' + )); + + $filtered = $this->coll->filter(function ($key, $value) { + return $value == 'ten'; + }); + + $this->assertNotSame($filtered, $this->coll); + + $this->assertEquals(array( + 'number' => 'ten', + 'same_number' => 'ten' + ), $filtered->toArray()); + } + + public function testAllowsFunctionalMapping() + { + $this->coll->merge(array( + 'number_1' => 1, + 'number_2' => 2, + 'number_3' => 3 + )); + + $mapped = $this->coll->map(function ($key, $value) { + return $value * $value; + }); + + $this->assertNotSame($mapped, $this->coll); + + $this->assertEquals(array( + 'number_1' => 1, + 'number_2' => 4, + 'number_3' => 9 + ), $mapped->toArray()); + } + + public function testImplementsArrayAccess() + { + $this->coll->merge(array( + 'k1' => 'v1', + 'k2' => 'v2' + )); + + $this->assertTrue($this->coll->offsetExists('k1')); + $this->assertFalse($this->coll->offsetExists('Krull')); + + $this->coll->offsetSet('k3', 'v3'); + $this->assertEquals('v3', $this->coll->offsetGet('k3')); + $this->assertEquals('v3', $this->coll->get('k3')); + + $this->coll->offsetUnset('k1'); + $this->assertFalse($this->coll->offsetExists('k1')); + } + + public function testCanReplaceAllData() + { + $this->coll->replace(array('a' => '123')); + $this->assertEquals(array('a' => '123'), $this->coll->toArray()); + } + + public function testPreparesFromConfig() + { + $c = Collection::fromConfig(array( + 'a' => '123', + 'base_url' => 'http://www.test.com/' + ), array( + 'a' => 'xyz', + 'b' => 'lol' + ), array('a')); + + $this->assertInstanceOf('GuzzleHttp\Collection', $c); + $this->assertEquals(array( + 'a' => '123', + 'b' => 'lol', + 'base_url' => 'http://www.test.com/' + ), $c->toArray()); + + try { + Collection::fromConfig(array(), array(), array('a')); + $this->fail('Exception not throw when missing config'); + } catch (\InvalidArgumentException $e) { + } + } + + function falseyDataProvider() + { + return array( + array(false, false), + array(null, null), + array('', ''), + array(array(), array()), + array(0, 0), + ); + } + + /** + * @dataProvider falseyDataProvider + */ + public function testReturnsCorrectData($a, $b) + { + $c = new Collection(array('value' => $a)); + $this->assertSame($b, $c->get('value')); + } + + public function testRetrievesNestedKeysUsingPath() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar' + ) + ) + ); + $collection = new Collection($data); + $this->assertEquals('bar', $collection->getPath('foo')); + $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); + $this->assertNull($collection->getPath('wewewf')); + $this->assertNull($collection->getPath('baz/mesa/jar/jar')); + } + + public function testFalseyKeysStillDescend() + { + $collection = new Collection(array( + '0' => array( + 'a' => 'jar' + ), + 1 => 'other' + )); + $this->assertEquals('jar', $collection->getPath('0/a')); + $this->assertEquals('other', $collection->getPath('1')); + } + + public function getPathProvider() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c') + ), + 'bar' => array( + 'baz' => 'bam', + 'array' => array('d', 'e', 'f') + ) + ), + 'bam' => array( + array('foo' => 1), + array('foo' => 2), + array('array' => array('h', 'i')) + ) + ); + $c = new Collection($data); + + return array( + // Simple path selectors + array($c, 'foo', 'bar'), + array($c, 'baz', $data['baz']), + array($c, 'bam', $data['bam']), + array($c, 'baz/mesa', $data['baz']['mesa']), + array($c, 'baz/mesa/jar', 'jar'), + // Does not barf on missing keys + array($c, 'fefwfw', null), + array($c, 'baz/mesa/array', $data['baz']['mesa']['array']) + ); + } + + /** + * @dataProvider getPathProvider + */ + public function testGetPath(Collection $c, $path, $expected, $separator = '/') + { + $this->assertEquals($expected, $c->getPath($path, $separator)); + } + + public function testOverridesSettings() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $c->overwriteWith(array('foo' => 10, 'bar' => 300)); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testOverwriteWithCollection() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testOverwriteWithTraversable() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b->getIterator()); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testCanSetNestedPathValueThatDoesNotExist() + { + $c = new Collection(array()); + $c->setPath('foo/bar/baz/123', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); + } + + public function testCanSetNestedPathValueThatExists() + { + $c = new Collection(array('foo' => array('bar' => 'test'))); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \RuntimeException + */ + public function testVerifiesNestedPathIsValidAtExactLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \RuntimeException + */ + public function testVerifiesThatNestedPathIsValidAtAnyLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar/baz', 'test'); + } + + public function testCanAppendToNestedPathValues() + { + $c = new Collection(); + $c->setPath('foo/bar/[]', 'a'); + $c->setPath('foo/bar/[]', 'b'); + $this->assertEquals(['a', 'b'], $c['foo']['bar']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php new file mode 100755 index 000000000..1360419d9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php @@ -0,0 +1,339 @@ +jar = new CookieJar(); + } + + protected function getTestCookies() + { + return [ + new SetCookie(['Name' => 'foo', 'Value' => 'bar', 'Domain' => 'foo.com', 'Path' => '/', 'Discard' => true]), + new SetCookie(['Name' => 'test', 'Value' => '123', 'Domain' => 'baz.com', 'Path' => '/foo', 'Expires' => 2]), + new SetCookie(['Name' => 'you', 'Value' => '123', 'Domain' => 'bar.com', 'Path' => '/boo', 'Expires' => time() + 1000]) + ]; + } + + public function testQuotesBadCookieValues() + { + $this->assertEquals('foo', CookieJar::getCookieValue('foo')); + $this->assertEquals('"foo,bar"', CookieJar::getCookieValue('foo,bar')); + } + + public function testCreatesFromArray() + { + $jar = CookieJar::fromArray([ + 'foo' => 'bar', + 'baz' => 'bam' + ], 'example.com'); + $this->assertCount(2, $jar); + } + + /** + * Provides test data for cookie cookieJar retrieval + */ + public function getCookiesDataProvider() + { + return [ + [['foo', 'baz', 'test', 'muppet', 'googoo'], '', '', '', false], + [['foo', 'baz', 'muppet', 'googoo'], '', '', '', true], + [['googoo'], 'www.example.com', '', '', false], + [['muppet', 'googoo'], 'test.y.example.com', '', '', false], + [['foo', 'baz'], 'example.com', '', '', false], + [['muppet'], 'x.y.example.com', '/acme/', '', false], + [['muppet'], 'x.y.example.com', '/acme/test/', '', false], + [['googoo'], 'x.y.example.com', '/test/acme/test/', '', false], + [['foo', 'baz'], 'example.com', '', '', false], + [['baz'], 'example.com', '', 'baz', false], + ]; + } + + public function testStoresAndRetrievesCookies() + { + $cookies = $this->getTestCookies(); + foreach ($cookies as $cookie) { + $this->assertTrue($this->jar->setCookie($cookie)); + } + + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(3, count($this->jar->getIterator())); + $this->assertEquals($cookies, $this->jar->getIterator()->getArrayCopy()); + } + + public function testRemovesTemporaryCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->setCookie($cookie); + } + $this->jar->clearSessionCookies(); + $this->assertEquals( + [$cookies[1], $cookies[2]], + $this->jar->getIterator()->getArrayCopy() + ); + } + + public function testRemovesSelectively() + { + foreach ($this->getTestCookies() as $cookie) { + $this->jar->setCookie($cookie); + } + + // Remove foo.com cookies + $this->jar->clear('foo.com'); + $this->assertEquals(2, count($this->jar)); + // Try again, removing no further cookies + $this->jar->clear('foo.com'); + $this->assertEquals(2, count($this->jar)); + + // Remove bar.com cookies with path of /boo + $this->jar->clear('bar.com', '/boo'); + $this->assertEquals(1, count($this->jar)); + + // Remove cookie by name + $this->jar->clear(null, null, 'test'); + $this->assertEquals(0, count($this->jar)); + } + + public function testDoesNotAddIncompleteCookies() + { + $this->assertEquals(false, $this->jar->setCookie(new SetCookie())); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo' + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => false + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => true + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com' + )))); + } + + public function testDoesAddValidCookies() + { + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => 0 + )))); + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => 0.0 + )))); + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => '0' + )))); + } + + public function testOverwritesCookiesThatAreOlderOrDiscardable() + { + $t = time() + 1000; + $data = array( + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => '.example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true, + 'Discard' => true, + 'Expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $data['Discard'] = false; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertEquals(false, $c[0]->getDiscard()); + + // Make sure it doesn't duplicate the cookie + $this->jar->setCookie(new SetCookie($data)); + $this->assertEquals(1, count($this->jar)); + + // Make sure the more future-ful expiration date supersede the other + $data['Expires'] = time() + 2000; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertNotEquals($t, $c[0]->getExpires()); + } + + public function testOverwritesCookiesThatHaveChanged() + { + $t = time() + 1000; + $data = array( + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => '.example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true, + 'Discard' => true, + 'Expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + + $data['Value'] = 'boo'; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + // Changing the value plus a parameter also must overwrite the existing one + $data['Value'] = 'zoo'; + $data['Secure'] = false; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertEquals('zoo', $c[0]->getValue()); + } + + public function testAddsCookiesFromResponseWithRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" + )); + $request = new Request('GET', 'http://www.example.com'); + $this->jar->extractCookies($request, $response); + $this->assertEquals(1, count($this->jar)); + } + + public function getMatchingCookiesDataProvider() + { + return array( + array('https://example.com', 'foo=bar; baz=foobar'), + array('http://example.com', ''), + array('https://example.com:8912', 'foo=bar; baz=foobar'), + array('https://foo.example.com', 'foo=bar; baz=foobar'), + array('http://foo.example.com/test/acme/', 'googoo=gaga') + ); + } + + /** + * @dataProvider getMatchingCookiesDataProvider + */ + public function testReturnsCookiesMatchingRequests($url, $cookies) + { + $bag = [ + new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true + ]), + new SetCookie([ + 'Name' => 'baz', + 'Value' => 'foobar', + 'Domain' => 'example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true + ]), + new SetCookie([ + 'Name' => 'test', + 'Value' => '123', + 'Domain' => 'www.foobar.com', + 'Path' => '/path/', + 'Discard' => true + ]), + new SetCookie([ + 'Name' => 'muppet', + 'Value' => 'cookie_monster', + 'Domain' => '.y.example.com', + 'Path' => '/acme/', + 'Expires' => time() + 86400 + ]), + new SetCookie([ + 'Name' => 'googoo', + 'Value' => 'gaga', + 'Domain' => '.example.com', + 'Path' => '/test/acme/', + 'Max-Age' => 1500 + ]) + ]; + + foreach ($bag as $cookie) { + $this->jar->setCookie($cookie); + } + + $request = new Request('GET', $url); + $this->jar->addCookieHeader($request); + $this->assertEquals($cookies, $request->getHeader('Cookie')); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Invalid cookie: Cookie name must not cannot invalid characters: + */ + public function testThrowsExceptionWithStrictMode() + { + $a = new CookieJar(true); + $a->setCookie(new SetCookie(['Name' => "abc\n", 'Value' => 'foo', 'Domain' => 'bar'])); + } + + public function testDeletesCookiesByName() + { + $cookies = $this->getTestCookies(); + $cookies[] = new SetCookie([ + 'Name' => 'other', + 'Value' => '123', + 'Domain' => 'bar.com', + 'Path' => '/boo', + 'Expires' => time() + 1000 + ]); + $jar = new CookieJar(); + foreach ($cookies as $cookie) { + $jar->setCookie($cookie); + } + $this->assertCount(4, $jar); + $jar->clear('bar.com', '/boo', 'other'); + $this->assertCount(3, $jar); + $names = array_map(function (SetCookie $c) { + return $c->getName(); + }, $jar->getIterator()->getArrayCopy()); + $this->assertEquals(['foo', 'test', 'you'], $names); + } + + public function testCanConvertToAndLoadFromArray() + { + $jar = new CookieJar(true); + foreach ($this->getTestCookies() as $cookie) { + $jar->setCookie($cookie); + } + $this->assertCount(3, $jar); + $arr = $jar->toArray(); + $this->assertCount(3, $arr); + $newCookieJar = new CookieJar(false, $arr); + $this->assertCount(3, $newCookieJar); + $this->assertSame($jar->toArray(), $newCookieJar->toArray()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php new file mode 100755 index 000000000..1d1133715 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php @@ -0,0 +1,71 @@ +file = tempnam('/tmp', 'file-cookies'); + } + + /** + * @expectedException \RuntimeException + */ + public function testValidatesCookieFile() + { + file_put_contents($this->file, 'true'); + new FileCookieJar($this->file); + } + + public function testLoadsFromFileFile() + { + $jar = new FileCookieJar($this->file); + $this->assertEquals([], $jar->getIterator()->getArrayCopy()); + unlink($this->file); + } + + public function testPersistsToFileFile() + { + $jar = new FileCookieJar($this->file); + $jar->setCookie(new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'baz', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'boo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + ])); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the file + $contents = file_get_contents($this->file); + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new FileCookieJar($this->file); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unlink($this->file); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php new file mode 100755 index 000000000..ccc6d4eeb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php @@ -0,0 +1,76 @@ +sessionVar = 'sessionKey'; + + if (!isset($_SESSION)) { + $_SESSION = array(); + } + } + + /** + * @expectedException \RuntimeException + */ + public function testValidatesCookieSession() + { + $_SESSION[$this->sessionVar] = 'true'; + new SessionCookieJar($this->sessionVar); + } + + public function testLoadsFromSession() + { + $jar = new SessionCookieJar($this->sessionVar); + $this->assertEquals([], $jar->getIterator()->getArrayCopy()); + unset($_SESSION[$this->sessionVar]); + } + + public function testPersistsToSession() + { + $jar = new SessionCookieJar($this->sessionVar); + $jar->setCookie(new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'baz', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'boo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + ])); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the sessionVar in $_SESSION + $contents = $_SESSION[$this->sessionVar]; + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new SessionCookieJar($this->sessionVar); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unset($_SESSION[$this->sessionVar]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php new file mode 100755 index 000000000..3ddd08201 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php @@ -0,0 +1,364 @@ +assertEquals('/', $cookie->getPath()); + } + + public function testConvertsDateTimeMaxAgeToUnixTimestamp() + { + $cookie = new SetCookie(['Expires' => 'November 20, 1984']); + $this->assertInternalType('integer', $cookie->getExpires()); + } + + public function testAddsExpiresBasedOnMaxAge() + { + $t = time(); + $cookie = new SetCookie(['Max-Age' => 100]); + $this->assertEquals($t + 100, $cookie->getExpires()); + } + + public function testHoldsValues() + { + $t = time(); + $data = array( + 'Name' => 'foo', + 'Value' => 'baz', + 'Path' => '/bar', + 'Domain' => 'baz.com', + 'Expires' => $t, + 'Max-Age' => 100, + 'Secure' => true, + 'Discard' => true, + 'HttpOnly' => true, + 'foo' => 'baz', + 'bar' => 'bam' + ); + + $cookie = new SetCookie($data); + $this->assertEquals($data, $cookie->toArray()); + + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('baz', $cookie->getValue()); + $this->assertEquals('baz.com', $cookie->getDomain()); + $this->assertEquals('/bar', $cookie->getPath()); + $this->assertEquals($t, $cookie->getExpires()); + $this->assertEquals(100, $cookie->getMaxAge()); + $this->assertTrue($cookie->getSecure()); + $this->assertTrue($cookie->getDiscard()); + $this->assertTrue($cookie->getHttpOnly()); + $this->assertEquals('baz', $cookie->toArray()['foo']); + $this->assertEquals('bam', $cookie->toArray()['bar']); + + $cookie->setName('a'); + $cookie->setValue('b'); + $cookie->setPath('c'); + $cookie->setDomain('bar.com'); + $cookie->setExpires(10); + $cookie->setMaxAge(200); + $cookie->setSecure(false); + $cookie->setHttpOnly(false); + $cookie->setDiscard(false); + + $this->assertEquals('a', $cookie->getName()); + $this->assertEquals('b', $cookie->getValue()); + $this->assertEquals('c', $cookie->getPath()); + $this->assertEquals('bar.com', $cookie->getDomain()); + $this->assertEquals(10, $cookie->getExpires()); + $this->assertEquals(200, $cookie->getMaxAge()); + $this->assertFalse($cookie->getSecure()); + $this->assertFalse($cookie->getDiscard()); + $this->assertFalse($cookie->getHttpOnly()); + } + + public function testDeterminesIfExpired() + { + $c = new SetCookie(); + $c->setExpires(10); + $this->assertTrue($c->isExpired()); + $c->setExpires(time() + 10000); + $this->assertFalse($c->isExpired()); + } + + public function testMatchesDomain() + { + $cookie = new SetCookie(); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('baz.com'); + $this->assertTrue($cookie->matchesDomain('baz.com')); + $this->assertFalse($cookie->matchesDomain('bar.com')); + + $cookie->setDomain('.baz.com'); + $this->assertTrue($cookie->matchesDomain('.baz.com')); + $this->assertTrue($cookie->matchesDomain('foo.baz.com')); + $this->assertFalse($cookie->matchesDomain('baz.bar.com')); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('.com.'); + $this->assertFalse($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.local'); + $this->assertTrue($cookie->matchesDomain('example.local')); + } + + public function testMatchesPath() + { + $cookie = new SetCookie(); + $this->assertTrue($cookie->matchesPath('/foo')); + + $cookie->setPath('/foo'); + $this->assertTrue($cookie->matchesPath('/foo')); + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/bar')); + } + + public function cookieValidateProvider() + { + return array( + array('foo', 'baz', 'bar', true), + array('0', '0', '0', true), + array('', 'baz', 'bar', 'The cookie name must not be empty'), + array('foo', '', 'bar', 'The cookie value must not be empty'), + array('foo', 'baz', '', 'The cookie domain must not be empty'), + array("foo\r", 'baz', '0', 'Cookie name must not cannot invalid characters: =,; \t\r\n\013\014'), + ); + } + + /** + * @dataProvider cookieValidateProvider + */ + public function testValidatesCookies($name, $value, $domain, $result) + { + $cookie = new SetCookie(array( + 'Name' => $name, + 'Value' => $value, + 'Domain' => $domain + )); + $this->assertSame($result, $cookie->validate()); + } + + public function testDoesNotMatchIp() + { + $cookie = new SetCookie(['Domain' => '192.168.16.']); + $this->assertFalse($cookie->matchesDomain('192.168.16.121')); + } + + public function testConvertsToString() + { + $t = 1382916008; + $cookie = new SetCookie([ + 'Name' => 'test', + 'Value' => '123', + 'Domain' => 'foo.com', + 'Expires' => $t, + 'Path' => '/abc', + 'HttpOnly' => true, + 'Secure' => true + ]); + $this->assertEquals( + 'test=123; Domain=foo.com; Path=/abc; Expires=Sun, 27 Oct 2013 23:20:08 GMT; Secure; HttpOnly', + (string) $cookie + ); + } + + /** + * Provides the parsed information from a cookie + * + * @return array + */ + public function cookieParserDataProvider() + { + return array( + array( + 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/";', + array( + 'Domain' => 'allseeing-i.com', + 'Path' => '/', + 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c', + 'Max-Age' => NULL, + 'Expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', + 'Secure' => NULL, + 'Discard' => NULL, + 'Name' => 'ASIHTTPRequestTestCookie', + 'Value' => 'This+is+the+value', + 'HttpOnly' => false + ) + ), + array('', []), + array('foo', []), + // Test setting a blank value for a cookie + array(array( + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), + array( + 'Name' => 'foo', + 'Value' => '', + 'Discard' => null, + 'Domain' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + // Test setting a value and removing quotes + array(array( + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), + array( + 'Name' => 'foo', + 'Value' => '1', + 'Discard' => null, + 'Domain' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php + array( + 'justacookie=foo; domain=example.com', + array( + 'Name' => 'justacookie', + 'Value' => 'foo', + 'Domain' => 'example.com', + 'Discard' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + array( + 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', + array( + 'Name' => 'expires', + 'Value' => 'tomorrow', + 'Domain' => '.example.com', + 'Path' => '/Space Out/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Discard' => null, + 'Secure' => true, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', + array( + 'Name' => 'domain', + 'Value' => 'unittests', + 'Domain' => 'example.com', + 'Path' => '/some value/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => false, + 'Discard' => null, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', + array( + 'Name' => 'path', + 'Value' => 'indexAction', + 'Domain' => '.foo.com', + 'Path' => '/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => false, + 'Discard' => null, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', + array( + 'Name' => 'secure', + 'Value' => 'sha1', + 'Domain' => 'some.really.deep.domain.com', + 'Path' => '/', + 'Secure' => true, + 'Discard' => null, + 'Expires' => time() + 86400, + 'Max-Age' => 86400, + 'HttpOnly' => false, + 'version' => '1' + ) + ), + array( + 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', + array( + 'Name' => 'PHPSESSID', + 'Value' => '123456789+abcd%2Cef', + 'Domain' => '.localdomain', + 'Path' => '/foo/baz', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => true, + 'Discard' => true, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + ); + } + + /** + * @dataProvider cookieParserDataProvider + */ + public function testParseCookie($cookie, $parsed) + { + foreach ((array) $cookie as $v) { + $c = SetCookie::fromString($v); + $p = $c->toArray(); + + if (isset($p['Expires'])) { + // Remove expires values from the assertion if they are relatively equal + if (abs($p['Expires'] != strtotime($parsed['Expires'])) < 40) { + unset($p['Expires']); + unset($parsed['Expires']); + } + } + + if (!empty($parsed)) { + foreach ($parsed as $key => $value) { + $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + foreach ($p as $key => $value) { + $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + } else { + $this->assertEquals([ + 'Name' => null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false, + ], $p); + } + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php new file mode 100755 index 000000000..b8c06f152 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php @@ -0,0 +1,14 @@ +getMockBuilder('GuzzleHttp\Event\AbstractEvent') + ->getMockForAbstractClass(); + $this->assertFalse($e->isPropagationStopped()); + $e->stopPropagation(); + $this->assertTrue($e->isPropagationStopped()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php new file mode 100755 index 000000000..50536c582 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php @@ -0,0 +1,33 @@ +getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertSame($t->client, $e->getClient()); + $this->assertSame($t->request, $e->getRequest()); + } + + public function testHasTransaction() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $r = new \ReflectionMethod($e, 'getTransaction'); + $r->setAccessible(true); + $this->assertSame($t, $r->invoke($e)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php new file mode 100755 index 000000000..6a39d8bb0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php @@ -0,0 +1,37 @@ +transferInfo = ['foo' => 'bar']; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRetryableEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $e->retry(); + $this->assertTrue($e->isPropagationStopped()); + $this->assertEquals('retry', $t->state); + } + + public function testCanRetryAfterDelay() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->transferInfo = ['foo' => 'bar']; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRetryableEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $e->retry(10); + $this->assertTrue($e->isPropagationStopped()); + $this->assertEquals('retry', $t->state); + $this->assertEquals(10, $t->request->getConfig()->get('delay')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php new file mode 100755 index 000000000..5313c8e7f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php @@ -0,0 +1,59 @@ +transferInfo = ['foo' => 'bar']; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertNull($e->getTransferInfo('baz')); + $this->assertEquals('bar', $e->getTransferInfo('foo')); + $this->assertEquals($t->transferInfo, $e->getTransferInfo()); + } + + public function testHasResponse() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->response = new Response(200); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertTrue($e->hasResponse()); + $this->assertSame($t->response, $e->getResponse()); + } + + public function testCanInterceptWithResponse() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $r = new Response(200); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $e->intercept($r); + $this->assertSame($t->response, $r); + $this->assertSame($t->response, $e->getResponse()); + $this->assertTrue($e->isPropagationStopped()); + } + + public function testReturnsNumberOfRetries() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->retries = 2; + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertEquals(2, $e->getRetryCount()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php new file mode 100755 index 000000000..469e4e251 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php @@ -0,0 +1,26 @@ +exception = new \Exception('foo'); + $e = new BeforeEvent($t); + $response = new Response(200); + $e->intercept($response); + $this->assertTrue($e->isPropagationStopped()); + $this->assertSame($t->response, $response); + $this->assertNull($t->exception); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php new file mode 100755 index 000000000..5b7061bc6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php @@ -0,0 +1,363 @@ +emitter = new Emitter(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->emitter = null; + $this->listener = null; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->emitter->listeners()); + } + + public function testAddListener() + { + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->emitter->hasListeners(self::preFoo)); + $this->assertTrue($this->emitter->hasListeners(self::preFoo)); + $this->assertCount(1, $this->emitter->listeners(self::postFoo)); + $this->assertCount(1, $this->emitter->listeners(self::postFoo)); + $this->assertCount(2, $this->emitter->listeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->emitter->on('pre.foo', array($listener1, 'preFoo'), -10); + $this->emitter->on('pre.foo', array($listener2, 'preFoo'), 10); + $this->emitter->on('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->emitter->listeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->emitter->on('pre.foo', [$listener1, 'preFoo'], -10); + $this->emitter->on('pre.foo', [$listener2, 'preFoo']); + $this->emitter->on('pre.foo', [$listener3, 'preFoo'], 10); + $this->emitter->on('post.foo', [$listener4, 'preFoo'], -10); + $this->emitter->on('post.foo', [$listener5, 'preFoo']); + $this->emitter->on('post.foo', [$listener6, 'preFoo'], 10); + + $expected = [ + 'pre.foo' => [[$listener3, 'preFoo'], [$listener2, 'preFoo'], [$listener1, 'preFoo']], + 'post.foo' => [[$listener6, 'preFoo'], [$listener5, 'preFoo'], [$listener4, 'preFoo']], + ]; + + $this->assertSame($expected, $this->emitter->listeners()); + } + + public function testDispatch() + { + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('post.foo', array($this->listener, 'postFoo')); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('GuzzleHttp\Event\EventInterface', $this->emitter->emit(self::preFoo, $this->getEvent())); + $event = $this->getEvent(); + $return = $this->emitter->emit(self::preFoo, $event); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->emitter->on('pre.foo', $listener); + $this->emitter->on('post.foo', $listener); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->emitter->on('post.foo', array($this->listener, 'postFoo'), 10); + $this->emitter->on('post.foo', array($otherListener, 'preFoo')); + $this->emitter->emit(self::postFoo, $this->getEvent()); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->emitter->on('pre.foo', $listener1, -10); + $this->emitter->on('pre.foo', $listener2); + $this->emitter->on('pre.foo', $listener3, 10); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->emitter->on('pre.bar', [$this->listener, 'preFoo']); + $this->assertNotEmpty($this->emitter->listeners(self::preBar)); + $this->emitter->removeListener('pre.bar', [$this->listener, 'preFoo']); + $this->assertEmpty($this->emitter->listeners(self::preBar)); + $this->emitter->removeListener('notExists', [$this->listener, 'preFoo']); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testAddSubscriberWithMultiple() + { + $eventSubscriber = new TestEventSubscriberWithMultiple(); + $this->emitter->attach($eventSubscriber); + $listeners = $this->emitter->listeners('pre.foo'); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertCount(2, $listeners); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->emitter->attach($eventSubscriber); + + $listeners = $this->emitter->listeners('pre.foo'); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('GuzzleHttp\Tests\Event\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testdetach() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + $this->emitter->detach($eventSubscriber); + $this->assertEmpty($this->emitter->listeners(self::preFoo)); + $this->assertEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testdetachWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + $this->emitter->detach($eventSubscriber); + $this->assertEmpty($this->emitter->listeners(self::preFoo)); + $this->assertEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testEventReceivesEventNameAsArgument() + { + $listener = new TestWithDispatcher(); + $this->emitter->on('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->emitter->emit('test', $this->getEvent()); + $this->assertEquals('test', $listener->name); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = new Emitter(); + $dispatcher->on('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertNotEmpty($dispatcher->listeners('bug.62976')); + } + + public function testRegistersEventsOnce() + { + $this->emitter->once('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->assertCount(2, $this->emitter->listeners(self::preFoo)); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertCount(1, $this->emitter->listeners(self::preFoo)); + } + + public function testReturnsEmptyArrayForNonExistentEvent() + { + $this->assertEquals([], $this->emitter->listeners('doesnotexist')); + } + + public function testCanAddFirstAndLastListeners() + { + $b = ''; + $this->emitter->on('foo', function () use (&$b) { $b .= 'a'; }, 'first'); // 1 + $this->emitter->on('foo', function () use (&$b) { $b .= 'b'; }, 'last'); // 0 + $this->emitter->on('foo', function () use (&$b) { $b .= 'c'; }, 'first'); // 2 + $this->emitter->on('foo', function () use (&$b) { $b .= 'd'; }, 'first'); // 3 + $this->emitter->on('foo', function () use (&$b) { $b .= 'e'; }, 'first'); // 4 + $this->emitter->on('foo', function () use (&$b) { $b .= 'f'; }); // 0 + $this->emitter->emit('foo', $this->getEvent()); + $this->assertEquals('edcabf', $b); + } + + /** + * @return \GuzzleHttp\Event\EventInterface + */ + private function getEvent() + { + return $this->getMockBuilder('GuzzleHttp\Event\AbstractEvent') + ->getMockForAbstractClass(); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(EventInterface $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(EventInterface $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + */ + public function testHasDeprecatedAddListener() + { + $emitter = new Emitter(); + $emitter->addListener('foo', function () {}); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + */ + public function testHasDeprecatedAddSubscriber() + { + $emitter = new Emitter(); + $emitter->addSubscriber('foo', new TestEventSubscriber()); + } +} + +class TestWithDispatcher +{ + public $name; + + public function foo(EventInterface $e, $name) + { + $this->name = $name; + } +} + +class TestEventSubscriber extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return [ + 'pre.foo' => ['preFoo'], + 'post.foo' => ['postFoo'] + ]; + } +} + +class TestEventSubscriberWithPriorities extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return [ + 'pre.foo' => ['preFoo', 10], + 'post.foo' => ['postFoo'] + ]; + } +} + +class TestEventSubscriberWithMultiple extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return ['pre.foo' => [['preFoo', 10],['preFoo', 20]]]; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php new file mode 100755 index 000000000..e91b7f0c3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php @@ -0,0 +1,23 @@ +request); + $t->exception = $except; + $e = new ErrorEvent($t); + $this->assertSame($e->getException(), $t->exception); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php new file mode 100755 index 000000000..470991871 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php @@ -0,0 +1,27 @@ +getMockBuilder('GuzzleHttp\Tests\Event\AbstractHasEmitter') + ->getMockForAbstractClass(); + + $result = $mock->getEmitter(); + $this->assertInstanceOf('GuzzleHttp\Event\EmitterInterface', $result); + $result2 = $mock->getEmitter(); + $this->assertSame($result, $result2); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php new file mode 100755 index 000000000..0b5d348fd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php @@ -0,0 +1,92 @@ +listeners = $this->prepareListeners($args, ['foo', 'bar']); + $this->attachListeners($this, $this->listeners); + } +} + +class ListenerAttacherTraitTest extends \PHPUnit_Framework_TestCase +{ + public function testRegistersEvents() + { + $fn = function () {}; + $o = new ObjectWithEvents([ + 'foo' => $fn, + 'bar' => $fn, + ]); + + $this->assertEquals([ + ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], + ], $o->listeners); + + $this->assertCount(1, $o->getEmitter()->listeners('foo')); + $this->assertCount(1, $o->getEmitter()->listeners('bar')); + } + + public function testRegistersEventsWithPriorities() + { + $fn = function () {}; + $o = new ObjectWithEvents([ + 'foo' => ['fn' => $fn, 'priority' => 99, 'once' => true], + 'bar' => ['fn' => $fn, 'priority' => 50], + ]); + + $this->assertEquals([ + ['name' => 'foo', 'fn' => $fn, 'priority' => 99, 'once' => true], + ['name' => 'bar', 'fn' => $fn, 'priority' => 50, 'once' => false], + ], $o->listeners); + } + + public function testRegistersMultipleEvents() + { + $fn = function () {}; + $eventArray = [['fn' => $fn], ['fn' => $fn]]; + $o = new ObjectWithEvents([ + 'foo' => $eventArray, + 'bar' => $eventArray, + ]); + + $this->assertEquals([ + ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], + ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], + ], $o->listeners); + + $this->assertCount(2, $o->getEmitter()->listeners('foo')); + $this->assertCount(2, $o->getEmitter()->listeners('bar')); + } + + public function testRegistersEventsWithOnce() + { + $called = 0; + $fn = function () use (&$called) { $called++; }; + $o = new ObjectWithEvents(['foo' => ['fn' => $fn, 'once' => true]]); + $ev = $this->getMock('GuzzleHttp\Event\EventInterface'); + $o->getEmitter()->emit('foo', $ev); + $o->getEmitter()->emit('foo', $ev); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEvents() + { + new ObjectWithEvents(['foo' => 'bar']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php new file mode 100755 index 000000000..664f8b6bb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php @@ -0,0 +1,25 @@ +assertSame($t->request, $p->getRequest()); + $this->assertSame($t->client, $p->getClient()); + $this->assertEquals(2, $p->downloadSize); + $this->assertEquals(1, $p->downloaded); + $this->assertEquals(3, $p->uploadSize); + $this->assertEquals(0, $p->uploaded); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php new file mode 100755 index 000000000..b3b96660f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php @@ -0,0 +1,74 @@ + [$cb]]], + [ + ['complete' => $cb], + ['complete'], + $cb, + ['complete' => [$cb, $cb]] + ], + [ + ['prepare' => []], + ['error', 'foo'], + $cb, + [ + 'prepare' => [], + 'error' => [$cb], + 'foo' => [$cb] + ] + ], + [ + ['prepare' => []], + ['prepare'], + $cb, + [ + 'prepare' => [$cb] + ] + ], + [ + ['prepare' => ['fn' => $cb]], + ['prepare'], $cb, + [ + 'prepare' => [ + ['fn' => $cb], + $cb + ] + ] + ], + ]; + } + + /** + * @dataProvider prepareEventProvider + */ + public function testConvertsEventArrays( + array $in, + array $events, + $add, + array $out + ) { + $result = RequestEvents::convertEventArray($in, $events, $add); + $this->assertEquals($out, $result); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventFormat() + { + RequestEvents::convertEventArray(['foo' => false], ['foo'], []); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php new file mode 100755 index 000000000..4ff9bfb6c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php @@ -0,0 +1,20 @@ +assertSame($res, $e->getResponse()); + $this->assertEquals('foo', $e->getMessage()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php new file mode 100755 index 000000000..bea9077bf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php @@ -0,0 +1,83 @@ +assertSame($req, $e->getRequest()); + $this->assertSame($res, $e->getResponse()); + $this->assertTrue($e->hasResponse()); + $this->assertEquals('foo', $e->getMessage()); + } + + public function testCreatesGenerateException() + { + $e = RequestException::create(new Request('GET', '/')); + $this->assertEquals('Error completing request', $e->getMessage()); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function testCreatesClientErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(400)); + $this->assertEquals( + 'Client error response [url] / [status code] 400 [reason phrase] Bad Request', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $e); + } + + public function testCreatesServerErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(500)); + $this->assertEquals( + 'Server error response [url] / [status code] 500 [reason phrase] Internal Server Error', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\ServerException', $e); + } + + public function testCreatesGenericErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(600)); + $this->assertEquals( + 'Unsuccessful response [url] / [status code] 600 [reason phrase] ', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function testHasStatusCodeAsExceptionCode() { + $e = RequestException::create(new Request('GET', '/'), new Response(442)); + $this->assertEquals(442, $e->getCode()); + } + + public function testWrapsRequestExceptions() + { + $e = new \Exception('foo'); + $r = new Request('GET', 'http://www.oo.com'); + $ex = RequestException::wrapException($r, $e); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $ex); + $this->assertSame($e, $ex->getPrevious()); + } + + public function testWrapsConnectExceptions() + { + $e = new ConnectException('foo'); + $r = new Request('GET', 'http://www.oo.com'); + $ex = RequestException::wrapException($r, $e); + $this->assertInstanceOf('GuzzleHttp\Exception\ConnectException', $ex); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php new file mode 100755 index 000000000..51b97425e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php @@ -0,0 +1,19 @@ +assertSame($error, $e->getError()); + $this->assertEquals('foo', $e->getMessage()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php new file mode 100755 index 000000000..e26c64d9f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php @@ -0,0 +1,123 @@ +createRequest( + 'GET', + Server::$url, + [ + 'timeout' => 1, + 'connect_timeout' => 1, + 'proxy' => 'http://127.0.0.1:123/foo' + ] + ); + + $events = []; + $fn = function(AbstractTransferEvent $event) use (&$events) { + $events[] = [ + get_class($event), + $event->hasResponse(), + $event->getResponse() + ]; + }; + + $pool = new Pool($c, [$r], [ + 'error' => $fn, + 'end' => $fn + ]); + + $pool->wait(); + + $this->assertCount(2, $events); + $this->assertEquals('GuzzleHttp\Event\ErrorEvent', $events[0][0]); + $this->assertFalse($events[0][1]); + $this->assertNull($events[0][2]); + + $this->assertEquals('GuzzleHttp\Event\EndEvent', $events[1][0]); + $this->assertFalse($events[1][1]); + $this->assertNull($events[1][2]); + } + + /** + * @issue https://github.com/guzzle/guzzle/issues/866 + */ + public function testProperyGetsTransferStats() + { + $transfer = []; + Server::enqueue([new Response(200)]); + $c = new Client(); + $response = $c->get(Server::$url . '/foo', [ + 'events' => [ + 'end' => function (EndEvent $e) use (&$transfer) { + $transfer = $e->getTransferInfo(); + } + ] + ]); + $this->assertEquals(Server::$url . '/foo', $response->getEffectiveUrl()); + $this->assertNotEmpty($transfer); + $this->assertArrayHasKey('url', $transfer); + } + + public function testNestedFutureResponsesAreResolvedWhenSending() + { + $c = new Client(); + $total = 3; + Server::enqueue([ + new Response(200), + new Response(201), + new Response(202) + ]); + $c->getEmitter()->on( + 'complete', + function (CompleteEvent $e) use (&$total) { + if (--$total) { + $e->retry(); + } + } + ); + $response = $c->get(Server::$url); + $this->assertEquals(202, $response->getStatusCode()); + $this->assertEquals('GuzzleHttp\Message\Response', get_class($response)); + } + + public function testNestedFutureErrorsAreResolvedWhenSending() + { + $c = new Client(); + $total = 3; + Server::enqueue([ + new Response(500), + new Response(501), + new Response(502) + ]); + $c->getEmitter()->on( + 'error', + function (ErrorEvent $e) use (&$total) { + if (--$total) { + $e->retry(); + } + } + ); + try { + $c->get(Server::$url); + $this->fail('Did not throw!'); + } catch (RequestException $e) { + $this->assertEquals(502, $e->getResponse()->getStatusCode()); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php new file mode 100755 index 000000000..f02a576f5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php @@ -0,0 +1,269 @@ +assertEquals(1.1, $m->getProtocolVersion()); + } + + public function testHasHeaders() + { + $m = new Request('GET', 'http://foo.com'); + $this->assertFalse($m->hasHeader('foo')); + $m->addHeader('foo', 'bar'); + $this->assertTrue($m->hasHeader('foo')); + } + + public function testInitializesMessageWithProtocolVersionOption() + { + $m = new Request('GET', '/', [], null, [ + 'protocol_version' => '10' + ]); + $this->assertEquals(10, $m->getProtocolVersion()); + } + + public function testHasBody() + { + $m = new Request('GET', 'http://foo.com'); + $this->assertNull($m->getBody()); + $s = Stream::factory('test'); + $m->setBody($s); + $this->assertSame($s, $m->getBody()); + $this->assertFalse($m->hasHeader('Content-Length')); + } + + public function testCanRemoveBodyBySettingToNullAndRemovesCommonBodyHeaders() + { + $m = new Request('GET', 'http://foo.com'); + $m->setBody(Stream::factory('foo')); + $m->setHeader('Content-Length', 3); + $m->setHeader('Transfer-Encoding', 'chunked'); + $m->setBody(null); + $this->assertNull($m->getBody()); + $this->assertFalse($m->hasHeader('Content-Length')); + $this->assertFalse($m->hasHeader('Transfer-Encoding')); + } + + public function testCastsToString() + { + $m = new Request('GET', 'http://foo.com'); + $m->setHeader('foo', 'bar'); + $m->setBody(Stream::factory('baz')); + $this->assertEquals("GET / HTTP/1.1\r\nHost: foo.com\r\nfoo: bar\r\n\r\nbaz", (string) $m); + } + + public function parseParamsProvider() + { + $res1 = array( + array( + '', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + array( + '', + 'rel' => 'back', + 'type' => 'image/jpeg', + ), + ); + + return array( + array( + '; rel="front"; type="image/jpeg", ; rel=back; type="image/jpeg"', + $res1 + ), + array( + '; rel="front"; type="image/jpeg",; rel=back; type="image/jpeg"', + $res1 + ), + array( + 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', + array( + array('foo' => 'baz', 'bar' => '123'), + array('boo'), + array('test' => '123'), + array('foobar' => 'foo;bar') + ) + ), + array( + '; rel="side"; type="image/jpeg",; rel=side; type="image/jpeg"', + array( + array('', 'rel' => 'side', 'type' => 'image/jpeg'), + array('', 'rel' => 'side', 'type' => 'image/jpeg') + ) + ), + array( + '', + array() + ) + ); + } + + /** + * @dataProvider parseParamsProvider + */ + public function testParseParams($header, $result) + { + $request = new Request('GET', '/', ['foo' => $header]); + $this->assertEquals($result, Request::parseHeader($request, 'foo')); + } + + public function testAddsHeadersWhenNotPresent() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('foo', 'bar'); + $this->assertInternalType('string', $h->getHeader('foo')); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testAddsHeadersWhenPresentSameCase() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('foo', 'bar'); + $h->addHeader('foo', 'baz'); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + $this->assertEquals(['bar', 'baz'], $h->getHeaderAsArray('foo')); + } + + public function testAddsMultipleHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeaders([ + 'foo' => ' bar', + 'baz' => [' bam ', 'boo'] + ]); + $this->assertEquals([ + 'foo' => ['bar'], + 'baz' => ['bam', 'boo'], + 'Host' => ['foo.com'] + ], $h->getHeaders()); + } + + public function testAddsHeadersWhenPresentDifferentCase() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('Foo', 'bar'); + $h->addHeader('fOO', 'baz'); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + } + + public function testAddsHeadersWithArray() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('Foo', ['bar', 'baz']); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + } + + public function testGetHeadersReturnsAnArrayOfOverTheWireHeaderValues() + { + $h = new Request('GET', 'http://foo.com'); + $h->addHeader('foo', 'bar'); + $h->addHeader('Foo', 'baz'); + $h->addHeader('boO', 'test'); + $result = $h->getHeaders(); + $this->assertInternalType('array', $result); + $this->assertArrayHasKey('Foo', $result); + $this->assertArrayNotHasKey('foo', $result); + $this->assertArrayHasKey('boO', $result); + $this->assertEquals(['bar', 'baz'], $result['Foo']); + $this->assertEquals(['test'], $result['boO']); + } + + public function testSetHeaderOverwritesExistingValues() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $this->assertEquals('bar', $h->getHeader('foo')); + $h->setHeader('Foo', 'baz'); + $this->assertEquals('baz', $h->getHeader('foo')); + $this->assertArrayHasKey('Foo', $h->getHeaders()); + } + + public function testSetHeaderOverwritesExistingValuesUsingHeaderArray() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', ['bar']); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testSetHeaderOverwritesExistingValuesUsingArray() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', ['bar']); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testSetHeadersOverwritesAllHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $h->setHeaders(['foo' => 'a', 'boo' => 'b']); + $this->assertEquals(['foo' => ['a'], 'boo' => ['b']], $h->getHeaders()); + } + + public function testChecksIfCaseInsensitiveHeaderIsPresent() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $this->assertTrue($h->hasHeader('foo')); + $this->assertTrue($h->hasHeader('Foo')); + $h->setHeader('fOo', 'bar'); + $this->assertTrue($h->hasHeader('Foo')); + } + + public function testRemovesHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 'bar'); + $h->removeHeader('foo'); + $this->assertFalse($h->hasHeader('foo')); + $h->setHeader('Foo', 'bar'); + $h->removeHeader('FOO'); + $this->assertFalse($h->hasHeader('foo')); + } + + public function testReturnsCorrectTypeWhenMissing() + { + $h = new Request('GET', 'http://foo.com'); + $this->assertInternalType('string', $h->getHeader('foo')); + $this->assertInternalType('array', $h->getHeaderAsArray('foo')); + } + + public function testSetsIntegersAndFloatsAsHeaders() + { + $h = new Request('GET', 'http://foo.com'); + $h->setHeader('foo', 10); + $h->setHeader('bar', 10.5); + $h->addHeader('foo', 10); + $h->addHeader('bar', 10.5); + $this->assertSame('10, 10', $h->getHeader('foo')); + $this->assertSame('10.5, 10.5', $h->getHeader('bar')); + } + + public function testGetsResponseStartLine() + { + $m = new Response(200); + $this->assertEquals('HTTP/1.1 200 OK', Response::getStartLine($m)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsWhenMessageIsUnknown() + { + $m = $this->getMockBuilder('GuzzleHttp\Message\AbstractMessage') + ->getMockForAbstractClass(); + AbstractMessage::getStartLine($m); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php new file mode 100755 index 000000000..771631d56 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php @@ -0,0 +1,160 @@ +foo; + } + + public function testDoesTheSameAsResponseWhenDereferenced() + { + $str = Stream::factory('foo'); + $response = new Response(200, ['Foo' => 'bar'], $str); + $future = MockTest::createFuture(function () use ($response) { + return $response; + }); + $this->assertFalse($this->readAttribute($future, 'isRealized')); + $this->assertEquals(200, $future->getStatusCode()); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + // Deref again does nothing. + $future->wait(); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + $this->assertEquals('bar', $future->getHeader('Foo')); + $this->assertEquals(['bar'], $future->getHeaderAsarray('Foo')); + $this->assertSame($response->getHeaders(), $future->getHeaders()); + $this->assertSame( + $response->getBody(), + $future->getBody() + ); + $this->assertSame( + $response->getProtocolVersion(), + $future->getProtocolVersion() + ); + $this->assertSame( + $response->getEffectiveUrl(), + $future->getEffectiveUrl() + ); + $future->setEffectiveUrl('foo'); + $this->assertEquals('foo', $response->getEffectiveUrl()); + $this->assertSame( + $response->getReasonPhrase(), + $future->getReasonPhrase() + ); + + $this->assertTrue($future->hasHeader('foo')); + + $future->removeHeader('Foo'); + $this->assertFalse($future->hasHeader('foo')); + $this->assertFalse($response->hasHeader('foo')); + + $future->setBody(Stream::factory('true')); + $this->assertEquals('true', (string) $response->getBody()); + $this->assertTrue($future->json()); + $this->assertSame((string) $response, (string) $future); + + $future->setBody(Stream::factory('c')); + $this->assertEquals('c', (string) $future->xml()->b); + + $future->addHeader('a', 'b'); + $this->assertEquals('b', $future->getHeader('a')); + + $future->addHeaders(['a' => '2']); + $this->assertEquals('b, 2', $future->getHeader('a')); + + $future->setHeader('a', '2'); + $this->assertEquals('2', $future->getHeader('a')); + + $future->setHeaders(['a' => '3']); + $this->assertEquals(['a' => ['3']], $future->getHeaders()); + } + + public function testCanDereferenceManually() + { + $response = new Response(200, ['Foo' => 'bar']); + $future = MockTest::createFuture(function () use ($response) { + return $response; + }); + $this->assertSame($response, $future->wait()); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + } + + public function testCanCancel() + { + $c = false; + $deferred = new Deferred(); + $future = new FutureResponse( + $deferred->promise(), + function () {}, + function () use (&$c) { + $c = true; + return true; + } + ); + + $this->assertFalse($this->readAttribute($future, 'isRealized')); + $future->cancel(); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + $future->cancel(); + } + + public function testCanCancelButReturnsFalseForNoCancelFunction() + { + $future = MockTest::createFuture(function () {}); + $future->cancel(); + $this->assertTrue($this->readAttribute($future, 'isRealized')); + } + + /** + * @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException + */ + public function testAccessingCancelledResponseThrows() + { + $future = MockTest::createFuture(function () {}); + $future->cancel(); + $future->getStatusCode(); + } + + public function testExceptionInToStringTriggersError() + { + $future = MockTest::createFuture(function () { + throw new \Exception('foo'); + }); + $err = ''; + set_error_handler(function () use (&$err) { + $err = func_get_args()[1]; + }); + echo $future; + restore_error_handler(); + $this->assertContains('foo', $err); + } + + public function testProxiesSetters() + { + $str = Stream::factory('foo'); + $response = new Response(200, ['Foo' => 'bar'], $str); + $future = MockTest::createFuture(function () use ($response) { + return $response; + }); + + $future->setStatusCode(202); + $this->assertEquals(202, $future->getStatusCode()); + $this->assertEquals(202, $response->getStatusCode()); + + $future->setReasonPhrase('foo'); + $this->assertEquals('foo', $future->getReasonPhrase()); + $this->assertEquals('foo', $response->getReasonPhrase()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php new file mode 100755 index 000000000..390f01034 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php @@ -0,0 +1,601 @@ +createResponse(200, ['foo' => 'bar'], 'test', [ + 'protocol_version' => 1.0 + ]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(['foo' => ['bar']], $response->getHeaders()); + $this->assertEquals('test', $response->getBody()); + $this->assertEquals(1.0, $response->getProtocolVersion()); + } + + public function testCreatesRequestFromMessage() + { + $f = new MessageFactory(); + $req = $f->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\n"); + $this->assertEquals('GET', $req->getMethod()); + $this->assertEquals('/', $req->getPath()); + $this->assertEquals('foo', $req->getHeader('Baz')); + $this->assertNull($req->getBody()); + } + + public function testCreatesRequestFromMessageWithBody() + { + $req = (new MessageFactory())->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\ntest"); + $this->assertEquals('test', $req->getBody()); + } + + public function testCreatesRequestWithPostBody() + { + $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', ['body' => ['abc' => '123']]); + $this->assertEquals('abc=123', $req->getBody()); + } + + public function testCreatesRequestWithPostBodyScalars() + { + $req = (new MessageFactory())->createRequest( + 'GET', + 'http://www.foo.com', + ['body' => [ + 'abc' => true, + '123' => false, + 'foo' => null, + 'baz' => 10, + 'bam' => 1.5, + 'boo' => [1]] + ] + ); + $this->assertEquals( + 'abc=1&123=&foo&baz=10&bam=1.5&boo%5B0%5D=1', + (string) $req->getBody() + ); + } + + public function testCreatesRequestWithPostBodyAndPostFiles() + { + $pf = fopen(__FILE__, 'r'); + $pfi = new PostFile('ghi', 'abc', __FILE__); + $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', [ + 'body' => [ + 'abc' => '123', + 'def' => $pf, + 'ghi' => $pfi + ] + ]); + $this->assertInstanceOf('GuzzleHttp\Post\PostBody', $req->getBody()); + $s = (string) $req; + $this->assertContains('testCreatesRequestWithPostBodyAndPostFiles', $s); + $this->assertContains('multipart/form-data', $s); + $this->assertTrue(in_array($pfi, $req->getBody()->getFiles(), true)); + } + + public function testCreatesResponseFromMessage() + { + $response = (new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals('4', $response->getHeader('Content-Length')); + $this->assertEquals('test', $response->getBody(true)); + } + + public function testCanCreateHeadResponses() + { + $response = (new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(null, $response->getBody()); + $this->assertEquals('4', $response->getHeader('Content-Length')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFactoryRequiresMessageForRequest() + { + (new MessageFactory())->fromMessage(''); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage foo + */ + public function testValidatesOptionsAreImplemented() + { + (new MessageFactory())->createRequest('GET', 'http://test.com', ['foo' => 'bar']); + } + + public function testOptionsAddsRequestOptions() + { + $request = (new MessageFactory())->createRequest( + 'GET', 'http://test.com', ['config' => ['baz' => 'bar']] + ); + $this->assertEquals('bar', $request->getConfig()->get('baz')); + } + + public function testCanDisableRedirects() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => false]); + $this->assertEmpty($request->getEmitter()->listeners('complete')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesRedirects() + { + (new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => 'foo']); + } + + public function testCanEnableStrictRedirectsAndSpecifyMax() + { + $request = (new MessageFactory())->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + $this->assertTrue($request->getConfig()['redirect']['strict']); + $this->assertEquals(10, $request->getConfig()['redirect']['max']); + } + + public function testCanAddCookiesFromHash() + { + $request = (new MessageFactory())->createRequest('GET', 'http://www.test.com/', [ + 'cookies' => ['Foo' => 'Bar'] + ]); + $cookies = null; + foreach ($request->getEmitter()->listeners('before') as $l) { + if ($l[0] instanceof Cookie) { + $cookies = $l[0]; + break; + } + } + if (!$cookies) { + $this->fail('Did not add cookie listener'); + } else { + $this->assertCount(1, $cookies->getCookieJar()); + } + } + + public function testAddsCookieUsingTrue() + { + $factory = new MessageFactory(); + $request1 = $factory->createRequest('GET', '/', ['cookies' => true]); + $request2 = $factory->createRequest('GET', '/', ['cookies' => true]); + $listeners = function ($r) { + return array_filter($r->getEmitter()->listeners('before'), function ($l) { + return $l[0] instanceof Cookie; + }); + }; + $this->assertSame($listeners($request1), $listeners($request2)); + } + + public function testAddsCookieFromCookieJar() + { + $jar = new CookieJar(); + $request = (new MessageFactory())->createRequest('GET', '/', ['cookies' => $jar]); + foreach ($request->getEmitter()->listeners('before') as $l) { + if ($l[0] instanceof Cookie) { + $this->assertSame($jar, $l[0]->getCookieJar()); + } + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCookies() + { + (new MessageFactory())->createRequest('GET', '/', ['cookies' => 'baz']); + } + + public function testCanAddQuery() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'query' => ['Foo' => 'Bar'] + ]); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesQuery() + { + (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'query' => 'foo' + ]); + } + + public function testCanSetDefaultQuery() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com?test=abc', [ + 'query' => ['Foo' => 'Bar', 'test' => 'def'] + ]); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + $this->assertEquals('abc', $request->getQuery()->get('test')); + } + + public function testCanSetDefaultQueryWithObject() + { + $request = (new MessageFactory)->createRequest( + 'GET', + 'http://foo.com?test=abc', [ + 'query' => new Query(['Foo' => 'Bar', 'test' => 'def']) + ] + ); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + $this->assertEquals('abc', $request->getQuery()->get('test')); + } + + public function testCanAddBasicAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => ['michael', 'test'] + ]); + $this->assertTrue($request->hasHeader('Authorization')); + } + + public function testCanAddDigestAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => ['michael', 'test', 'digest'] + ]); + $this->assertEquals('michael:test', $request->getConfig()->getPath('curl/' . CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getConfig()->getPath('curl/' . CURLOPT_HTTPAUTH)); + } + + public function testCanDisableAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => false + ]); + $this->assertFalse($request->hasHeader('Authorization')); + } + + public function testCanSetCustomAuth() + { + $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ + 'auth' => 'foo' + ]); + $this->assertEquals('foo', $request->getConfig()['auth']); + } + + public function testCanAddEvents() + { + $foo = null; + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $client->get('http://test.com', [ + 'events' => [ + 'before' => function () use (&$foo) { $foo = true; } + ] + ]); + $this->assertTrue($foo); + } + + public function testCanAddEventsWithPriority() + { + $foo = null; + $client = new Client(); + $client->getEmitter()->attach(new Mock(array(new Response(200)))); + $request = $client->createRequest('GET', 'http://test.com', [ + 'events' => [ + 'before' => [ + 'fn' => function () use (&$foo) { $foo = true; }, + 'priority' => 123 + ] + ] + ]); + $client->send($request); + $this->assertTrue($foo); + $l = $this->readAttribute($request->getEmitter(), 'listeners'); + $this->assertArrayHasKey(123, $l['before']); + } + + public function testCanAddEventsOnce() + { + $foo = 0; + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(200), + new Response(200), + ])); + $fn = function () use (&$foo) { ++$foo; }; + $request = $client->createRequest('GET', 'http://test.com', [ + 'events' => ['before' => ['fn' => $fn, 'once' => true]] + ]); + $client->send($request); + $this->assertEquals(1, $foo); + $client->send($request); + $this->assertEquals(1, $foo); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventContainsFn() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->createRequest('GET', '/', ['events' => ['before' => ['foo' => 'bar']]]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventIsArray() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->createRequest('GET', '/', ['events' => ['before' => '123']]); + } + + public function testCanAddSubscribers() + { + $mock = new Mock([new Response(200)]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->get('http://test.com', ['subscribers' => [$mock]]); + } + + public function testCanDisableExceptions() + { + $client = new Client(); + $this->assertEquals(500, $client->get('http://test.com', [ + 'subscribers' => [new Mock([new Response(500)])], + 'exceptions' => false + ])->getStatusCode()); + } + + public function testCanChangeSaveToLocation() + { + $saveTo = Stream::factory(); + $request = (new MessageFactory())->createRequest('GET', '/', ['save_to' => $saveTo]); + $this->assertSame($saveTo, $request->getConfig()->get('save_to')); + } + + public function testCanSetProxy() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['proxy' => '192.168.16.121']); + $this->assertEquals('192.168.16.121', $request->getConfig()->get('proxy')); + } + + public function testCanSetHeadersOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['headers' => ['Foo' => 'Bar']]); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + } + + public function testCanSetHeaders() + { + $request = (new MessageFactory())->createRequest('GET', '/', [ + 'headers' => ['Foo' => ['Baz', 'Bar'], 'Test' => '123'] + ]); + $this->assertEquals('Baz, Bar', $request->getHeader('Foo')); + $this->assertEquals('123', $request->getHeader('Test')); + } + + public function testCanSetTimeoutOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['timeout' => 1.5]); + $this->assertEquals(1.5, $request->getConfig()->get('timeout')); + } + + public function testCanSetConnectTimeoutOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['connect_timeout' => 1.5]); + $this->assertEquals(1.5, $request->getConfig()->get('connect_timeout')); + } + + public function testCanSetDebug() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['debug' => true]); + $this->assertTrue($request->getConfig()->get('debug')); + } + + public function testCanSetVerifyToOff() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => false]); + $this->assertFalse($request->getConfig()->get('verify')); + } + + public function testCanSetVerifyToOn() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => true]); + $this->assertTrue($request->getConfig()->get('verify')); + } + + public function testCanSetVerifyToPath() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('verify')); + } + + public function inputValidation() + { + return array_map(function ($option) { return array($option); }, array( + 'headers', 'events', 'subscribers', 'params' + )); + } + + /** + * @dataProvider inputValidation + * @expectedException \InvalidArgumentException + */ + public function testValidatesInput($option) + { + (new MessageFactory())->createRequest('GET', '/', [$option => 'foo']); + } + + public function testCanAddSslKey() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('ssl_key')); + } + + public function testCanAddSslKeyPassword() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => ['/foo.pem', 'bar']]); + $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('ssl_key')); + } + + public function testCanAddSslCert() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('cert')); + } + + public function testCanAddSslCertPassword() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => ['/foo.pem', 'bar']]); + $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('cert')); + } + + public function testCreatesBodyWithoutZeroString() + { + $request = (new MessageFactory())->createRequest('PUT', 'http://test.com', ['body' => '0']); + $this->assertSame('0', (string) $request->getBody()); + } + + public function testCanSetProtocolVersion() + { + $request = (new MessageFactory())->createRequest('GET', 'http://t.com', ['version' => 1.0]); + $this->assertEquals(1.0, $request->getProtocolVersion()); + } + + public function testCanAddJsonData() + { + $request = (new MessageFactory())->createRequest('PUT', 'http://f.com', [ + 'json' => ['foo' => 'bar'] + ]); + $this->assertEquals( + 'application/json', + $request->getHeader('Content-Type') + ); + $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); + } + + public function testCanAddJsonDataToAPostRequest() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.com', [ + 'json' => ['foo' => 'bar'] + ]); + $this->assertEquals( + 'application/json', + $request->getHeader('Content-Type') + ); + $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); + } + + public function testCanAddJsonDataAndNotOverwriteContentType() + { + $request = (new MessageFactory())->createRequest('PUT', 'http://f.com', [ + 'headers' => ['Content-Type' => 'foo'], + 'json' => null + ]); + $this->assertEquals('foo', $request->getHeader('Content-Type')); + $this->assertEquals('null', (string) $request->getBody()); + } + + public function testCanUseCustomRequestOptions() + { + $c = false; + $f = new MessageFactory([ + 'foo' => function (RequestInterface $request, $value) use (&$c) { + $c = true; + $this->assertEquals('bar', $value); + } + ]); + + $f->createRequest('PUT', 'http://f.com', [ + 'headers' => ['Content-Type' => 'foo'], + 'foo' => 'bar' + ]); + + $this->assertTrue($c); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/706 + */ + public function testDoesNotApplyPostBodyRightAway() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'body' => ['foo' => ['bar', 'baz']] + ]); + $this->assertEquals('', $request->getHeader('Content-Type')); + $this->assertEquals('', $request->getHeader('Content-Length')); + $request->getBody()->setAggregator(Query::duplicateAggregator()); + $request->getBody()->applyRequestHeaders($request); + $this->assertEquals('foo=bar&foo=baz', $request->getBody()); + } + + public function testCanForceMultipartUploadWithContentType() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $history = new History(); + $client->getEmitter()->attach($history); + $client->post('http://foo.com', [ + 'headers' => ['Content-Type' => 'multipart/form-data'], + 'body' => ['foo' => 'bar'] + ]); + $this->assertContains( + 'multipart/form-data; boundary=', + $history->getLastRequest()->getHeader('Content-Type') + ); + $this->assertContains( + "Content-Disposition: form-data; name=\"foo\"\r\n\r\nbar", + (string) $history->getLastRequest()->getBody() + ); + } + + public function testDecodeDoesNotForceAcceptHeader() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'decode_content' => true + ]); + $this->assertEquals('', $request->getHeader('Accept-Encoding')); + $this->assertTrue($request->getConfig()->get('decode_content')); + } + + public function testDecodeCanAddAcceptHeader() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'decode_content' => 'gzip' + ]); + $this->assertEquals('gzip', $request->getHeader('Accept-Encoding')); + $this->assertTrue($request->getConfig()->get('decode_content')); + } + + public function testCanDisableDecoding() + { + $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ + 'decode_content' => false + ]); + $this->assertEquals('', $request->getHeader('Accept-Encoding')); + $this->assertNull($request->getConfig()->get('decode_content')); + } +} + +class ExtendedFactory extends MessageFactory +{ + protected function add_foo() {} +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php new file mode 100755 index 000000000..0bcc9430f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php @@ -0,0 +1,276 @@ +compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new MessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } + + public function testParsesRequestsWithMissingProtocol() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function testParsesRequestsWithMissingVersion() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function testParsesResponsesWithMissingReasonPhrase() + { + $parser = new MessageParser(); + $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); + $this->assertEquals('200', $parts['code']); + $this->assertEquals('', $parts['reason_phrase']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function requestProvider() + { + $auth = base64_encode('michael:foo'); + + return array( + + // Empty request + array('', false), + + // Converts casing of request. Does not require host header. + array("GET / HTTP/1.1\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => '', + 'port' => '', + 'path' => '/', + 'query' => '' + ), + 'headers' => array(), + 'body' => '' + )), + // Path and query string, multiple header values per header and case sensitive storage + array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( + 'method' => 'HEAD', + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '', + 'path' => '/path', + 'query' => 'query=foo' + ), + 'headers' => array( + 'Host' => 'example.com', + 'X-Foo' => array('foo', 'foo', 'Baz'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + // Includes a body + array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( + 'method' => 'PUT', + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'request_url' => array( + 'scheme' => 'https', + 'host' => 'example.com', + 'port' => '443', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'host' => 'example.com:443', + 'Content-Length' => '4' + ), + 'body' => 'test' + )), + // Includes Authorization headers + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'Authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + // Include authorization header + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + ); + } + + public function responseProvider() + { + return array( + // Empty request + array('', false), + + array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'OK', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'code' => '400', + 'reason_phrase' => 'Bad Request', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 100 Continue\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'code' => '100', + 'reason_phrase' => 'Continue', + 'headers' => array(), + 'body' => '' + )), + array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '204', + 'reason_phrase' => 'No Content', + 'headers' => array( + 'X-Foo' => array('foo', 'foo'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'Ok that is great!', + 'headers' => array( + 'Content-Length' => 4 + ), + 'body' => 'Test' + )), + ); + } + + public function compareRequestResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['method'], $expected['method']); + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['protocol_version'], $expected['protocol_version']); + $this->assertEquals($result['request_url'], $expected['request_url']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + public function compareResponseResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['protocol_version'], $expected['protocol_version']); + $this->assertEquals($result['code'], $expected['code']); + $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + protected function normalizeHeaders($headers) + { + $normalized = array(); + foreach ($headers as $key => $value) { + $key = strtolower($key); + if (!isset($normalized[$key])) { + $normalized[$key] = $value; + } elseif (!is_array($normalized[$key])) { + $normalized[$key] = array($value); + } else { + $normalized[$key][] = $value; + } + } + + foreach ($normalized as $key => &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $normalized; + } + + public function compareHttpHeaders($result, $expected) + { + // Aggregate all headers case-insensitively + $result = $this->normalizeHeaders($result); + $expected = $this->normalizeHeaders($expected); + $this->assertEquals($result, $expected); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php new file mode 100755 index 000000000..4e670a49f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php @@ -0,0 +1,144 @@ + '123'], Stream::factory('foo')); + $this->assertEquals('PUT', $r->getMethod()); + $this->assertEquals('/test', $r->getUrl()); + $this->assertEquals('123', $r->getHeader('test')); + $this->assertEquals('foo', $r->getBody()); + } + + public function testConstructorInitializesMessageWithMixedCaseHeaders() + { + $r = new Request('GET', '/test', [ + 'Set-Cookie' => 'foo=bar, baz=bam', + 'Set-cookie' => 'hi=there', + 'other' => ['1', '2'] + ]); + + $this->assertEquals('foo=bar, baz=bam, hi=there', $r->getHeader('Set-Cookie')); + $this->assertEquals('1, 2', $r->getHeader('other')); + } + + public function testConstructorInitializesMessageWithProtocolVersion() + { + $r = new Request('GET', '', [], null, ['protocol_version' => 10]); + $this->assertEquals(10, $r->getProtocolVersion()); + } + + public function testConstructorInitializesMessageWithEmitter() + { + $e = new Emitter(); + $r = new Request('GET', '', [], null, ['emitter' => $e]); + $this->assertSame($r->getEmitter(), $e); + } + + public function testCloneIsDeep() + { + $r = new Request('GET', '/test', ['foo' => 'baz'], Stream::factory('foo')); + $r2 = clone $r; + + $this->assertNotSame($r->getEmitter(), $r2->getEmitter()); + $this->assertEquals('foo', $r2->getBody()); + + $r->getConfig()->set('test', 123); + $this->assertFalse($r2->getConfig()->hasKey('test')); + + $r->setPath('/abc'); + $this->assertEquals('/test', $r2->getPath()); + } + + public function testCastsToString() + { + $r = new Request('GET', 'http://test.com/test', ['foo' => 'baz'], Stream::factory('body')); + $s = explode("\r\n", (string) $r); + $this->assertEquals("GET /test HTTP/1.1", $s[0]); + $this->assertContains('Host: test.com', $s); + $this->assertContains('foo: baz', $s); + $this->assertContains('', $s); + $this->assertContains('body', $s); + } + + public function testSettingUrlOverridesHostHeaders() + { + $r = new Request('GET', 'http://test.com/test'); + $r->setUrl('https://baz.com/bar'); + $this->assertEquals('baz.com', $r->getHost()); + $this->assertEquals('baz.com', $r->getHeader('Host')); + $this->assertEquals('/bar', $r->getPath()); + $this->assertEquals('https', $r->getScheme()); + } + + public function testQueryIsMutable() + { + $r = new Request('GET', 'http://www.foo.com?baz=bar'); + $this->assertEquals('baz=bar', $r->getQuery()); + $this->assertInstanceOf('GuzzleHttp\Query', $r->getQuery()); + $r->getQuery()->set('hi', 'there'); + $this->assertEquals('/?baz=bar&hi=there', $r->getResource()); + } + + public function testQueryCanChange() + { + $r = new Request('GET', 'http://www.foo.com?baz=bar'); + $r->setQuery(new Query(['foo' => 'bar'])); + $this->assertEquals('foo=bar', $r->getQuery()); + } + + public function testCanChangeMethod() + { + $r = new Request('GET', 'http://www.foo.com'); + $r->setMethod('put'); + $this->assertEquals('PUT', $r->getMethod()); + } + + public function testCanChangeSchemeWithPort() + { + $r = new Request('GET', 'http://www.foo.com:80'); + $r->setScheme('https'); + $this->assertEquals('https://www.foo.com', $r->getUrl()); + } + + public function testCanChangeScheme() + { + $r = new Request('GET', 'http://www.foo.com'); + $r->setScheme('https'); + $this->assertEquals('https://www.foo.com', $r->getUrl()); + } + + public function testCanChangeHost() + { + $r = new Request('GET', 'http://www.foo.com:222'); + $r->setHost('goo'); + $this->assertEquals('http://goo:222', $r->getUrl()); + $this->assertEquals('goo:222', $r->getHeader('host')); + $r->setHost('goo:80'); + $this->assertEquals('http://goo', $r->getUrl()); + $this->assertEquals('goo', $r->getHeader('host')); + } + + public function testCanChangePort() + { + $r = new Request('GET', 'http://www.foo.com:222'); + $this->assertSame(222, $r->getPort()); + $this->assertEquals('www.foo.com', $r->getHost()); + $this->assertEquals('www.foo.com:222', $r->getHeader('host')); + $r->setPort(80); + $this->assertSame(80, $r->getPort()); + $this->assertEquals('www.foo.com', $r->getHost()); + $this->assertEquals('www.foo.com', $r->getHeader('host')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php new file mode 100755 index 000000000..bbae24a17 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php @@ -0,0 +1,120 @@ + 'hi!']); + $this->assertEquals(999, $response->getStatusCode()); + $this->assertEquals('hi!', $response->getReasonPhrase()); + } + + public function testConvertsToString() + { + $response = new Response(200); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); + // Add another header + $response = new Response(200, ['X-Test' => 'Guzzle']); + $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); + $response = new Response(200, ['Content-Length' => 4], Stream::factory('test')); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); + } + + public function testConvertsToStringAndSeeksToByteZero() + { + $response = new Response(200); + $s = Stream::factory('foo'); + $s->read(1); + $response->setBody($s); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\nfoo", (string) $response); + } + + public function testParsesJsonResponses() + { + $json = '{"foo": "bar"}'; + $response = new Response(200, [], Stream::factory($json)); + $this->assertEquals(['foo' => 'bar'], $response->json()); + $this->assertEquals(json_decode($json), $response->json(['object' => true])); + + $response = new Response(200); + $this->assertEquals(null, $response->json()); + } + + /** + * @expectedException \GuzzleHttp\Exception\ParseException + * @expectedExceptionMessage Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON + */ + public function testThrowsExceptionWhenFailsToParseJsonResponse() + { + $response = new Response(200, [], Stream::factory('{"foo": "')); + $response->json(); + } + + public function testParsesXmlResponses() + { + $response = new Response(200, [], Stream::factory('bar')); + $this->assertEquals('bar', (string) $response->xml()->foo); + // Always return a SimpleXMLElement from the xml method + $response = new Response(200); + $this->assertEmpty((string) $response->xml()->foo); + } + + /** + * @expectedException \GuzzleHttp\Exception\XmlParseException + * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML + */ + public function testThrowsExceptionWhenFailsToParseXmlResponse() + { + $response = new Response(200, [], Stream::factory('xml(); + } catch (XmlParseException $e) { + $xmlParseError = $e->getError(); + $this->assertInstanceOf('\LibXMLError', $xmlParseError); + $this->assertContains("Couldn't find end of Start Tag abc line 1", $xmlParseError->message); + throw $e; + } + } + + public function testHasEffectiveUrl() + { + $r = new Response(200); + $this->assertNull($r->getEffectiveUrl()); + $r->setEffectiveUrl('http://www.test.com'); + $this->assertEquals('http://www.test.com', $r->getEffectiveUrl()); + } + + public function testPreventsComplexExternalEntities() + { + $xml = ']>&test;'; + $response = new Response(200, [], Stream::factory($xml)); + + $oldCwd = getcwd(); + chdir(__DIR__); + try { + $xml = $response->xml(); + chdir($oldCwd); + $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); + } catch (\Exception $e) { + chdir($oldCwd); + } + } + + public function testStatusAndReasonAreMutable() + { + $response = new Response(200); + $response->setStatusCode(201); + $this->assertEquals(201, $response->getStatusCode()); + $response->setReasonPhrase('Foo'); + $this->assertEquals('Foo', $response->getReasonPhrase()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php new file mode 100755 index 000000000..a18ec3813 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php @@ -0,0 +1,31 @@ +assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); + } + + public function testGetsFromFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); + } + + public function testGetsFromCaseInsensitiveFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); + } + + public function testReturnsNullWhenNoMatchFound() + { + $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/PoolTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/PoolTest.php new file mode 100755 index 000000000..b5f02add8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/PoolTest.php @@ -0,0 +1,319 @@ + 10]); + $this->assertSame($c, $this->readAttribute($p, 'client')); + $this->assertEquals(10, $this->readAttribute($p, 'poolSize')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEachElement() + { + $c = new Client(); + $requests = ['foo']; + $p = new Pool($c, new \ArrayIterator($requests)); + $p->wait(); + } + + public function testSendsAndRealizesFuture() + { + $c = $this->getClient(); + $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); + $this->assertTrue($p->wait()); + $this->assertFalse($p->wait()); + $this->assertTrue($this->readAttribute($p, 'isRealized')); + $this->assertFalse($p->cancel()); + } + + public function testSendsManyRequestsInCappedPool() + { + $c = $this->getClient(); + $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); + $this->assertTrue($p->wait()); + $this->assertFalse($p->wait()); + } + + public function testSendsRequestsThatHaveNotBeenRealized() + { + $c = $this->getClient(); + $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); + $this->assertTrue($p->wait()); + $this->assertFalse($p->wait()); + $this->assertFalse($p->cancel()); + } + + public function testCancelsInFlightRequests() + { + $c = $this->getClient(); + $h = new History(); + $c->getEmitter()->attach($h); + $p = new Pool($c, [ + $c->createRequest('GET', 'http://foo.com'), + $c->createRequest('GET', 'http://foo.com', [ + 'events' => [ + 'before' => [ + 'fn' => function () use (&$p) { + $this->assertTrue($p->cancel()); + }, + 'priority' => RequestEvents::EARLY + ] + ] + ]) + ]); + ob_start(); + $p->wait(); + $contents = ob_get_clean(); + $this->assertEquals(1, count($h)); + $this->assertEquals('Cancelling', $contents); + } + + private function getClient() + { + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function() use ($deferred) { + $deferred->resolve(['status' => 200, 'headers' => []]); + }, function () { + echo 'Cancelling'; + } + ); + + return new Client(['handler' => new MockHandler($future)]); + } + + public function testBatchesRequests() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $responses = [ + new Response(301, ['Location' => 'http://foo.com/bar']), + new Response(200), + new Response(200), + new Response(404) + ]; + + $client->getEmitter()->attach(new Mock($responses)); + $requests = [ + $client->createRequest('GET', 'http://foo.com/baz'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + $a = $b = $c = $d = 0; + $result = Pool::batch($client, $requests, [ + 'before' => function (BeforeEvent $e) use (&$a) { $a++; }, + 'complete' => function (CompleteEvent $e) use (&$b) { $b++; }, + 'error' => function (ErrorEvent $e) use (&$c) { $c++; }, + 'end' => function (EndEvent $e) use (&$d) { $d++; } + ]); + + $this->assertEquals(4, $a); + $this->assertEquals(2, $b); + $this->assertEquals(1, $c); + $this->assertEquals(3, $d); + $this->assertCount(3, $result); + $this->assertInstanceOf('GuzzleHttp\BatchResults', $result); + + // The first result is actually the second (redirect) response. + $this->assertSame($responses[1], $result[0]); + // The second result is a 1:1 request:response map + $this->assertSame($responses[2], $result[1]); + // The third entry is the 404 RequestException + $this->assertSame($responses[3], $result[2]->getResponse()); + } + + public function testBatchesRequestsWithDynamicPoolSize() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $responses = [ + new Response(301, ['Location' => 'http://foo.com/bar']), + new Response(200), + new Response(200), + new Response(404) + ]; + + $client->getEmitter()->attach(new Mock($responses)); + $requests = [ + $client->createRequest('GET', 'http://foo.com/baz'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + $a = $b = $c = $d = 0; + $result = Pool::batch($client, $requests, [ + 'before' => function (BeforeEvent $e) use (&$a) { $a++; }, + 'complete' => function (CompleteEvent $e) use (&$b) { $b++; }, + 'error' => function (ErrorEvent $e) use (&$c) { $c++; }, + 'end' => function (EndEvent $e) use (&$d) { $d++; }, + 'pool_size' => function ($queueSize) { + static $options = [1, 2, 1]; + static $queued = 0; + + $this->assertEquals( + $queued, + $queueSize, + 'The number of queued requests should be equal to the sum of pool sizes so far.' + ); + + $next = array_shift($options); + $queued += $next; + + return $next; + } + ]); + + $this->assertEquals(4, $a); + $this->assertEquals(2, $b); + $this->assertEquals(1, $c); + $this->assertEquals(3, $d); + $this->assertCount(3, $result); + $this->assertInstanceOf('GuzzleHttp\BatchResults', $result); + + // The first result is actually the second (redirect) response. + $this->assertSame($responses[1], $result[0]); + // The second result is a 1:1 request:response map + $this->assertSame($responses[2], $result[1]); + // The third entry is the 404 RequestException + $this->assertSame($responses[3], $result[2]->getResponse()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Each event listener must be a callable or + */ + public function testBatchValidatesTheEventFormat() + { + $client = new Client(); + $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; + Pool::batch($client, $requests, ['complete' => 'foo']); + } + + public function testEmitsProgress() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $responses = [new Response(200), new Response(404)]; + $client->getEmitter()->attach(new Mock($responses)); + $requests = [ + $client->createRequest('GET', 'http://foo.com/baz'), + $client->createRequest('HEAD', 'http://httpbin.org/get') + ]; + + $pool = new Pool($client, $requests); + $count = 0; + $thenned = null; + $pool->then( + function ($value) use (&$thenned) { + $thenned = $value; + }, + null, + function ($result) use (&$count, $requests) { + $this->assertSame($requests[$count], $result['request']); + if ($count == 0) { + $this->assertNull($result['error']); + $this->assertEquals(200, $result['response']->getStatusCode()); + } else { + $this->assertInstanceOf( + 'GuzzleHttp\Exception\ClientException', + $result['error'] + ); + } + $count++; + } + ); + + $pool->wait(); + $this->assertEquals(2, $count); + $this->assertEquals(true, $thenned); + } + + public function testDoesNotThrowInErrorEvent() + { + $client = new Client(); + $responses = [new Response(404)]; + $client->getEmitter()->attach(new Mock($responses)); + $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; + $result = Pool::batch($client, $requests); + $this->assertCount(1, $result); + $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $result[0]); + } + + public function testHasSendMethod() + { + $client = new Client(); + $responses = [new Response(404)]; + $history = new History(); + $client->getEmitter()->attach($history); + $client->getEmitter()->attach(new Mock($responses)); + $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; + Pool::send($client, $requests); + $this->assertCount(1, $history); + } + + public function testDoesNotInfinitelyRecurse() + { + $client = new Client(['handler' => function () { + throw new \RuntimeException('No network access'); + }]); + + $last = null; + $client->getEmitter()->on( + 'before', + function (BeforeEvent $e) use (&$last) { + $e->intercept(new Response(200)); + if (function_exists('xdebug_get_stack_depth')) { + if ($last) { + $this->assertEquals($last, xdebug_get_stack_depth()); + } else { + $last = xdebug_get_stack_depth(); + } + } + } + ); + + $requests = []; + for ($i = 0; $i < 100; $i++) { + $requests[] = $client->createRequest('GET', 'http://foo.com'); + } + + $pool = new Pool($client, $requests); + $pool->wait(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php new file mode 100755 index 000000000..4b3b39164 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php @@ -0,0 +1,120 @@ + 'bar'], [ + new PostFile('foo', 'abc', 'foo.txt') + ], 'abcdef'); + } + + public function testConstructorAddsFieldsAndFiles() + { + $b = $this->getTestBody(); + $this->assertEquals('abcdef', $b->getBoundary()); + $c = (string) $b; + $this->assertContains("--abcdef\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n", $c); + $this->assertContains("--abcdef\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"foo.txt\"\r\n" + . "Content-Type: text/plain\r\n\r\nabc\r\n--abcdef--", $c); + } + + public function testDoesNotModifyFieldFormat() + { + $m = new MultipartBody(['foo+baz' => 'bar+bam %20 boo'], [ + new PostFile('foo+bar', 'abc %20 123', 'foo.txt') + ], 'abcdef'); + $this->assertContains('name="foo+baz"', (string) $m); + $this->assertContains('name="foo+bar"', (string) $m); + $this->assertContains('bar+bam %20 boo', (string) $m); + $this->assertContains('abc %20 123', (string) $m); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorValidatesFiles() + { + new MultipartBody([], ['bar']); + } + + public function testConstructorCanCreateBoundary() + { + $b = new MultipartBody(); + $this->assertNotNull($b->getBoundary()); + } + + public function testWrapsStreamMethods() + { + $b = $this->getTestBody(); + $this->assertFalse($b->write('foo')); + $this->assertFalse($b->isWritable()); + $this->assertTrue($b->isReadable()); + $this->assertTrue($b->isSeekable()); + $this->assertEquals(0, $b->tell()); + } + + public function testCanDetachFieldsAndFiles() + { + $b = $this->getTestBody(); + $b->detach(); + $b->close(); + $this->assertEquals('', (string) $b); + } + + public function testIsSeekableReturnsTrueIfAllAreSeekable() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable', 'isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(false)); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $p = new PostFile('foo', $s, 'foo.php'); + $b = new MultipartBody([], [$p]); + $this->assertFalse($b->isSeekable()); + $this->assertFalse($b->seek(10)); + } + + public function testReadsFromBuffer() + { + $b = $this->getTestBody(); + $c = $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $this->assertEquals('--abc', $c); + } + + public function testCalculatesSize() + { + $b = $this->getTestBody(); + $this->assertEquals(strlen($b), $b->getSize()); + } + + public function testCalculatesSizeAndReturnsNullForUnknown() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['getSize', 'isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(null)); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $b = new MultipartBody([], [new PostFile('foo', $s, 'foo.php')]); + $this->assertNull($b->getSize()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php new file mode 100755 index 000000000..0283a5ebf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php @@ -0,0 +1,255 @@ +assertTrue($b->isSeekable()); + $this->assertTrue($b->isReadable()); + $this->assertFalse($b->isWritable()); + $this->assertFalse($b->write('foo')); + } + + public function testApplyingWithNothingDoesNothing() + { + $b = new PostBody(); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertFalse($m->hasHeader('Content-Length')); + $this->assertFalse($m->hasHeader('Content-Type')); + } + + public function testCanForceMultipartUploadsWhenApplying() + { + $b = new PostBody(); + $b->forceMultipartUpload(true); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + } + + public function testApplyingWithFilesAddsMultipartUpload() + { + $b = new PostBody(); + $p = new PostFile('foo', fopen(__FILE__, 'r')); + $b->addFile($p); + $this->assertEquals([$p], $b->getFiles()); + $this->assertNull($b->getFile('missing')); + $this->assertSame($p, $b->getFile('foo')); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + } + + public function testApplyingWithFieldsAddsMultipartUpload() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $this->assertEquals(['foo' => 'bar'], $b->getFields()); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'application/x-www-form', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + } + + public function testMultipartWithNestedFields() + { + $b = new PostBody(); + $b->setField('foo', ['bar' => 'baz']); + $b->forceMultipartUpload(true); + $this->assertEquals(['foo' => ['bar' => 'baz']], $b->getFields()); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + $contents = $b->getContents(); + $this->assertContains('name="foo[bar]"', $contents); + $this->assertNotContains('name="foo"', $contents); + } + + public function testCountProvidesFieldsAndFiles() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); + $this->assertEquals(2, count($b)); + $b->clearFiles(); + $b->removeField('foo'); + $this->assertEquals(0, count($b)); + $this->assertEquals([], $b->getFiles()); + $this->assertEquals([], $b->getFields()); + } + + public function testHasFields() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->setField('baz', '123'); + $this->assertEquals('bar', $b->getField('foo')); + $this->assertEquals('123', $b->getField('baz')); + $this->assertNull($b->getField('ahh')); + $this->assertTrue($b->hasField('foo')); + $this->assertFalse($b->hasField('test')); + $b->replaceFields(['abc' => '123']); + $this->assertFalse($b->hasField('foo')); + $this->assertTrue($b->hasField('abc')); + } + + public function testConvertsFieldsToQueryStyleBody() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->setField('baz', '123'); + $this->assertEquals('foo=bar&baz=123', $b); + $this->assertEquals(15, $b->getSize()); + $b->seek(0); + $this->assertEquals('foo=bar&baz=123', $b->getContents()); + $b->seek(0); + $this->assertEquals('foo=bar&baz=123', $b->read(1000)); + $this->assertEquals(15, $b->tell()); + } + + public function testCanSpecifyQueryAggregator() + { + $b = new PostBody(); + $b->setField('foo', ['baz', 'bar']); + $this->assertEquals('foo%5B0%5D=baz&foo%5B1%5D=bar', (string) $b); + $b = new PostBody(); + $b->setField('foo', ['baz', 'bar']); + $agg = Query::duplicateAggregator(); + $b->setAggregator($agg); + $this->assertEquals('foo=baz&foo=bar', (string) $b); + } + + public function testDetachesAndCloses() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->detach(); + $b->close(); + $this->assertEquals('', $b->read(10)); + } + + public function testDetachesWhenBodyIsPresent() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->getContents(); + $b->detach(); + } + + public function testFlushAndMetadataPlaceholders() + { + $b = new PostBody(); + $this->assertEquals([], $b->getMetadata()); + $this->assertNull($b->getMetadata('foo')); + } + + public function testCreatesMultipartUploadWithMultiFields() + { + $b = new PostBody(); + $b->setField('testing', ['baz', 'bar']); + $b->setField('other', 'hi'); + $b->setField('third', 'there'); + $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); + $s = (string) $b; + $this->assertContains(file_get_contents(__FILE__), $s); + $this->assertContains('testing=bar', $s); + $this->assertContains( + 'Content-Disposition: form-data; name="third"', + $s + ); + $this->assertContains( + 'Content-Disposition: form-data; name="other"', + $s + ); + } + + public function testMultipartWithBase64Fields() + { + $b = new PostBody(); + $b->setField('foo64', '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='); + $b->forceMultipartUpload(true); + $this->assertEquals( + ['foo64' => '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='], + $b->getFields() + ); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + $contents = $b->getContents(); + $this->assertContains('name="foo64"', $contents); + $this->assertContains( + '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc=', + $contents + ); + } + + public function testMultipartWithAmpersandInValue() + { + $b = new PostBody(); + $b->setField('a', 'b&c=d'); + $b->forceMultipartUpload(true); + $this->assertEquals(['a' => 'b&c=d'], $b->getFields()); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains( + 'multipart/form-data', + $m->getHeader('Content-Type') + ); + $this->assertTrue($m->hasHeader('Content-Length')); + $contents = $b->getContents(); + $this->assertContains('name="a"', $contents); + $this->assertContains('b&c=d', $contents); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $b = new PostBody(); + $b->attach('foo'); + } + + public function testDoesNotOverwriteExistingHeaderForUrlencoded() + { + $m = new Request('POST', 'http://foo.com', [ + 'content-type' => 'application/x-www-form-urlencoded; charset=utf-8' + ]); + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->applyRequestHeaders($m); + $this->assertEquals( + 'application/x-www-form-urlencoded; charset=utf-8', + $m->getHeader('Content-Type') + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php new file mode 100755 index 000000000..800cee503 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php @@ -0,0 +1,61 @@ +assertInstanceOf('GuzzleHttp\Post\PostFileInterface', $p); + $this->assertEquals('hi', $p->getContent()); + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('/path/to/test.php', $p->getFilename()); + $this->assertEquals( + 'form-data; name="foo"; filename="test.php"', + $p->getHeaders()['Content-Disposition'] + ); + } + + public function testGetsFilenameFromMetadata() + { + $p = new PostFile('foo', fopen(__FILE__, 'r')); + $this->assertEquals(__FILE__, $p->getFilename()); + } + + public function testDefaultsToNameWhenNoFilenameExists() + { + $p = new PostFile('foo', 'bar'); + $this->assertEquals('foo', $p->getFilename()); + } + + public function testCreatesFromMultipartFormData() + { + $mp = new MultipartBody([], [], 'baz'); + $p = new PostFile('foo', $mp); + $this->assertEquals( + 'form-data; name="foo"', + $p->getHeaders()['Content-Disposition'] + ); + $this->assertEquals( + 'multipart/form-data; boundary=baz', + $p->getHeaders()['Content-Type'] + ); + } + + public function testCanAddHeaders() + { + $p = new PostFile('foo', Stream::factory('hi'), 'test.php', [ + 'X-Foo' => '123', + 'Content-Disposition' => 'bar' + ]); + $this->assertEquals('bar', $p->getHeaders()['Content-Disposition']); + $this->assertEquals('123', $p->getHeaders()['X-Foo']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php new file mode 100755 index 000000000..e9075a80d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php @@ -0,0 +1,80 @@ + ['a', 'b']]], + // Can parse multi-valued items that use numeric indices + ['q[0]=a&q[1]=b', ['q' => ['a', 'b']]], + // Can parse duplicates and does not include numeric indices + ['q[]=a&q[]=b', ['q' => ['a', 'b']]], + // Ensures that the value of "q" is an array even though one value + ['q[]=a', ['q' => ['a']]], + // Does not modify "." to "_" like PHP's parse_str() + ['q.a=a&q.b=b', ['q.a' => 'a', 'q.b' => 'b']], + // Can decode %20 to " " + ['q%20a=a%20b', ['q a' => 'a b']], + // Can parse funky strings with no values by assigning each to null + ['q&a', ['q' => null, 'a' => null]], + // Does not strip trailing equal signs + ['data=abc=', ['data' => 'abc=']], + // Can store duplicates without affecting other values + ['foo=a&foo=b&?µ=c', ['foo' => ['a', 'b'], '?µ' => 'c']], + // Sets value to null when no "=" is present + ['foo', ['foo' => null]], + // Preserves "0" keys. + ['0', ['0' => null]], + // Sets the value to an empty string when "=" is present + ['0=', ['0' => '']], + // Preserves falsey keys + ['var=0', ['var' => '0']], + // Can deeply nest and store duplicate PHP values + ['a[b][c]=1&a[b][c]=2', [ + 'a' => ['b' => ['c' => ['1', '2']]] + ]], + // Can parse PHP style arrays + ['a[b]=c&a[d]=e', ['a' => ['b' => 'c', 'd' => 'e']]], + // Ensure it doesn't leave things behind with repeated values + // Can parse mult-values items + ['q=a&q=b&q=c', ['q' => ['a', 'b', 'c']]], + ]; + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesQueries($input, $output) + { + $query = Query::fromString($input); + $this->assertEquals($output, $query->toArray()); + // Normalize the input and output + $query->setEncodingType(false); + $this->assertEquals(rawurldecode($input), (string) $query); + } + + public function testConvertsPlusSymbolsToSpacesByDefault() + { + $query = Query::fromString('var=foo+bar', true); + $this->assertEquals('foo bar', $query->get('var')); + } + + public function testCanControlDecodingType() + { + $qp = new QueryParser(); + $q = new Query(); + $qp->parseInto($q, 'var=foo+bar', Query::RFC3986); + $this->assertEquals('foo+bar', $q->get('var')); + $qp->parseInto($q, 'var=foo+bar', Query::RFC1738); + $this->assertEquals('foo bar', $q->get('var')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryTest.php new file mode 100755 index 000000000..8b9d3448f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/QueryTest.php @@ -0,0 +1,171 @@ + 'baz', 'bar' => 'bam boozle']); + $this->assertEquals('foo=baz&bar=bam%20boozle', (string) $q); + } + + public function testCanDisableUrlEncoding() + { + $q = new Query(['bar' => 'bam boozle']); + $q->setEncodingType(false); + $this->assertEquals('bar=bam boozle', (string) $q); + } + + public function testCanSpecifyRfc1783UrlEncodingType() + { + $q = new Query(['bar abc' => 'bam boozle']); + $q->setEncodingType(Query::RFC1738); + $this->assertEquals('bar+abc=bam+boozle', (string) $q); + } + + public function testCanSpecifyRfc3986UrlEncodingType() + { + $q = new Query(['bar abc' => 'bam boozle', 'ሴ' => 'hi']); + $q->setEncodingType(Query::RFC3986); + $this->assertEquals('bar%20abc=bam%20boozle&%E1%88%B4=hi', (string) $q); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEncodingType() + { + (new Query(['bar' => 'bam boozle']))->setEncodingType('foo'); + } + + public function testAggregatesMultipleValues() + { + $q = new Query(['foo' => ['bar', 'baz']]); + $this->assertEquals('foo%5B0%5D=bar&foo%5B1%5D=baz', (string) $q); + } + + public function testCanSetAggregator() + { + $q = new Query(['foo' => ['bar', 'baz']]); + $q->setAggregator(function (array $data) { + return ['foo' => ['barANDbaz']]; + }); + $this->assertEquals('foo=barANDbaz', (string) $q); + } + + public function testAllowsMultipleValuesPerKey() + { + $q = new Query(); + $q->add('facet', 'size'); + $q->add('facet', 'width'); + $q->add('facet.field', 'foo'); + // Use the duplicate aggregator + $q->setAggregator($q::duplicateAggregator()); + $this->assertEquals('facet=size&facet=width&facet.field=foo', (string) $q); + } + + public function testAllowsZeroValues() + { + $query = new Query(array( + 'foo' => 0, + 'baz' => '0', + 'bar' => null, + 'boo' => false + )); + $this->assertEquals('foo=0&baz=0&bar&boo=', (string) $query); + } + + private $encodeData = [ + 't' => [ + 'v1' => ['a', '1'], + 'v2' => 'b', + 'v3' => ['v4' => 'c', 'v5' => 'd'] + ] + ]; + + public function testEncodesDuplicateAggregator() + { + $agg = Query::duplicateAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testDuplicateEncodesNoNumericIndices() + { + $agg = Query::duplicateAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testEncodesPhpAggregator() + { + $agg = Query::phpAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1][0]' => ['a'], + 't[v1][1]' => ['1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testPhpEncodesNoNumericIndices() + { + $agg = Query::phpAggregator(false); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1][]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testCanDisableUrlEncodingDecoding() + { + $q = Query::fromString('foo=bar+baz boo%20', false); + $this->assertEquals('bar+baz boo%20', $q['foo']); + $this->assertEquals('foo=bar+baz boo%20', (string) $q); + } + + public function testCanChangeUrlEncodingDecodingToRfc1738() + { + $q = Query::fromString('foo=bar+baz', Query::RFC1738); + $this->assertEquals('bar baz', $q['foo']); + $this->assertEquals('foo=bar+baz', (string) $q); + } + + public function testCanChangeUrlEncodingDecodingToRfc3986() + { + $q = Query::fromString('foo=bar%20baz', Query::RFC3986); + $this->assertEquals('bar baz', $q['foo']); + $this->assertEquals('foo=bar%20baz', (string) $q); + } + + public function testQueryStringsAllowSlashButDoesNotDecodeWhenDisable() + { + $q = Query::fromString('foo=bar%2Fbaz&bam=boo%20boo', Query::RFC3986); + $q->setEncodingType(false); + $this->assertEquals('foo=bar/baz&bam=boo boo', (string) $q); + } + + public function testQueryStringsAllowDecodingEncodingCompletelyDisabled() + { + $q = Query::fromString('foo=bar%2Fbaz&bam=boo boo!', false); + $this->assertEquals('foo=bar%2Fbaz&bam=boo boo!', (string) $q); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php new file mode 100755 index 000000000..dd6768405 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php @@ -0,0 +1,187 @@ +mf = new MessageFactory(); + } + + public function testEmitsBeforeEventInTransition() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); + $c = false; + $t->request->getEmitter()->on('before', function (BeforeEvent $e) use (&$c) { + $c = true; + }); + $fsm($t); + $this->assertTrue($c); + } + + public function testEmitsCompleteEventInTransition() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); + $t->response = new Response(200); + $t->state = 'complete'; + $c = false; + $t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) { + $c = true; + }); + $fsm($t); + $this->assertTrue($c); + } + + public function testDoesNotEmitCompleteForFuture() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); + $deferred = new Deferred(); + $t->response = new FutureResponse($deferred->promise()); + $t->state = 'complete'; + $c = false; + $t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) { + $c = true; + }); + $fsm($t); + $this->assertFalse($c); + } + + public function testTransitionsThroughSuccessfulTransfer() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $this->addListeners($request, $calls); + $client->send($request); + $this->assertEquals(['before', 'complete', 'end'], $calls); + } + + public function testTransitionsThroughErrorsInBefore() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 200]); + }, $this->mf); + $client = new Client(); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $t = new Transaction($client, $request); + $calls = []; + $this->addListeners($t->request, $calls); + $t->request->getEmitter()->on('before', function (BeforeEvent $e) { + throw new \Exception('foo'); + }); + try { + $fsm($t); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertContains('foo', $t->exception->getMessage()); + $this->assertEquals(['before', 'error', 'end'], $calls); + } + } + + public function testTransitionsThroughErrorsInComplete() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $this->addListeners($request, $calls); + $request->getEmitter()->once('complete', function (CompleteEvent $e) { + throw new \Exception('foo'); + }); + try { + $client->send($request); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertContains('foo', $e->getMessage()); + $this->assertEquals(['before', 'complete', 'error', 'end'], $calls); + } + } + + public function testTransitionsThroughErrorInterception() + { + $fsm = new RequestFsm(function () { + return new CompletedFutureArray(['status' => 404]); + }, $this->mf); + $client = new Client(); + $request = $client->createRequest('GET', 'http://ewfewwef.com'); + $t = new Transaction($client, $request); + $calls = []; + $this->addListeners($t->request, $calls); + $t->request->getEmitter()->on('error', function (ErrorEvent $e) { + $e->intercept(new Response(200)); + }); + $fsm($t); + $this->assertEquals(200, $t->response->getStatusCode()); + $this->assertNull($t->exception); + $this->assertEquals(['before', 'complete', 'error', 'complete', 'end'], $calls); + } + + private function addListeners(RequestInterface $request, &$calls) + { + $request->getEmitter()->on('before', function (BeforeEvent $e) use (&$calls) { + $calls[] = 'before'; + }, RequestEvents::EARLY); + $request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$calls) { + $calls[] = 'complete'; + }, RequestEvents::EARLY); + $request->getEmitter()->on('error', function (ErrorEvent $e) use (&$calls) { + $calls[] = 'error'; + }, RequestEvents::EARLY); + $request->getEmitter()->on('end', function (EndEvent $e) use (&$calls) { + $calls[] = 'end'; + }, RequestEvents::EARLY); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage Too many state transitions + */ + public function testDetectsInfiniteLoops() + { + $client = new Client([ + 'fsm' => $fsm = new RequestFsm( + function () { + return new CompletedFutureArray(['status' => 200]); + }, + new MessageFactory(), + 3 + ) + ]); + $request = $client->createRequest('GET', 'http://foo.com:123'); + $request->getEmitter()->on('before', function () { + throw new \Exception('foo'); + }); + $request->getEmitter()->on('error', function ($e) { + $e->retry(); + }); + $client->send($request); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php new file mode 100755 index 000000000..dc26a42aa --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php @@ -0,0 +1,195 @@ + 'hello' + ], $stream); + $request->getConfig()->set('foo', 'bar'); + $trans = new Transaction(new Client(), $request); + $factory = new MessageFactory(); + $fsm = new RequestFsm(function () {}, new MessageFactory()); + $r = RingBridge::prepareRingRequest($trans, $factory, $fsm); + $this->assertEquals('http', $r['scheme']); + $this->assertEquals('1.1', $r['version']); + $this->assertEquals('GET', $r['http_method']); + $this->assertEquals('http://httpbin.org/get?a=b', $r['url']); + $this->assertEquals('/get', $r['uri']); + $this->assertEquals('a=b', $r['query_string']); + $this->assertEquals([ + 'Host' => ['httpbin.org'], + 'test' => ['hello'] + ], $r['headers']); + $this->assertSame($stream, $r['body']); + $this->assertEquals(['foo' => 'bar'], $r['client']); + $this->assertFalse($r['future']); + } + + public function testCreatesRingRequestsWithNullQueryString() + { + $request = new Request('GET', 'http://httpbin.org'); + $trans = new Transaction(new Client(), $request); + $factory = new MessageFactory(); + $fsm = new RequestFsm(function () {}, new MessageFactory()); + $r = RingBridge::prepareRingRequest($trans, $factory, $fsm); + $this->assertNull($r['query_string']); + $this->assertEquals('/', $r['uri']); + $this->assertEquals(['Host' => ['httpbin.org']], $r['headers']); + $this->assertNull($r['body']); + $this->assertEquals([], $r['client']); + } + + public function testAddsProgress() + { + Server::enqueue([new Response(200)]); + $client = new Client(['base_url' => Server::$url]); + $request = $client->createRequest('GET'); + $called = false; + $request->getEmitter()->on( + 'progress', + function (ProgressEvent $e) use (&$called) { + $called = true; + } + ); + $this->assertEquals(200, $client->send($request)->getStatusCode()); + $this->assertTrue($called); + } + + public function testGetsResponseProtocolVersionAndEffectiveUrlAndReason() + { + $client = new Client([ + 'handler' => new MockHandler([ + 'status' => 200, + 'reason' => 'test', + 'headers' => [], + 'version' => '1.0', + 'effective_url' => 'http://foo.com' + ]) + ]); + $request = $client->createRequest('GET', 'http://foo.com'); + $response = $client->send($request); + $this->assertEquals('1.0', $response->getProtocolVersion()); + $this->assertEquals('http://foo.com', $response->getEffectiveUrl()); + $this->assertEquals('test', $response->getReasonPhrase()); + } + + public function testGetsStreamFromResponse() + { + $res = fopen('php://temp', 'r+'); + fwrite($res, 'foo'); + rewind($res); + $client = new Client([ + 'handler' => new MockHandler([ + 'status' => 200, + 'headers' => [], + 'body' => $res + ]) + ]); + $request = $client->createRequest('GET', 'http://foo.com'); + $response = $client->send($request); + $this->assertEquals('foo', (string) $response->getBody()); + } + + public function testEmitsErrorEventOnError() + { + $client = new Client(['base_url' => 'http://127.0.0.1:123']); + $request = $client->createRequest('GET'); + $called = false; + $request->getEmitter()->on('error', function () use (&$called) { + $called = true; + }); + $request->getConfig()['timeout'] = 0.001; + $request->getConfig()['connect_timeout'] = 0.001; + try { + $client->send($request); + $this->fail('did not throw'); + } catch (RequestException $e) { + $this->assertSame($request, $e->getRequest()); + $this->assertContains('cURL error', $e->getMessage()); + $this->assertTrue($called); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesRingRequest() + { + RingBridge::fromRingRequest([]); + } + + public function testCreatesRequestFromRing() + { + $request = RingBridge::fromRingRequest([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => [ + 'foo' => ['bar'], + 'host' => ['foo.com'] + ], + 'body' => 'test', + 'version' => '1.0' + ]); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http://foo.com/', $request->getUrl()); + $this->assertEquals('1.0', $request->getProtocolVersion()); + $this->assertEquals('test', (string) $request->getBody()); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testCanInterceptException() + { + $client = new Client(['base_url' => 'http://127.0.0.1:123']); + $request = $client->createRequest('GET'); + $called = false; + $request->getEmitter()->on( + 'error', + function (ErrorEvent $e) use (&$called) { + $called = true; + $e->intercept(new Response(200)); + } + ); + $request->getConfig()['timeout'] = 0.001; + $request->getConfig()['connect_timeout'] = 0.001; + $this->assertEquals(200, $client->send($request)->getStatusCode()); + $this->assertTrue($called); + } + + public function testCreatesLongException() + { + $r = new Request('GET', 'http://www.google.com'); + $e = RingBridge::getNoRingResponseException($r); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + $this->assertSame($r, $e->getRequest()); + } + + public function testEnsuresResponseOrExceptionWhenCompletingResponse() + { + $trans = new Transaction(new Client(), new Request('GET', 'http://f.co')); + $f = new MessageFactory(); + $fsm = new RequestFsm(function () {}, new MessageFactory()); + try { + RingBridge::completeRingResponse($trans, [], $f, $fsm); + } catch (RequestException $e) { + $this->assertSame($trans->request, $e->getRequest()); + $this->assertContains('RingPHP', $e->getMessage()); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Server.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Server.php new file mode 100755 index 000000000..1de20e38b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Server.php @@ -0,0 +1,107 @@ +fromMessage($response); + } elseif (!($response instanceof ResponseInterface)) { + throw new \Exception('Responses must be strings or Responses'); + } + $data[] = self::convertResponse($response); + } + + TestServer::enqueue($data); + } + + /** + * Get all of the received requests + * + * @param bool $hydrate Set to TRUE to turn the messages into + * actual {@see RequestInterface} objects. If $hydrate is FALSE, + * requests will be returned as strings. + * + * @return array + * @throws \RuntimeException + */ + public static function received($hydrate = false) + { + $response = TestServer::received(); + + if ($hydrate) { + $c = new Client(); + $factory = new MessageFactory(); + $response = array_map(function($message) use ($factory, $c) { + return RingBridge::fromRingRequest($message); + }, $response); + } + + return $response; + } + + public static function flush() + { + TestServer::flush(); + } + + public static function stop() + { + TestServer::stop(); + } + + public static function wait($maxTries = 5) + { + TestServer::wait($maxTries); + } + + public static function start() + { + TestServer::start(); + } + + private static function convertResponse(Response $response) + { + $headers = array_map(function ($h) { + return implode(', ', $h); + }, $response->getHeaders()); + + return [ + 'status' => $response->getStatusCode(), + 'reason' => $response->getReasonPhrase(), + 'headers' => $headers, + 'body' => base64_encode((string) $response->getBody()) + ]; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php new file mode 100755 index 000000000..bc17e2dc2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php @@ -0,0 +1,74 @@ +getMockBuilder('GuzzleHttp\Cookie\CookieJar') + ->setMethods(array('extractCookies')) + ->getMock(); + + $mock->expects($this->exactly(1)) + ->method('extractCookies') + ->with($request, $response); + + $plugin = new Cookie($mock); + $t = new Transaction(new Client(), $request); + $t->response = $response; + $plugin->onComplete(new CompleteEvent($t)); + } + + public function testProvidesCookieJar() + { + $jar = new CookieJar(); + $plugin = new Cookie($jar); + $this->assertSame($jar, $plugin->getCookieJar()); + } + + public function testCookiesAreExtractedFromRedirectResponses() + { + $jar = new CookieJar(); + $cookie = new Cookie($jar); + $history = new History(); + $mock = new Mock([ + "HTTP/1.1 302 Moved Temporarily\r\n" . + "Set-Cookie: test=583551; Domain=www.foo.com; Expires=Wednesday, 23-Mar-2050 19:49:45 GMT; Path=/\r\n" . + "Location: /redirect\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n" + ]); + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach($cookie); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($history); + + $client->get(); + $request = $client->createRequest('GET', '/'); + $client->send($request); + + $this->assertEquals('test=583551', $request->getHeader('Cookie')); + $requests = $history->getRequests(); + // Confirm subsequent requests have the cookie. + $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); + // Confirm the redirected request has the cookie. + $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php new file mode 100755 index 000000000..d28e301cd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php @@ -0,0 +1,140 @@ +response = $response; + $e = new RequestException('foo', $request, $response); + $ev = new ErrorEvent($t, $e); + $h = new History(2); + $h->onError($ev); + // Only tracks when no response is present + $this->assertEquals([], $h->getRequests()); + } + + public function testLogsConnectionErrors() + { + $request = new Request('GET', '/'); + $t = new Transaction(new Client(), $request); + $e = new RequestException('foo', $request); + $ev = new ErrorEvent($t, $e); + $h = new History(); + $h->onError($ev); + $this->assertEquals([$request], $h->getRequests()); + } + + public function testMaintainsLimitValue() + { + $request = new Request('GET', '/'); + $response = new Response(200); + $t = new Transaction(new Client(), $request); + $t->response = $response; + $ev = new CompleteEvent($t); + $h = new History(2); + $h->onComplete($ev); + $h->onComplete($ev); + $h->onComplete($ev); + $this->assertEquals(2, count($h)); + $this->assertSame($request, $h->getLastRequest()); + $this->assertSame($response, $h->getLastResponse()); + foreach ($h as $trans) { + $this->assertInstanceOf('GuzzleHttp\Message\RequestInterface', $trans['request']); + $this->assertInstanceOf('GuzzleHttp\Message\ResponseInterface', $trans['response']); + } + return $h; + } + + /** + * @depends testMaintainsLimitValue + */ + public function testClearsHistory($h) + { + $this->assertEquals(2, count($h)); + $h->clear(); + $this->assertEquals(0, count($h)); + } + + public function testWorksWithMock() + { + $client = new Client(['base_url' => 'http://localhost/']); + $h = new History(); + $client->getEmitter()->attach($h); + $mock = new Mock([new Response(200), new Response(201), new Response(202)]); + $client->getEmitter()->attach($mock); + $request = $client->createRequest('GET', '/'); + $client->send($request); + $request->setMethod('PUT'); + $client->send($request); + $request->setMethod('POST'); + $client->send($request); + $this->assertEquals(3, count($h)); + + $result = implode("\n", array_map(function ($line) { + return strpos($line, 'User-Agent') === 0 + ? 'User-Agent:' + : trim($line); + }, explode("\n", $h))); + + $this->assertEquals("> GET / HTTP/1.1 +Host: localhost +User-Agent: + +< HTTP/1.1 200 OK + +> PUT / HTTP/1.1 +Host: localhost +User-Agent: + +< HTTP/1.1 201 Created + +> POST / HTTP/1.1 +Host: localhost +User-Agent: + +< HTTP/1.1 202 Accepted +", $result); + } + + public function testCanCastToString() + { + $client = new Client(['base_url' => 'http://localhost/']); + $h = new History(); + $client->getEmitter()->attach($h); + + $mock = new Mock(array( + new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), + new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), + new Response(200, array('Content-Length' => '2'), Stream::factory('HI')) + )); + + $client->getEmitter()->attach($mock); + $request = $client->createRequest('GET', '/'); + $client->send($request); + $this->assertEquals(3, count($h)); + + $h = str_replace("\r", '', $h); + $this->assertContains("> GET / HTTP/1.1\nHost: localhost\nUser-Agent:", $h); + $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); + $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); + $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php new file mode 100755 index 000000000..b0266340c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php @@ -0,0 +1,60 @@ +getEvent(); + $event->intercept(new Response(200)); + (new HttpError())->onComplete($event); + } + + /** + * @expectedException \GuzzleHttp\Exception\ClientException + */ + public function testThrowsClientExceptionOnFailure() + { + $event = $this->getEvent(); + $event->intercept(new Response(403)); + (new HttpError())->onComplete($event); + } + + /** + * @expectedException \GuzzleHttp\Exception\ServerException + */ + public function testThrowsServerExceptionOnFailure() + { + $event = $this->getEvent(); + $event->intercept(new Response(500)); + (new HttpError())->onComplete($event); + } + + private function getEvent() + { + return new CompleteEvent(new Transaction(new Client(), new Request('PUT', '/'))); + } + + /** + * @expectedException \GuzzleHttp\Exception\ClientException + */ + public function testFullTransaction() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(403) + ])); + $client->get('http://httpbin.org'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php new file mode 100755 index 000000000..936edf26f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php @@ -0,0 +1,225 @@ +promise(), + function () use ($deferred, $wait) { + $deferred->resolve($wait()); + }, + $cancel + ); + } + + public function testDescribesSubscribedEvents() + { + $mock = new Mock(); + $this->assertInternalType('array', $mock->getEvents()); + } + + public function testIsCountable() + { + $plugin = new Mock(); + $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $this->assertEquals(1, count($plugin)); + } + + public function testCanClearQueue() + { + $plugin = new Mock(); + $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $plugin->clearQueue(); + $this->assertEquals(0, count($plugin)); + } + + public function testRetrievesResponsesFromFiles() + { + $tmp = tempnam('/tmp', 'tfile'); + file_put_contents($tmp, "HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n"); + $plugin = new Mock(); + $plugin->addResponse($tmp); + unlink($tmp); + $this->assertEquals(1, count($plugin)); + $q = $this->readAttribute($plugin, 'queue'); + $this->assertEquals(201, $q[0]->getStatusCode()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidResponse() + { + (new Mock())->addResponse(false); + } + + public function testAddsMockResponseToRequestFromClient() + { + $response = new Response(200); + $t = new Transaction(new Client(), new Request('GET', '/')); + $m = new Mock([$response]); + $ev = new BeforeEvent($t); + $m->onBefore($ev); + $this->assertSame($response, $t->response); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testUpdateThrowsExceptionWhenEmpty() + { + $p = new Mock(); + $ev = new BeforeEvent(new Transaction(new Client(), new Request('GET', '/'))); + $p->onBefore($ev); + } + + public function testReadsBodiesFromMockedRequests() + { + $m = new Mock([new Response(200)]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($m); + $body = Stream::factory('foo'); + $client->put('/', ['body' => $body]); + $this->assertEquals(3, $body->tell()); + } + + public function testCanMockBadRequestExceptions() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/'); + $ex = new RequestException('foo', $request); + $mock = new Mock([$ex]); + $this->assertCount(1, $mock); + $request->getEmitter()->attach($mock); + + try { + $client->send($request); + $this->fail('Did not dequeue an exception'); + } catch (RequestException $e) { + $this->assertSame($e, $ex); + $this->assertSame($request, $ex->getRequest()); + } + } + + public function testCanMockFutureResponses() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/', ['future' => true]); + $response = new Response(200); + $future = self::createFuture(function () use ($response) { + return $response; + }); + $mock = new Mock([$future]); + $this->assertCount(1, $mock); + $request->getEmitter()->attach($mock); + $res = $client->send($request); + $this->assertSame($future, $res); + $this->assertFalse($this->readAttribute($res, 'isRealized')); + $this->assertSame($response, $res->wait()); + } + + public function testCanMockExceptionFutureResponses() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/', ['future' => true]); + $future = self::createFuture(function () use ($request) { + throw new RequestException('foo', $request); + }); + + $mock = new Mock([$future]); + $request->getEmitter()->attach($mock); + $response = $client->send($request); + $this->assertSame($future, $response); + $this->assertFalse($this->readAttribute($response, 'isRealized')); + + try { + $response->wait(); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertContains('foo', $e->getMessage()); + } + } + + public function testSaveToFile() + { + $filename = sys_get_temp_dir().'/mock_test_'.uniqid(); + $file = tmpfile(); + $stream = new Stream(tmpfile()); + + $m = new Mock([ + new Response(200, [], Stream::factory('TEST FILENAME')), + new Response(200, [], Stream::factory('TEST FILE')), + new Response(200, [], Stream::factory('TEST STREAM')), + ]); + + $client = new Client(); + $client->getEmitter()->attach($m); + + $client->get('/', ['save_to' => $filename]); + $client->get('/', ['save_to' => $file]); + $client->get('/', ['save_to' => $stream]); + + $this->assertFileExists($filename); + $this->assertEquals('TEST FILENAME', file_get_contents($filename)); + + $meta = stream_get_meta_data($file); + + $this->assertFileExists($meta['uri']); + $this->assertEquals('TEST FILE', file_get_contents($meta['uri'])); + + $this->assertFileExists($stream->getMetadata('uri')); + $this->assertEquals('TEST STREAM', file_get_contents($stream->getMetadata('uri'))); + + unlink($filename); + } + + public function testCanMockFailedFutureResponses() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/', ['future' => true]); + + // The first mock will be a mocked future response. + $future = self::createFuture(function () use ($client) { + // When dereferenced, we will set a mocked response and send + // another request. + $client->get('http://httpbin.org', ['events' => [ + 'before' => function (BeforeEvent $e) { + $e->intercept(new Response(404)); + } + ]]); + }); + + $mock = new Mock([$future]); + $request->getEmitter()->attach($mock); + $response = $client->send($request); + $this->assertSame($future, $response); + $this->assertFalse($this->readAttribute($response, 'isRealized')); + + try { + $response->wait(); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertEquals(404, $e->getResponse()->getStatusCode()); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php new file mode 100755 index 000000000..d07fdb44c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php @@ -0,0 +1,213 @@ +getTrans(); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Expect')); + } + + public function testAppliesPostBody() + { + $s = new Prepare(); + $t = $this->getTrans(); + $p = $this->getMockBuilder('GuzzleHttp\Post\PostBody') + ->setMethods(['applyRequestHeaders']) + ->getMockForAbstractClass(); + $p->expects($this->once()) + ->method('applyRequestHeaders'); + $t->request->setBody($p); + $s->onBefore(new BeforeEvent($t)); + } + + public function testAddsExpectHeaderWithTrue() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', true); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('100-Continue', $t->request->getHeader('Expect')); + } + + public function testAddsExpectHeaderBySize() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', 2); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertTrue($t->request->hasHeader('Expect')); + } + + public function testDoesNotModifyExpectHeaderIfPresent() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setHeader('Expect', 'foo'); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('foo', $t->request->getHeader('Expect')); + } + + public function testDoesAddExpectHeaderWhenSetToFalse() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', false); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Expect')); + } + + public function testDoesNotAddExpectHeaderBySize() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->getConfig()->set('expect', 10); + $t->request->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Expect')); + } + + public function testAddsExpectHeaderForNonSeekable() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody(new NoSeekStream(Stream::factory('foo'))); + $s->onBefore(new BeforeEvent($t)); + $this->assertTrue($t->request->hasHeader('Expect')); + } + + public function testRemovesContentLengthWhenSendingWithChunked() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody(Stream::factory('foo')); + $t->request->setHeader('Transfer-Encoding', 'chunked'); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->request->hasHeader('Content-Length')); + } + + public function testUsesProvidedContentLengthAndRemovesXferEncoding() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody(Stream::factory('foo')); + $t->request->setHeader('Content-Length', '3'); + $t->request->setHeader('Transfer-Encoding', 'chunked'); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals(3, $t->request->getHeader('Content-Length')); + $this->assertFalse($t->request->hasHeader('Transfer-Encoding')); + } + + public function testSetsContentTypeIfPossibleFromStream() + { + $body = $this->getMockBody(); + $sub = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody($body); + $sub->onBefore(new BeforeEvent($t)); + $this->assertEquals( + 'image/jpeg', + $t->request->getHeader('Content-Type') + ); + $this->assertEquals(4, $t->request->getHeader('Content-Length')); + } + + public function testDoesNotOverwriteExistingContentType() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody($this->getMockBody()); + $t->request->setHeader('Content-Type', 'foo/baz'); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals( + 'foo/baz', + $t->request->getHeader('Content-Type') + ); + } + + public function testSetsContentLengthIfPossible() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->request->setBody($this->getMockBody()); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals(4, $t->request->getHeader('Content-Length')); + } + + public function testSetsTransferEncodingChunkedIfNeeded() + { + $r = new Request('PUT', '/'); + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['getSize']) + ->getMockForAbstractClass(); + $s->expects($this->exactly(2)) + ->method('getSize') + ->will($this->returnValue(null)); + $r->setBody($s); + $t = $this->getTrans($r); + $s = new Prepare(); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('chunked', $r->getHeader('Transfer-Encoding')); + } + + public function testContentLengthIntegrationTest() + { + Server::flush(); + Server::enqueue([new Response(200)]); + $client = new Client(['base_url' => Server::$url]); + $this->assertEquals(200, $client->put('/', [ + 'body' => 'test' + ])->getStatusCode()); + $request = Server::received(true)[0]; + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('4', $request->getHeader('Content-Length')); + $this->assertEquals('test', (string) $request->getBody()); + } + + private function getTrans($request = null) + { + return new Transaction( + new Client(), + $request ?: new Request('PUT', '/') + ); + } + + /** + * @return \GuzzleHttp\Stream\StreamInterface + */ + private function getMockBody() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\MetadataStreamInterface') + ->setMethods(['getMetadata', 'getSize']) + ->getMockForAbstractClass(); + $s->expects($this->any()) + ->method('getMetadata') + ->with('uri') + ->will($this->returnValue('/foo/baz/bar.jpg')); + $s->expects($this->exactly(2)) + ->method('getSize') + ->will($this->returnValue(4)); + + return $s; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php new file mode 100755 index 000000000..bd12af758 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php @@ -0,0 +1,302 @@ +addMultiple([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($history); + $client->getEmitter()->attach($mock); + + $request = $client->createRequest('GET', '/foo'); + // Ensure "end" is called only once + $called = 0; + $request->getEmitter()->on('end', function () use (&$called) { + $called++; + }); + $response = $client->send($request); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + // Ensure that two requests were sent + $requests = $history->getRequests(true); + + $this->assertEquals('/foo', $requests[0]->getPath()); + $this->assertEquals('GET', $requests[0]->getMethod()); + $this->assertEquals('/redirect1', $requests[1]->getPath()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('/redirect2', $requests[2]->getPath()); + $this->assertEquals('GET', $requests[2]->getMethod()); + + $this->assertEquals(1, $called); + } + + /** + * @expectedException \GuzzleHttp\Exception\TooManyRedirectsException + * @expectedExceptionMessage Will not follow more than + */ + public function testCanLimitNumberOfRedirects() + { + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->get('http://www.example.com/foo'); + } + + public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('http://test.com/foo', [ + 'headers' => ['X-Baz' => 'bar'], + 'body' => 'testing' + ]); + + $requests = $h->getRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('GET', $requests[2]->getMethod()); + } + + public function testCanRedirectWithStrictRfcCompliance() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('/foo', [ + 'headers' => ['X-Baz' => 'bar'], + 'body' => 'testing', + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + + $requests = $h->getRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('POST', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('POST', $requests[2]->getMethod()); + } + + public function testRewindsStreamWhenRedirectingIfNeeded() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + + $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['seek', 'read', 'eof', 'tell']) + ->getMockForAbstractClass(); + $body->expects($this->once())->method('tell')->will($this->returnValue(1)); + $body->expects($this->once())->method('seek')->will($this->returnValue(true)); + $body->expects($this->any())->method('eof')->will($this->returnValue(true)); + $body->expects($this->any())->method('read')->will($this->returnValue('foo')); + $client->post('/foo', [ + 'body' => $body, + 'allow_redirects' => ['max' => 5, 'strict' => true] + ]); + } + + /** + * @expectedException \GuzzleHttp\Exception\CouldNotRewindStreamException + * @expectedExceptionMessage Unable to rewind the non-seekable request body after redirecting + */ + public function testThrowsExceptionWhenStreamCannotBeRewound() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + + $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['seek', 'read', 'eof', 'tell']) + ->getMockForAbstractClass(); + $body->expects($this->once())->method('tell')->will($this->returnValue(1)); + $body->expects($this->once())->method('seek')->will($this->returnValue(false)); + $body->expects($this->any())->method('eof')->will($this->returnValue(true)); + $body->expects($this->any())->method('read')->will($this->returnValue('foo')); + $client->post('http://example.com/foo', [ + 'body' => $body, + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + } + + public function testRedirectsCanBeDisabledPerRequest() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ])); + $response = $client->put('/', ['body' => 'test', 'allow_redirects' => false]); + $this->assertEquals(301, $response->getStatusCode()); + } + + public function testCanRedirectWithNoLeadingSlashAndQuery() + { + $h = new History(); + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect?foo=bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ])); + $client->getEmitter()->attach($h); + $client->get('?foo=bar'); + $requests = $h->getRequests(true); + $this->assertEquals('http://www.foo.com?foo=bar', $requests[0]->getUrl()); + $this->assertEquals('http://www.foo.com/redirect?foo=bar', $requests[1]->getUrl()); + } + + public function testHandlesRedirectsWithSpacesProperly() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo'); + $reqs = $h->getRequests(true); + $this->assertEquals('/redirect%201', $reqs[1]->getResource()); + } + + public function testAddsRefererWhenPossible() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); + $reqs = $h->getRequests(true); + $this->assertEquals('http://www.foo.com/foo', $reqs[1]->getHeader('Referer')); + } + + public function testDoesNotAddRefererWhenChangingProtocols() + { + $client = new Client(['base_url' => 'https://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\n" + . "Location: http://www.foo.com/foo\r\n" + . "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); + $reqs = $h->getRequests(true); + $this->assertFalse($reqs[1]->hasHeader('Referer')); + } + + public function testRedirectsWithGetOn303() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('http://test.com/foo', ['body' => 'testing']); + $requests = $h->getRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRelativeLinkBasedLatestRequest() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: http://www.bar.com\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $response = $client->get('/'); + $this->assertEquals( + 'http://www.bar.com/redirect', + $response->getEffectiveUrl() + ); + } + + public function testUpperCaseScheme() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: HTTP://www.bar.com\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $response = $client->get('/'); + $this->assertEquals( + 'http://www.bar.com', + $response->getEffectiveUrl() + ); + } + + /** + * @expectedException \GuzzleHttp\Exception\BadResponseException + * @expectedExceptionMessage Redirect URL, https://foo.com/redirect2, does not use one of the allowed redirect protocols: http + */ + public function testThrowsWhenRedirectingToInvalidUrlProtocol() + { + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: https://foo.com/redirect2\r\nContent-Length: 0\r\n\r\n" + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->get('http://www.example.com/foo', [ + 'allow_redirects' => [ + 'protocols' => ['http'] + ] + ]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/TransactionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/TransactionTest.php new file mode 100755 index 000000000..42965b1b5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/TransactionTest.php @@ -0,0 +1,22 @@ +assertSame($client, $t->client); + $this->assertSame($request, $t->request); + $response = new Response(200); + $t->response = $response; + $this->assertSame($response, $t->response); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php new file mode 100755 index 000000000..3f7a7f063 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php @@ -0,0 +1,202 @@ + 'value', + 'hello' => 'Hello World!', + 'empty' => '', + 'path' => '/foo/bar', + 'x' => '1024', + 'y' => '768', + 'null' => null, + 'list' => array('red', 'green', 'blue'), + 'keys' => array( + "semi" => ';', + "dot" => '.', + "comma" => ',' + ), + 'empty_keys' => array(), + ); + + return array_map(function ($t) use ($params) { + $t[] = $params; + return $t; + }, array( + array('foo', 'foo'), + array('{var}', 'value'), + array('{hello}', 'Hello%20World%21'), + array('{+var}', 'value'), + array('{+hello}', 'Hello%20World!'), + array('{+path}/here', '/foo/bar/here'), + array('here?ref={+path}', 'here?ref=/foo/bar'), + array('X{#var}', 'X#value'), + array('X{#hello}', 'X#Hello%20World!'), + array('map?{x,y}', 'map?1024,768'), + array('{x,hello,y}', '1024,Hello%20World%21,768'), + array('{+x,hello,y}', '1024,Hello%20World!,768'), + array('{+path,x}/here', '/foo/bar,1024/here'), + array('{#x,hello,y}', '#1024,Hello%20World!,768'), + array('{#path,x}/here', '#/foo/bar,1024/here'), + array('X{.var}', 'X.value'), + array('X{.x,y}', 'X.1024.768'), + array('{/var}', '/value'), + array('{/var,x}/here', '/value/1024/here'), + array('{;x,y}', ';x=1024;y=768'), + array('{;x,y,empty}', ';x=1024;y=768;empty'), + array('{?x,y}', '?x=1024&y=768'), + array('{?x,y,empty}', '?x=1024&y=768&empty='), + array('?fixed=yes{&x}', '?fixed=yes&x=1024'), + array('{&x,y,empty}', '&x=1024&y=768&empty='), + array('{var:3}', 'val'), + array('{var:30}', 'value'), + array('{list}', 'red,green,blue'), + array('{list*}', 'red,green,blue'), + array('{keys}', 'semi,%3B,dot,.,comma,%2C'), + array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), + array('{+path:6}/here', '/foo/b/here'), + array('{+list}', 'red,green,blue'), + array('{+list*}', 'red,green,blue'), + array('{+keys}', 'semi,;,dot,.,comma,,'), + array('{+keys*}', 'semi=;,dot=.,comma=,'), + array('{#path:6}/here', '#/foo/b/here'), + array('{#list}', '#red,green,blue'), + array('{#list*}', '#red,green,blue'), + array('{#keys}', '#semi,;,dot,.,comma,,'), + array('{#keys*}', '#semi=;,dot=.,comma=,'), + array('X{.var:3}', 'X.val'), + array('X{.list}', 'X.red,green,blue'), + array('X{.list*}', 'X.red.green.blue'), + array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), + array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), + array('{/var:1,var}', '/v/value'), + array('{/list}', '/red,green,blue'), + array('{/list*}', '/red/green/blue'), + array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), + array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), + array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), + array('{;hello:5}', ';hello=Hello'), + array('{;list}', ';list=red,green,blue'), + array('{;list*}', ';list=red;list=green;list=blue'), + array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), + array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), + array('{?var:3}', '?var=val'), + array('{?list}', '?list=red,green,blue'), + array('{?list*}', '?list=red&list=green&list=blue'), + array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), + array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), + array('{&var:3}', '&var=val'), + array('{&list}', '&list=red,green,blue'), + array('{&list*}', '&list=red&list=green&list=blue'), + array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), + array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), + array('{.null}', ''), + array('{.null,var}', '.value'), + array('X{.empty_keys*}', 'X'), + array('X{.empty_keys}', 'X'), + // Test that missing expansions are skipped + array('test{&missing*}', 'test'), + // Test that multiple expansions can be set + array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + // Test more complex query string stuff + array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') + )); + } + + /** + * @dataProvider templateProvider + */ + public function testExpandsUriTemplates($template, $expansion, $params) + { + $uri = new UriTemplate($template); + $this->assertEquals($expansion, $uri->expand($template, $params)); + } + + public function expressionProvider() + { + return array( + array( + '{+var*}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'var', 'modifier' => '*') + ) + ), + ), + array( + '{?keys,var,val}', array( + 'operator' => '?', + 'values' => array( + array('value' => 'keys', 'modifier' => ''), + array('value' => 'var', 'modifier' => ''), + array('value' => 'val', 'modifier' => '') + ) + ), + ), + array( + '{+x,hello,y}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'x', 'modifier' => ''), + array('value' => 'hello', 'modifier' => ''), + array('value' => 'y', 'modifier' => '') + ) + ) + ) + ); + } + + /** + * @dataProvider expressionProvider + */ + public function testParsesExpressions($exp, $data) + { + $template = new UriTemplate($exp); + + // Access the config object + $class = new \ReflectionClass($template); + $method = $class->getMethod('parseExpression'); + $method->setAccessible(true); + + $exp = substr($exp, 1, -1); + $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/90 + */ + public function testAllowsNestedArrayExpansion() + { + $template = new UriTemplate(); + + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => array('fun', 'ice cream') + ), + 'foo' => array( + 'baz' => array( + 'bar' => 'fizz', + 'test' => 'buzz' + ), + 'bam' => 'boo' + ) + )); + + $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UrlTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UrlTest.php new file mode 100755 index 000000000..22bf7e495 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UrlTest.php @@ -0,0 +1,364 @@ +assertEquals('', (string) $url); + } + + public function testPortIsDeterminedFromScheme() + { + $this->assertEquals(80, Url::fromString('http://www.test.com/')->getPort()); + $this->assertEquals(443, Url::fromString('https://www.test.com/')->getPort()); + $this->assertEquals(21, Url::fromString('ftp://www.test.com/')->getPort()); + $this->assertEquals(8192, Url::fromString('http://www.test.com:8192/')->getPort()); + $this->assertEquals(null, Url::fromString('foo://www.test.com/')->getPort()); + } + + public function testRemovesDefaultPortWhenSettingScheme() + { + $url = Url::fromString('http://www.test.com/'); + $url->setPort(80); + $url->setScheme('https'); + $this->assertEquals(443, $url->getPort()); + } + + public function testCloneCreatesNewInternalObjects() + { + $u1 = Url::fromString('http://www.test.com/'); + $u2 = clone $u1; + $this->assertNotSame($u1->getQuery(), $u2->getQuery()); + } + + public function testValidatesUrlPartsInFactory() + { + $url = Url::fromString('/index.php'); + $this->assertEquals('/index.php', (string) $url); + $this->assertFalse($url->isAbsolute()); + + $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; + $u = Url::fromString($url); + $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); + $this->assertTrue($u->isAbsolute()); + } + + public function testAllowsFalsyUrlParts() + { + $url = Url::fromString('http://a:50/0?0#0'); + $this->assertSame('a', $url->getHost()); + $this->assertEquals(50, $url->getPort()); + $this->assertSame('/0', $url->getPath()); + $this->assertEquals('0', (string) $url->getQuery()); + $this->assertSame('0', $url->getFragment()); + $this->assertEquals('http://a:50/0?0#0', (string) $url); + + $url = Url::fromString(''); + $this->assertSame('', (string) $url); + + $url = Url::fromString('0'); + $this->assertSame('0', (string) $url); + } + + public function testBuildsRelativeUrlsWithFalsyParts() + { + $url = Url::buildUrl(['path' => '/0']); + $this->assertSame('/0', $url); + + $url = Url::buildUrl(['path' => '0']); + $this->assertSame('0', $url); + + $url = Url::buildUrl(['host' => '', 'path' => '0']); + $this->assertSame('0', $url); + } + + public function testUrlStoresParts() + { + $url = Url::fromString('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); + $this->assertEquals('http', $url->getScheme()); + $this->assertEquals('test', $url->getUsername()); + $this->assertEquals('pass', $url->getPassword()); + $this->assertEquals('www.test.com', $url->getHost()); + $this->assertEquals(8081, $url->getPort()); + $this->assertEquals('/path/path2/', $url->getPath()); + $this->assertEquals('fragment', $url->getFragment()); + $this->assertEquals('a=1&b=2', (string) $url->getQuery()); + + $this->assertEquals(array( + 'fragment' => 'fragment', + 'host' => 'www.test.com', + 'pass' => 'pass', + 'path' => '/path/path2/', + 'port' => 8081, + 'query' => 'a=1&b=2', + 'scheme' => 'http', + 'user' => 'test' + ), $url->getParts()); + } + + public function testHandlesPathsCorrectly() + { + $url = Url::fromString('http://www.test.com'); + $this->assertEquals('', $url->getPath()); + $url->setPath('test'); + $this->assertEquals('test', $url->getPath()); + + $url->setPath('/test/123/abc'); + $this->assertEquals(array('', 'test', '123', 'abc'), $url->getPathSegments()); + + $parts = parse_url('http://www.test.com/test'); + $parts['path'] = ''; + $this->assertEquals('http://www.test.com', Url::buildUrl($parts)); + $parts['path'] = 'test'; + $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); + } + + public function testAddsQueryIfPresent() + { + $this->assertEquals('?foo=bar', Url::buildUrl(array( + 'query' => 'foo=bar' + ))); + } + + public function testAddsToPath() + { + // Does nothing here + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath(false); + $this->assertEquals('http://e.com/base?a=1', $url); + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath(''); + $this->assertEquals('http://e.com/base?a=1', $url); + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath('/'); + $this->assertEquals('http://e.com/base?a=1', $url); + $url = Url::fromString('http://e.com/base'); + $url->addPath('0'); + $this->assertEquals('http://e.com/base/0', $url); + + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath('relative'); + $this->assertEquals('http://e.com/base/relative?a=1', $url); + $url = Url::fromString('http://e.com/base?a=1'); + $url->addPath('/relative'); + $this->assertEquals('http://e.com/base/relative?a=1', $url); + } + + /** + * URL combination data provider + * + * @return array + */ + public function urlCombineDataProvider() + { + return [ + // Specific test cases + ['http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'], + ['http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'], + ['http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'], + ['http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'], + ['http://www.example.com/path', 'http://test.com', 'http://test.com'], + ['http://www.example.com:8080/path', 'http://test.com', 'http://test.com'], + ['http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'], + ['http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'], + ['/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'], + ['http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'], + ['https://www.example.com/path', '//foo.com/abc', 'https://foo.com/abc'], + ['https://www.example.com/0/', 'relative/foo', 'https://www.example.com/0/relative/foo'], + ['', '0', '0'], + // RFC 3986 test cases + [self::RFC3986_BASE, 'g:h', 'g:h'], + [self::RFC3986_BASE, 'g', 'http://a/b/c/g'], + [self::RFC3986_BASE, './g', 'http://a/b/c/g'], + [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], + [self::RFC3986_BASE, '/g', 'http://a/g'], + [self::RFC3986_BASE, '//g', 'http://g'], + [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'], + [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'], + [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'], + [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'], + [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'], + [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'], + [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'], + [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'], + [self::RFC3986_BASE, '', self::RFC3986_BASE], + [self::RFC3986_BASE, '.', 'http://a/b/c/'], + [self::RFC3986_BASE, './', 'http://a/b/c/'], + [self::RFC3986_BASE, '..', 'http://a/b/'], + [self::RFC3986_BASE, '../', 'http://a/b/'], + [self::RFC3986_BASE, '../g', 'http://a/b/g'], + [self::RFC3986_BASE, '../..', 'http://a/'], + [self::RFC3986_BASE, '../../', 'http://a/'], + [self::RFC3986_BASE, '../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../../g', 'http://a/g'], + [self::RFC3986_BASE, '/./g', 'http://a/g'], + [self::RFC3986_BASE, '/../g', 'http://a/g'], + [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'], + [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'], + [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'], + [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'], + [self::RFC3986_BASE, './../g', 'http://a/b/g'], + [self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'], + [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'], + [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'], + [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'], + [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'], + [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'], + [self::RFC3986_BASE, 'http:g', 'http:g'], + ]; + } + + /** + * @dataProvider urlCombineDataProvider + */ + public function testCombinesUrls($a, $b, $c) + { + $this->assertEquals($c, (string) Url::fromString($a)->combine($b)); + } + + public function testHasGettersAndSetters() + { + $url = Url::fromString('http://www.test.com/'); + $url->setHost('example.com'); + $this->assertEquals('example.com', $url->getHost()); + $url->setPort(8080); + $this->assertEquals('8080', $url->getPort()); + $url->setPath('/foo/bar'); + $this->assertEquals('/foo/bar', $url->getPath()); + $url->setPassword('a'); + $this->assertEquals('a', $url->getPassword()); + $url->setUsername('b'); + $this->assertEquals('b', $url->getUsername()); + $url->setFragment('abc'); + $this->assertEquals('abc', $url->getFragment()); + $url->setScheme('https'); + $this->assertEquals('https', $url->getScheme()); + $url->setQuery('a=123'); + $this->assertEquals('a=123', (string) $url->getQuery()); + $this->assertEquals( + 'https://b:a@example.com:8080/foo/bar?a=123#abc', + (string) $url + ); + $url->setQuery(new Query(['b' => 'boo'])); + $this->assertEquals('b=boo', $url->getQuery()); + $this->assertEquals( + 'https://b:a@example.com:8080/foo/bar?b=boo#abc', + (string) $url + ); + + $url->setQuery('a%20=bar!', true); + $this->assertEquals( + 'https://b:a@example.com:8080/foo/bar?a%20=bar!#abc', + (string) $url + ); + } + + public function testSetQueryAcceptsArray() + { + $url = Url::fromString('http://www.test.com'); + $url->setQuery(array('a' => 'b')); + $this->assertEquals('http://www.test.com?a=b', (string) $url); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testQueryMustBeValid() + { + $url = Url::fromString('http://www.test.com'); + $url->setQuery(false); + } + + public function testDefersParsingAndEncodingQueryUntilNecessary() + { + $url = Url::fromString('http://www.test.com'); + // Note that invalid characters are encoded. + $url->setQuery('foo#bar/', true); + $this->assertEquals('http://www.test.com?foo%23bar/', (string) $url); + $this->assertInternalType('string', $this->readAttribute($url, 'query')); + $this->assertEquals('foo%23bar%2F', (string) $url->getQuery()); + $this->assertInstanceOf('GuzzleHttp\Query', $this->readAttribute($url, 'query')); + } + + public function urlProvider() + { + return array( + array('/foo/..', '/'), + array('//foo//..', '//foo/'), + array('/foo//', '/foo//'), + array('/foo/../..', '/'), + array('/foo/../.', '/'), + array('/./foo/..', '/'), + array('/./foo', '/foo'), + array('/./foo/', '/foo/'), + array('*', '*'), + array('/foo', '/foo'), + array('/abc/123/../foo/', '/abc/foo/'), + array('/a/b/c/./../../g', '/a/g'), + array('/b/c/./../../g', '/g'), + array('/b/c/./../../g', '/g'), + array('/c/./../../g', '/g'), + array('/./../../g', '/g'), + array('foo', 'foo'), + ); + } + + /** + * @dataProvider urlProvider + */ + public function testRemoveDotSegments($path, $result) + { + $url = Url::fromString('http://www.example.com'); + $url->setPath($path); + $url->removeDotSegments(); + $this->assertEquals($result, $url->getPath()); + } + + public function testSettingHostWithPortModifiesPort() + { + $url = Url::fromString('http://www.example.com'); + $url->setHost('foo:8983'); + $this->assertEquals('foo', $url->getHost()); + $this->assertEquals(8983, $url->getPort()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesUrlCanBeParsed() + { + Url::fromString('foo:////'); + } + + public function testConvertsSpecialCharsInPathWhenCastingToString() + { + $url = Url::fromString('http://foo.com/baz bar?a=b'); + $url->addPath('?'); + $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); + } + + public function testCorrectlyEncodesPathWithoutDoubleEncoding() + { + $url = Url::fromString('http://foo.com/baz%20 bar:boo/baz!'); + $this->assertEquals('/baz%20%20bar:boo/baz!', $url->getPath()); + } + + public function testLowercaseScheme() + { + $url = Url::fromString('HTTP://foo.com/'); + $this->assertEquals('http', $url->getScheme()); + $url->setScheme('HTTPS'); + $this->assertEquals('https', $url->getScheme()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UtilsTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UtilsTest.php new file mode 100755 index 000000000..10bdc5453 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/UtilsTest.php @@ -0,0 +1,40 @@ +assertEquals( + 'foo/123', + Utils::uriTemplate('foo/{bar}', ['bar' => '123']) + ); + } + + public function noBodyProvider() + { + return [['get'], ['head'], ['delete']]; + } + + public function testJsonDecodes() + { + $this->assertTrue(Utils::jsonDecode('true')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON + */ + public function testJsonDecodesWithErrorMessages() + { + Utils::jsonDecode('!narf!'); + } + + public function testProvidesDefaultUserAgent() + { + $ua = Utils::getDefaultUserAgent(); + $this->assertEquals(1, preg_match('#^Guzzle/.+ curl/.+ PHP/.+$#', $ua)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/bootstrap.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/bootstrap.php new file mode 100755 index 000000000..8713f9624 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/guzzle/tests/bootstrap.php @@ -0,0 +1,11 @@ + Server::$url]); + +$t = microtime(true); +for ($i = 0; $i < $total; $i++) { + $client->get('/guzzle-server/perf'); +} +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Serial: %f (%f ms / request) %d total\n", + $totalTime, $perRequest, $total); + +// Create a generator used to yield batches of requests +$reqs = function () use ($client, $total) { + for ($i = 0; $i < $total; $i++) { + yield $client->createRequest('GET', '/guzzle-server/perf'); + } +}; + +$t = microtime(true); +Pool::send($client, $reqs(), ['parallel' => $parallel]); +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Batch: %f (%f ms / request) %d total with %d in parallel\n", + $totalTime, $perRequest, $total, $parallel); + +$handler = new CurlMultiHandler(['max_handles' => $parallel]); +$client = new Client(['handler' => $handler, 'base_url' => Server::$url]); +$t = microtime(true); +for ($i = 0; $i < $total; $i++) { + $client->get('/guzzle-server/perf'); +} +unset($client); +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Future: %f (%f ms / request) %d total\n", + $totalTime, $perRequest, $total); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.gitignore b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.gitignore new file mode 100755 index 000000000..290a94524 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.gitignore @@ -0,0 +1,4 @@ +vendor +build/artifacts/ +composer.lock +docs/_build/ diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.travis.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.travis.yml new file mode 100755 index 000000000..e43fbdd9c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/.travis.yml @@ -0,0 +1,22 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +before_script: + - composer self-update + - composer install --no-interaction --prefer-source --dev + - ~/.nvm/nvm.sh install v0.6.14 + - ~/.nvm/nvm.sh run v0.6.14 + +script: + - make test + +matrix: + allow_failures: + - php: hhvm + fast_finish: true diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/CHANGELOG.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/CHANGELOG.md new file mode 100755 index 000000000..d399d8262 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/CHANGELOG.md @@ -0,0 +1,54 @@ +# CHANGELOG + +## 1.1.0 - 2015-05-19 + +* Added `CURL_HTTP_VERSION_2_0` +* The PHP stream wrapper handler now sets `allow_self_signed` to `false` to + match the cURL handler when `verify` is set to `true` or a certificate file. +* Ensuring that a directory exists before using the `save_to` option. +* Response protocol version is now correctly extracted from a response. +* Fixed a bug in which the result of `CurlFactory::retryFailedRewind` did not + return an array. + +## 1.0.7 - 2015-03-29 + +* PHP 7 fixes. + +## 1.0.6 - 2015-02-26 + +* Bug fix: futures now extend from React's PromiseInterface to ensure that they + are properly forwarded down the promise chain. +* The multi handle of the CurlMultiHandler is now created lazily. + +## 1.0.5 - 2014-12-10 + +* Adding more error information to PHP stream wrapper exceptions. +* Added digest auth integration test support to test server. + +## 1.0.4 - 2014-12-01 + +* Added support for older versions of cURL that do not have CURLOPT_TIMEOUT_MS. +* Setting debug to `false` does not enable debug output. +* Added a fix to the StreamHandler to return a `FutureArrayInterface` when an + error occurs. + +## 1.0.3 - 2014-11-03 + +* Setting the `header` stream option as a string to be compatible with GAE. +* Header parsing now ensures that header order is maintained in the parsed + message. + +## 1.0.2 - 2014-10-28 + +* Now correctly honoring a `version` option is supplied in a request. + See https://github.com/guzzle/RingPHP/pull/8 + +## 1.0.1 - 2014-10-26 + +* Fixed a header parsing issue with the `CurlHandler` and `CurlMultiHandler` + that caused cURL requests with multiple responses to merge repsonses together + (e.g., requests with digest authentication). + +## 1.0.0 - 2014-10-12 + +* Initial release. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/LICENSE new file mode 100755 index 000000000..71d3b783c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/Makefile b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/Makefile new file mode 100755 index 000000000..21c812e38 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/Makefile @@ -0,0 +1,46 @@ +all: clean coverage docs + +docs: + cd docs && make html + +view-docs: + open docs/_build/html/index.html + +start-server: stop-server + node tests/Client/server.js &> /dev/null & + +stop-server: + @PID=$(shell ps axo pid,command \ + | grep 'tests/Client/server.js' \ + | grep -v grep \ + | cut -f 1 -d " "\ + ) && [ -n "$$PID" ] && kill $$PID || true + +test: start-server + vendor/bin/phpunit $(TEST) + $(MAKE) stop-server + +coverage: start-server + vendor/bin/phpunit --coverage-html=build/artifacts/coverage $(TEST) + $(MAKE) stop-server + +view-coverage: + open build/artifacts/coverage/index.html + +clean: + rm -rf build/artifacts/* + cd docs && make clean + +tag: + $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) + @echo Tagging $(TAG) + chag update -m '$(TAG) ()' + git add -A + git commit -m '$(TAG) release' + chag tag + +perf: start-server + php tests/perf.php + $(MAKE) stop-server + +.PHONY: docs diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/README.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/README.rst new file mode 100755 index 000000000..10374e813 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/README.rst @@ -0,0 +1,46 @@ +======= +RingPHP +======= + +Provides a simple API and specification that abstracts away the details of HTTP +into a single PHP function. RingPHP be used to power HTTP clients and servers +through a PHP function that accepts a request hash and returns a response hash +that is fulfilled using a `promise `_, +allowing RingPHP to support both synchronous and asynchronous workflows. + +By abstracting the implementation details of different HTTP clients and +servers, RingPHP allows you to utilize pluggable HTTP clients and servers +without tying your application to a specific implementation. + +.. code-block:: php + + 'GET', + 'uri' => '/', + 'headers' => [ + 'host' => ['www.google.com'], + 'x-foo' => ['baz'] + ] + ]); + + $response->then(function (array $response) { + echo $response['status']; + }); + + $response->wait(); + +RingPHP is inspired by Clojure's `Ring `_, +which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is +utilized as the handler layer in `Guzzle `_ 5.0+ to send +HTTP requests. + +Documentation +------------- + +See http://ringphp.readthedocs.org/ for the full online documentation. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/composer.json new file mode 100755 index 000000000..22002efca --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/composer.json @@ -0,0 +1,39 @@ +{ + "name": "guzzlehttp/ringphp", + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0", + "guzzlehttp/streams": "~3.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Tests\\Ring\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/Makefile b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/Makefile new file mode 100755 index 000000000..51270aa5d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GuzzleRing.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GuzzleRing.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/GuzzleRing" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GuzzleRing" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_handlers.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_handlers.rst new file mode 100755 index 000000000..3151f0021 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_handlers.rst @@ -0,0 +1,173 @@ +=============== +Client Handlers +=============== + +Client handlers accept a request array and return a future response array that +can be used synchronously as an array or asynchronously using a promise. + +Built-In Handlers +----------------- + +RingPHP comes with three built-in client handlers. + +Stream Handler +~~~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\StreamHandler`` uses PHP's +`http stream wrapper `_ to send +requests. + +.. note:: + + This handler cannot send requests concurrently. + +You can provide an associative array of custom stream context options to the +StreamHandler using the ``stream_context`` key of the ``client`` request +option. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\StreamHandler; + + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['httpbin.org']], + 'client' => [ + 'stream_context' => [ + 'http' => [ + 'request_fulluri' => true, + 'method' => 'HEAD' + ], + 'socket' => [ + 'bindto' => '127.0.0.1:0' + ], + 'ssl' => [ + 'verify_peer' => false + ] + ] + ] + ]); + + // Even though it's already completed, you can still use a promise + $response->then(function ($response) { + echo $response['status']; // 200 + }); + + // Or access the response using the future interface + echo $response['status']; // 200 + +cURL Handler +~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\CurlHandler`` can be used with PHP 5.5+ to send +requests using cURL easy handles. This handler is great for sending requests +one at a time because the execute and select loop is implemented in C code +which executes faster and consumes less memory than using PHP's +``curl_multi_*`` interface. + +.. note:: + + This handler cannot send requests concurrently. + +When using the CurlHandler, custom curl options can be specified as an +associative array of `cURL option constants `_ +mapping to values in the ``client`` option of a requst using the **curl** key. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + + $handler = new CurlHandler(); + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]] + ]; + + $response = $handler($request); + + // The response can be used directly as an array. + echo $response['status']; // 200 + + // Or, it can be used as a promise (that has already fulfilled). + $response->then(function ($response) { + echo $response['status']; // 200 + }); + +cURL Multi Handler +~~~~~~~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\CurlMultiHandler`` transfers requests using +cURL's `multi API `_. The +``CurlMultiHandler`` is great for sending requests concurrently. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $handler = new CurlMultiHandler(); + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]] + ]; + + // this call returns a future array immediately. + $response = $handler($request); + + // Ideally, you should use the promise API to not block. + $response + ->then(function ($response) { + // Got the response at some point in the future + echo $response['status']; // 200 + // Don't break the chain + return $response; + })->then(function ($response) { + // ... + }); + + // If you really need to block, then you can use the response as an + // associative array. This will block until it has completed. + echo $response['status']; // 200 + +Just like the ``CurlHandler``, the ``CurlMultiHandler`` accepts custom curl +option in the ``curl`` key of the ``client`` request option. + +Mock Handler +~~~~~~~~~~~~ + +The ``GuzzleHttp\Ring\Client\MockHandler`` is used to return mock responses. +When constructed, the handler can be configured to return the same response +array over and over, a future response, or a the evaluation of a callback +function. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\MockHandler; + + // Return a canned response. + $mock = new MockHandler(['status' => 200]); + $response = $mock([]); + assert(200 == $response['status']); + assert([] == $response['headers']); + +Implementing Handlers +--------------------- + +Client handlers are just PHP callables (functions or classes that have the +``__invoke`` magic method). The callable accepts a request array and MUST +return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface`` so that +the response can be used by both blocking and non-blocking consumers. + +Handlers need to follow a few simple rules: + +1. Do not throw exceptions. If an error is encountered, return an array that + contains the ``error`` key that maps to an ``\Exception`` value. +2. If the request has a ``delay`` client option, then the handler should only + send the request after the specified delay time in seconds. Blocking + handlers may find it convenient to just let the + ``GuzzleHttp\Ring\Core::doSleep($request)`` function handle this for them. +3. Always return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface``. +4. Complete any outstanding requests when the handler is destructed. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_middleware.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_middleware.rst new file mode 100755 index 000000000..5a2c1a8ab --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/client_middleware.rst @@ -0,0 +1,165 @@ +================= +Client Middleware +================= + +Middleware intercepts requests before they are sent over the wire and can be +used to add functionality to handlers. + +Modifying Requests +------------------ + +Let's say you wanted to modify requests before they are sent over the wire +so that they always add specific headers. This can be accomplished by creating +a function that accepts a handler and returns a new function that adds the +composed behavior. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + + $handler = new CurlHandler(); + + $addHeaderHandler = function (callable $handler, array $headers = []) { + return function (array $request) use ($handler, $headers) { + // Add our custom headers + foreach ($headers as $key => $value) { + $request['headers'][$key] = $value; + } + + // Send the request using the handler and return the response. + return $handler($request); + } + }; + + // Create a new handler that adds headers to each request. + $handler = $addHeaderHandler($handler, [ + 'X-AddMe' => 'hello', + 'Authorization' => 'Basic xyz' + ]); + + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['httpbin.org']] + ]); + +Modifying Responses +------------------- + +You can change a response as it's returned from a middleware. Remember that +responses returned from an handler (including middleware) must implement +``GuzzleHttp\Ring\Future\FutureArrayInterface``. In order to be a good citizen, +you should not expect that the responses returned through your middleware will +be completed synchronously. Instead, you should use the +``GuzzleHttp\Ring\Core::proxy()`` function to modify the response when the +underlying promise is resolved. This function is a helper function that makes it +easy to create a new instance of ``FutureArrayInterface`` that wraps an existing +``FutureArrayInterface`` object. + +Let's say you wanted to add headers to a response as they are returned from +your middleware, but you want to make sure you aren't causing future +responses to be dereferenced right away. You can achieve this by modifying the +incoming request and using the ``Core::proxy`` function. + +.. code-block:: php + + use GuzzleHttp\Ring\Core; + use GuzzleHttp\Ring\Client\CurlHandler; + + $handler = new CurlHandler(); + + $responseHeaderHandler = function (callable $handler, array $headers) { + return function (array $request) use ($handler, $headers) { + // Send the request using the wrapped handler. + return Core::proxy($handler($request), function ($response) use ($headers) { + // Add the headers to the response when it is available. + foreach ($headers as $key => $value) { + $response['headers'][$key] = (array) $value; + } + // Note that you can return a regular response array when using + // the proxy method. + return $response; + }); + } + }; + + // Create a new handler that adds headers to each response. + $handler = $responseHeaderHandler($handler, ['X-Header' => 'hello!']); + + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['httpbin.org']] + ]); + + assert($response['headers']['X-Header'] == 'hello!'); + +Built-In Middleware +------------------- + +RingPHP comes with a few basic client middlewares that modify requests +and responses. + +Streaming Middleware +~~~~~~~~~~~~~~~~~~~~ + +If you want to send all requests with the ``streaming`` option to a specific +handler but other requests to a different handler, then use the streaming +middleware. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + use GuzzleHttp\Ring\Client\StreamHandler; + use GuzzleHttp\Ring\Client\Middleware; + + $defaultHandler = new CurlHandler(); + $streamingHandler = new StreamHandler(); + $streamingHandler = Middleware::wrapStreaming( + $defaultHandler, + $streamingHandler + ); + + // Send the request using the streaming handler. + $response = $streamingHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']], + 'stream' => true + ]); + + // Send the request using the default handler. + $response = $streamingHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']] + ]); + +Future Middleware +~~~~~~~~~~~~~~~~~ + +If you want to send all requests with the ``future`` option to a specific +handler but other requests to a different handler, then use the future +middleware. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + use GuzzleHttp\Ring\Client\CurlMultiHandler; + use GuzzleHttp\Ring\Client\Middleware; + + $defaultHandler = new CurlHandler(); + $futureHandler = new CurlMultiHandler(); + $futureHandler = Middleware::wrapFuture( + $defaultHandler, + $futureHandler + ); + + // Send the request using the blocking CurlHandler. + $response = $futureHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']] + ]); + + // Send the request using the non-blocking CurlMultiHandler. + $response = $futureHandler([ + 'http_method' => 'GET', + 'headers' => ['Host' => ['www.google.com']], + 'future' => true + ]); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/conf.py b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/conf.py new file mode 100755 index 000000000..c6404aa1e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/conf.py @@ -0,0 +1,23 @@ +import sys, os +import sphinx_rtd_theme +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'RingPHP' +copyright = u'2014, Michael Dowling' +version = '1.0.0-alpha' +exclude_patterns = ['_build'] + +html_title = "RingPHP" +html_short_title = "RingPHP" +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/futures.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/futures.rst new file mode 100755 index 000000000..af29cb378 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/futures.rst @@ -0,0 +1,164 @@ +======= +Futures +======= + +Futures represent a computation that may have not yet completed. RingPHP +uses hybrid of futures and promises to provide a consistent API that can be +used for both blocking and non-blocking consumers. + +Promises +-------- + +You can get the result of a future when it is ready using the promise interface +of a future. Futures expose a promise API via a ``then()`` method that utilizes +`React's promise library `_. You should +use this API when you do not wish to block. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $request = [ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['httpbin.org']] + ]; + + $response = $handler($request); + + // Use the then() method to use the promise API of the future. + $response->then(function ($response) { + echo $response['status']; + }); + +You can get the promise used by a future, an instance of +``React\Promise\PromiseInterface``, by calling the ``promise()`` method. + +.. code-block:: php + + $response = $handler($request); + $promise = $response->promise(); + $promise->then(function ($response) { + echo $response['status']; + }); + +This promise value can be used with React's +`aggregate promise functions `_. + +Waiting +------- + +You can wait on a future to complete and retrieve the value, or *dereference* +the future, using the ``wait()`` method. Calling the ``wait()`` method of a +future will block until the result is available. The result is then returned or +an exception is thrown if and exception was encountered while waiting on the +the result. Subsequent calls to dereference a future will return the previously +completed result or throw the previously encountered exception. Futures can be +cancelled, which stops the computation if possible. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['httpbin.org']] + ]); + + // You can explicitly call block to wait on a result. + $realizedResponse = $response->wait(); + + // Future responses can be used like a regular PHP array. + echo $response['status']; + +In addition to explicitly calling the ``wait()`` function, using a future like +a normal value will implicitly trigger the ``wait()`` function. + +Future Responses +---------------- + +RingPHP uses futures to return asynchronous responses immediately. Client +handlers always return future responses that implement +``GuzzleHttp\Ring\Future\ArrayFutureInterface``. These future responses act +just like normal PHP associative arrays for blocking access and provide a +promise interface for non-blocking access. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlMultiHandler; + + $handler = new CurlMultiHandler(); + + $request = [ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['Host' => ['www.google.com']] + ]; + + $response = $handler($request); + + // Use the promise API for non-blocking access to the response. The actual + // response value will be delivered to the promise. + $response->then(function ($response) { + echo $response['status']; + }); + + // You can wait (block) until the future is completed. + $response->wait(); + + // This will implicitly call wait(), and will block too! + $response['status']; + +.. important:: + + Futures that are not completed by the time the underlying handler is + destructed will be completed when the handler is shutting down. + +Cancelling +---------- + +Futures can be cancelled if they have not already been dereferenced. + +RingPHP futures are typically implemented with the +``GuzzleHttp\Ring\Future\BaseFutureTrait``. This trait provides the cancellation +functionality that should be common to most implementations. Cancelling a +future response will try to prevent the request from sending over the wire. + +When a future is cancelled, the cancellation function is invoked and performs +the actual work needed to cancel the request from sending if possible +(e.g., telling an event loop to stop sending a request or to close a socket). +If no cancellation function is provided, then a request cannot be cancelled. If +a cancel function is provided, then it should accept the future as an argument +and return true if the future was successfully cancelled or false if it could +not be cancelled. + +Wrapping an existing Promise +---------------------------- + +You can easily create a future from any existing promise using the +``GuzzleHttp\Ring\Future\FutureValue`` class. This class's constructor +accepts a promise as the first argument, a wait function as the second +argument, and a cancellation function as the third argument. The dereference +function is used to force the promise to resolve (for example, manually ticking +an event loop). The cancel function is optional and is used to tell the thing +that created the promise that it can stop computing the result (for example, +telling an event loop to stop transferring a request). + +.. code-block:: php + + use GuzzleHttp\Ring\Future\FutureValue; + use React\Promise\Deferred; + + $deferred = new Deferred(); + $promise = $deferred->promise(); + + $f = new FutureValue( + $promise, + function () use ($deferred) { + // This function is responsible for blocking and resolving the + // promise. Here we pass in a reference to the deferred so that + // it can be resolved or rejected. + $deferred->resolve('foo'); + } + ); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/index.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/index.rst new file mode 100755 index 000000000..4bbce631c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/index.rst @@ -0,0 +1,50 @@ +======= +RingPHP +======= + +Provides a simple API and specification that abstracts away the details of HTTP +into a single PHP function. RingPHP be used to power HTTP clients and servers +through a PHP function that accepts a request hash and returns a response hash +that is fulfilled using a `promise `_, +allowing RingPHP to support both synchronous and asynchronous workflows. + +By abstracting the implementation details of different HTTP clients and +servers, RingPHP allows you to utilize pluggable HTTP clients and servers +without tying your application to a specific implementation. + +.. toctree:: + :maxdepth: 2 + + spec + futures + client_middleware + client_handlers + testing + +.. code-block:: php + + 'GET', + 'uri' => '/', + 'headers' => [ + 'host' => ['www.google.com'], + 'x-foo' => ['baz'] + ] + ]); + + $response->then(function (array $response) { + echo $response['status']; + }); + + $response->wait(); + +RingPHP is inspired by Clojure's `Ring `_, +which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is +utilized as the handler layer in `Guzzle `_ 5.0+ to send +HTTP requests. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/requirements.txt b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/requirements.txt new file mode 100755 index 000000000..483a4e960 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/requirements.txt @@ -0,0 +1 @@ +sphinx_rtd_theme diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/spec.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/spec.rst new file mode 100755 index 000000000..bc9107898 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/spec.rst @@ -0,0 +1,311 @@ +============= +Specification +============= + +RingPHP applications consist of handlers, requests, responses, and +middleware. + +Handlers +-------- + +Handlers are implemented as a PHP ``callable`` that accept a request array +and return a response array (``GuzzleHttp\Ring\Future\FutureArrayInterface``). + +For example: + +.. code-block:: php + + use GuzzleHttp\Ring\Future\CompletedFutureArray; + + $mockHandler = function (array $request) { + return new CompletedFutureArray([ + 'status' => 200, + 'headers' => ['X-Foo' => ['Bar']], + 'body' => 'Hello!' + ]); + }; + +This handler returns the same response each time it is invoked. All RingPHP +handlers must return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. Use +``GuzzleHttp\Ring\Future\CompletedFutureArray`` when returning a response that +has already completed. + +Requests +-------- + +A request array is a PHP associative array that contains the configuration +settings need to send a request. + +.. code-block:: php + + $request = [ + 'http_method' => 'GET', + 'scheme' => 'http', + 'uri' => '/', + 'body' => 'hello!', + 'client' => ['timeout' => 1.0], + 'headers' => [ + 'host' => ['httpbin.org'], + 'X-Foo' => ['baz', 'bar'] + ] + ]; + +The request array contains the following key value pairs: + +request_method + (string, required) The HTTP request method, must be all caps corresponding + to a HTTP request method, such as ``GET`` or ``POST``. + +scheme + (string) The transport protocol, must be one of ``http`` or ``https``. + Defaults to ``http``. + +uri + (string, required) The request URI excluding the query string. Must + start with "/". + +query_string + (string) The query string, if present (e.g., ``foo=bar``). + +version + (string) HTTP protocol version. Defaults to ``1.1``. + +headers + (required, array) Associative array of headers. Each key represents the + header name. Each value contains an array of strings where each entry of + the array SHOULD be sent over the wire on a separate header line. + +body + (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) + The body of the request, if present. Can be a string, resource returned + from fopen, an ``Iterator`` that yields chunks of data, an object that + implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. + +future + (bool, string) Controls the asynchronous behavior of a response. + + Set to ``true`` or omit the ``future`` option to *request* that a request + will be completed asynchronously. Keep in mind that your request might not + necessarily be completed asynchronously based on the handler you are using. + Set the ``future`` option to ``false`` to request that a synchronous + response be provided. + + You can provide a string value to specify fine-tuned future behaviors that + may be specific to the underlying handlers you are using. There are, + however, some common future options that handlers should implement if + possible. + + lazy + Requests that the handler does not open and send the request + immediately, but rather only opens and sends the request once the + future is dereferenced. This option is often useful for sending a large + number of requests concurrently to allow handlers to take better + advantage of non-blocking transfers by first building up a pool of + requests. + + If an handler does not implement or understand a provided string value, + then the request MUST be treated as if the user provided ``true`` rather + than the string value. + + Future responses created by asynchronous handlers MUST attempt to complete + any outstanding future responses when they are destructed. Asynchronous + handlers MAY choose to automatically complete responses when the number + of outstanding requests reaches an handler-specific threshold. + +Client Specific Options +~~~~~~~~~~~~~~~~~~~~~~~ + +The following options are only used in ring client handlers. + +.. _client-options: + +client + (array) Associative array of client specific transfer options. The + ``client`` request key value pair can contain the following keys: + + cert + (string, array) Set to a string to specify the path to a file + containing a PEM formatted SSL client side certificate. If a password + is required, then set ``cert`` to an array containing the path to the + PEM file in the first array element followed by the certificate + password in the second array element. + + connect_timeout + (float) Float describing the number of seconds to wait while trying to + connect to a server. Use ``0`` to wait indefinitely (the default + behavior). + + debug + (bool, fopen() resource) Set to true or set to a PHP stream returned by + fopen() to enable debug output with the handler used to send a request. + If set to ``true``, the output is written to PHP's STDOUT. If a PHP + ``fopen`` resource handle is provided, the output is written to the + stream. + + "Debug output" is handler specific: different handlers will yield + different output and various various level of detail. For example, when + using cURL to transfer requests, cURL's `CURLOPT_VERBOSE `_ + will be used. When using the PHP stream wrapper, `stream notifications `_ + will be emitted. + + decode_content + (bool) Specify whether or not ``Content-Encoding`` responses + (gzip, deflate, etc.) are automatically decoded. Set to ``true`` to + automatically decode encoded responses. Set to ``false`` to not decode + responses. By default, content is *not* decoded automatically. + + delay + (int) The number of milliseconds to delay before sending the request. + This is often used for delaying before retrying a request. Handlers + SHOULD implement this if possible, but it is not a strict requirement. + + progress + (function) Defines a function to invoke when transfer progress is made. + The function accepts the following arguments: + + 1. The total number of bytes expected to be downloaded + 2. The number of bytes downloaded so far + 3. The number of bytes expected to be uploaded + 4. The number of bytes uploaded so far + + proxy + (string, array) Pass a string to specify an HTTP proxy, or an + associative array to specify different proxies for different protocols + where the scheme is the key and the value is the proxy address. + + .. code-block:: php + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => ['httpbin.org']], + 'client' => [ + // Use different proxies for different URI schemes. + 'proxy' => [ + 'http' => 'http://proxy.example.com:5100', + 'https' => 'https://proxy.example.com:6100' + ] + ] + ]; + + ssl_key + (string, array) Specify the path to a file containing a private SSL key + in PEM format. If a password is required, then set to an array + containing the path to the SSL key in the first array element followed + by the password required for the certificate in the second element. + + save_to + (string, fopen resource, ``GuzzleHttp\Stream\StreamInterface``) + Specifies where the body of the response is downloaded. Pass a string to + open a local file on disk and save the output to the file. Pass an fopen + resource to save the output to a PHP stream resource. Pass a + ``GuzzleHttp\Stream\StreamInterface`` to save the output to a Guzzle + StreamInterface. Omitting this option will typically save the body of a + response to a PHP temp stream. + + stream + (bool) Set to true to stream a response rather than download it all + up-front. This option will only be utilized when the corresponding + handler supports it. + + timeout + (float) Float describing the timeout of the request in seconds. Use 0 to + wait indefinitely (the default behavior). + + verify + (bool, string) Describes the SSL certificate verification behavior of a + request. Set to true to enable SSL certificate verification using the + system CA bundle when available (the default). Set to false to disable + certificate verification (this is insecure!). Set to a string to provide + the path to a CA bundle on disk to enable verification using a custom + certificate. + + version + (string) HTTP protocol version to use with the request. + +Server Specific Options +~~~~~~~~~~~~~~~~~~~~~~~ + +The following options are only used in ring server handlers. + +server_port + (integer) The port on which the request is being handled. This is only + used with ring servers, and is required. + +server_name + (string) The resolved server name, or the server IP address. Required when + using a Ring server. + +remote_addr + (string) The IP address of the client or the last proxy that sent the + request. Required when using a Ring server. + +Responses +--------- + +A response is an array-like object that implements +``GuzzleHttp\Ring\Future\FutureArrayInterface``. Responses contain the +following key value pairs: + +body + (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) + The body of the response, if present. Can be a string, resource returned + from fopen, an ``Iterator`` that yields chunks of data, an object that + implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. + +effective_url + (string) The URL that returned the resulting response. + +error + (``\Exception``) Contains an exception describing any errors that were + encountered during the transfer. + +headers + (Required, array) Associative array of headers. Each key represents the + header name. Each value contains an array of strings where each entry of + the array is a header line. The headers array MAY be an empty array in the + event an error occurred before a response was received. + +reason + (string) Optional reason phrase. This option should be provided when the + reason phrase does not match the typical reason phrase associated with the + ``status`` code. See `RFC 7231 `_ + for a list of HTTP reason phrases mapped to status codes. + +status + (Required, integer) The HTTP status code. The status code MAY be set to + ``null`` in the event an error occurred before a response was received + (e.g., a networking error). + +transfer_stats + (array) Provides an associative array of arbitrary transfer statistics if + provided by the underlying handler. + +version + (string) HTTP protocol version. Defaults to ``1.1``. + +Middleware +---------- + +Ring middleware augments the functionality of handlers by invoking them in the +process of generating responses. Middleware is typically implemented as a +higher-order function that takes one or more handlers as arguments followed by +an optional associative array of options as the last argument, returning a new +handler with the desired compound behavior. + +Here's an example of a middleware that adds a Content-Type header to each +request. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\CurlHandler; + use GuzzleHttp\Ring\Core; + + $contentTypeHandler = function(callable $handler, $contentType) { + return function (array $request) use ($handler, $contentType) { + return $handler(Core::setHeader('Content-Type', $contentType)); + }; + }; + + $baseHandler = new CurlHandler(); + $wrappedHandler = $contentTypeHandler($baseHandler, 'text/html'); + $response = $wrappedHandler([/** request hash **/]); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/testing.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/testing.rst new file mode 100755 index 000000000..9df2562ed --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/docs/testing.rst @@ -0,0 +1,74 @@ +======= +Testing +======= + +RingPHP tests client handlers using `PHPUnit `_ and a +built-in node.js web server. + +Running Tests +------------- + +First, install the dependencies using `Composer `_. + + composer.phar install + +Next, run the unit tests using ``Make``. + + make test + +The tests are also run on Travis-CI on each commit: https://travis-ci.org/guzzle/guzzle-ring + +Test Server +----------- + +Testing client handlers usually involves actually sending HTTP requests. +RingPHP provides a node.js web server that returns canned responses and +keep a list of the requests that have been received. The server can then +be queried to get a list of the requests that were sent by the client so that +you can ensure that the client serialized and transferred requests as intended. + +The server keeps a list of queued responses and returns responses that are +popped off of the queue as HTTP requests are received. When there are not +more responses to serve, the server returns a 500 error response. + +The test server uses the ``GuzzleHttp\Tests\Ring\Client\Server`` class to +control the server. + +.. code-block:: php + + use GuzzleHttp\Ring\Client\StreamHandler; + use GuzzleHttp\Tests\Ring\Client\Server; + + // First return a 200 followed by a 404 response. + Server::enqueue([ + ['status' => 200], + ['status' => 404] + ]); + + $handler = new StreamHandler(); + + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/' + ]); + + assert(200 == $response['status']); + + $response = $handler([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/' + ]); + + assert(404 == $response['status']); + +After requests have been sent, you can get a list of the requests as they +were sent over the wire to ensure they were sent correctly. + +.. code-block:: php + + $received = Server::received(); + + assert('GET' == $received[0]['http_method']); + assert('HEAD' == $received[1]['http_method']); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/phpunit.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/phpunit.xml.dist new file mode 100755 index 000000000..1d1929025 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + tests + + + + + src + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php new file mode 100755 index 000000000..2acf92eba --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php @@ -0,0 +1,74 @@ +getDefaultOptions($request, $headers); + $this->applyMethod($request, $options); + + if (isset($request['client'])) { + $this->applyHandlerOptions($request, $options); + } + + $this->applyHeaders($request, $options); + unset($options['_headers']); + + // Add handler options from the request's configuration options + if (isset($request['client']['curl'])) { + $options = $this->applyCustomCurlOptions( + $request['client']['curl'], + $options + ); + } + + if (!$handle) { + $handle = curl_init(); + } + + $body = $this->getOutputBody($request, $options); + curl_setopt_array($handle, $options); + + return [$handle, &$headers, $body]; + } + + /** + * Creates a response hash from a cURL result. + * + * @param callable $handler Handler that was used. + * @param array $request Request that sent. + * @param array $response Response hash to update. + * @param array $headers Headers received during transfer. + * @param resource $body Body fopen response. + * + * @return array + */ + public static function createResponse( + callable $handler, + array $request, + array $response, + array $headers, + $body + ) { + if (isset($response['transfer_stats']['url'])) { + $response['effective_url'] = $response['transfer_stats']['url']; + } + + if (!empty($headers)) { + $startLine = explode(' ', array_shift($headers), 3); + $headerList = Core::headersFromLines($headers); + $response['headers'] = $headerList; + $response['version'] = isset($startLine[0]) ? substr($startLine[0], 5) : null; + $response['status'] = isset($startLine[1]) ? (int) $startLine[1] : null; + $response['reason'] = isset($startLine[2]) ? $startLine[2] : null; + $response['body'] = $body; + Core::rewindBody($response); + } + + return !empty($response['curl']['errno']) || !isset($response['status']) + ? self::createErrorResponse($handler, $request, $response) + : $response; + } + + private static function createErrorResponse( + callable $handler, + array $request, + array $response + ) { + static $connectionErrors = [ + CURLE_OPERATION_TIMEOUTED => true, + CURLE_COULDNT_RESOLVE_HOST => true, + CURLE_COULDNT_CONNECT => true, + CURLE_SSL_CONNECT_ERROR => true, + CURLE_GOT_NOTHING => true, + ]; + + // Retry when nothing is present or when curl failed to rewind. + if (!isset($response['err_message']) + && (empty($response['curl']['errno']) + || $response['curl']['errno'] == 65) + ) { + return self::retryFailedRewind($handler, $request, $response); + } + + $message = isset($response['err_message']) + ? $response['err_message'] + : sprintf('cURL error %s: %s', + $response['curl']['errno'], + isset($response['curl']['error']) + ? $response['curl']['error'] + : 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html'); + + $error = isset($response['curl']['errno']) + && isset($connectionErrors[$response['curl']['errno']]) + ? new ConnectException($message) + : new RingException($message); + + return $response + [ + 'status' => null, + 'reason' => null, + 'body' => null, + 'headers' => [], + 'error' => $error, + ]; + } + + private function getOutputBody(array $request, array &$options) + { + // Determine where the body of the response (if any) will be streamed. + if (isset($options[CURLOPT_WRITEFUNCTION])) { + return $request['client']['save_to']; + } + + if (isset($options[CURLOPT_FILE])) { + return $options[CURLOPT_FILE]; + } + + if ($request['http_method'] != 'HEAD') { + // Create a default body if one was not provided + return $options[CURLOPT_FILE] = fopen('php://temp', 'w+'); + } + + return null; + } + + private function getDefaultOptions(array $request, array &$headers) + { + $url = Core::url($request); + $startingResponse = false; + + $options = [ + '_headers' => $request['headers'], + CURLOPT_CUSTOMREQUEST => $request['http_method'], + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers, &$startingResponse) { + $value = trim($h); + if ($value === '') { + $startingResponse = true; + } elseif ($startingResponse) { + $startingResponse = false; + $headers = [$value]; + } else { + $headers[] = $value; + } + return strlen($h); + }, + ]; + + if (isset($request['version'])) { + if ($request['version'] == 2.0) { + $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + } else if ($request['version'] == 1.1) { + $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + } else { + $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + } + } + + if (defined('CURLOPT_PROTOCOLS')) { + $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + return $options; + } + + private function applyMethod(array $request, array &$options) + { + if (isset($request['body'])) { + $this->applyBody($request, $options); + return; + } + + switch ($request['http_method']) { + case 'PUT': + case 'POST': + // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + if (!Core::hasHeader($request, 'Content-Length')) { + $options[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; + } + break; + case 'HEAD': + $options[CURLOPT_NOBODY] = true; + unset( + $options[CURLOPT_WRITEFUNCTION], + $options[CURLOPT_READFUNCTION], + $options[CURLOPT_FILE], + $options[CURLOPT_INFILE] + ); + } + } + + private function applyBody(array $request, array &$options) + { + $contentLength = Core::firstHeader($request, 'Content-Length'); + $size = $contentLength !== null ? (int) $contentLength : null; + + // Send the body as a string if the size is less than 1MB OR if the + // [client][curl][body_as_string] request value is set. + if (($size !== null && $size < 1000000) || + isset($request['client']['curl']['body_as_string']) || + is_string($request['body']) + ) { + $options[CURLOPT_POSTFIELDS] = Core::body($request); + // Don't duplicate the Content-Length header + $this->removeHeader('Content-Length', $options); + $this->removeHeader('Transfer-Encoding', $options); + } else { + $options[CURLOPT_UPLOAD] = true; + if ($size !== null) { + // Let cURL handle setting the Content-Length header + $options[CURLOPT_INFILESIZE] = $size; + $this->removeHeader('Content-Length', $options); + } + $this->addStreamingBody($request, $options); + } + + // If the Expect header is not present, prevent curl from adding it + if (!Core::hasHeader($request, 'Expect')) { + $options[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + + // cURL sometimes adds a content-type by default. Prevent this. + if (!Core::hasHeader($request, 'Content-Type')) { + $options[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } + + private function addStreamingBody(array $request, array &$options) + { + $body = $request['body']; + + if ($body instanceof StreamInterface) { + $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { + return (string) $body->read($length); + }; + if (!isset($options[CURLOPT_INFILESIZE])) { + if ($size = $body->getSize()) { + $options[CURLOPT_INFILESIZE] = $size; + } + } + } elseif (is_resource($body)) { + $options[CURLOPT_INFILE] = $body; + } elseif ($body instanceof \Iterator) { + $buf = ''; + $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, &$buf) { + if ($body->valid()) { + $buf .= $body->current(); + $body->next(); + } + $result = (string) substr($buf, 0, $length); + $buf = substr($buf, $length); + return $result; + }; + } else { + throw new \InvalidArgumentException('Invalid request body provided'); + } + } + + private function applyHeaders(array $request, array &$options) + { + foreach ($options['_headers'] as $name => $values) { + foreach ($values as $value) { + $options[CURLOPT_HTTPHEADER][] = "$name: $value"; + } + } + + // Remove the Accept header if one was not set + if (!Core::hasHeader($request, 'Accept')) { + $options[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + } + + /** + * Takes an array of curl options specified in the 'curl' option of a + * request's configuration array and maps them to CURLOPT_* options. + * + * This method is only called when a request has a 'curl' config setting. + * + * @param array $config Configuration array of custom curl option + * @param array $options Array of existing curl options + * + * @return array Returns a new array of curl options + */ + private function applyCustomCurlOptions(array $config, array $options) + { + $curlOptions = []; + foreach ($config as $key => $value) { + if (is_int($key)) { + $curlOptions[$key] = $value; + } + } + + return $curlOptions + $options; + } + + /** + * Remove a header from the options array. + * + * @param string $name Case-insensitive header to remove + * @param array $options Array of options to modify + */ + private function removeHeader($name, array &$options) + { + foreach (array_keys($options['_headers']) as $key) { + if (!strcasecmp($key, $name)) { + unset($options['_headers'][$key]); + return; + } + } + } + + /** + * Applies an array of request client options to a the options array. + * + * This method uses a large switch rather than double-dispatch to save on + * high overhead of calling functions in PHP. + */ + private function applyHandlerOptions(array $request, array &$options) + { + foreach ($request['client'] as $key => $value) { + switch ($key) { + // Violating PSR-4 to provide more room. + case 'verify': + + if ($value === false) { + unset($options[CURLOPT_CAINFO]); + $options[CURLOPT_SSL_VERIFYHOST] = 0; + $options[CURLOPT_SSL_VERIFYPEER] = false; + continue; + } + + $options[CURLOPT_SSL_VERIFYHOST] = 2; + $options[CURLOPT_SSL_VERIFYPEER] = true; + + if (is_string($value)) { + $options[CURLOPT_CAINFO] = $value; + if (!file_exists($value)) { + throw new \InvalidArgumentException( + "SSL CA bundle not found: $value" + ); + } + } + break; + + case 'decode_content': + + if ($value === false) { + continue; + } + + $accept = Core::firstHeader($request, 'Accept-Encoding'); + if ($accept) { + $options[CURLOPT_ENCODING] = $accept; + } else { + $options[CURLOPT_ENCODING] = ''; + // Don't let curl send the header over the wire + $options[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; + } + break; + + case 'save_to': + + if (is_string($value)) { + if (!is_dir(dirname($value))) { + throw new \RuntimeException(sprintf( + 'Directory %s does not exist for save_to value of %s', + dirname($value), + $value + )); + } + $value = new LazyOpenStream($value, 'w+'); + } + + if ($value instanceof StreamInterface) { + $options[CURLOPT_WRITEFUNCTION] = + function ($ch, $write) use ($value) { + return $value->write($write); + }; + } elseif (is_resource($value)) { + $options[CURLOPT_FILE] = $value; + } else { + throw new \InvalidArgumentException('save_to must be a ' + . 'GuzzleHttp\Stream\StreamInterface or resource'); + } + break; + + case 'timeout': + + if (defined('CURLOPT_TIMEOUT_MS')) { + $options[CURLOPT_TIMEOUT_MS] = $value * 1000; + } else { + $options[CURLOPT_TIMEOUT] = $value; + } + break; + + case 'connect_timeout': + + if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { + $options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000; + } else { + $options[CURLOPT_CONNECTTIMEOUT] = $value; + } + break; + + case 'proxy': + + if (!is_array($value)) { + $options[CURLOPT_PROXY] = $value; + } elseif (isset($request['scheme'])) { + $scheme = $request['scheme']; + if (isset($value[$scheme])) { + $options[CURLOPT_PROXY] = $value[$scheme]; + } + } + break; + + case 'cert': + + if (is_array($value)) { + $options[CURLOPT_SSLCERTPASSWD] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \InvalidArgumentException( + "SSL certificate not found: {$value}" + ); + } + + $options[CURLOPT_SSLCERT] = $value; + break; + + case 'ssl_key': + + if (is_array($value)) { + $options[CURLOPT_SSLKEYPASSWD] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \InvalidArgumentException( + "SSL private key not found: {$value}" + ); + } + + $options[CURLOPT_SSLKEY] = $value; + break; + + case 'progress': + + if (!is_callable($value)) { + throw new \InvalidArgumentException( + 'progress client option must be callable' + ); + } + + $options[CURLOPT_NOPROGRESS] = false; + $options[CURLOPT_PROGRESSFUNCTION] = + function () use ($value) { + $args = func_get_args(); + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + call_user_func_array($value, $args); + }; + break; + + case 'debug': + + if ($value) { + $options[CURLOPT_STDERR] = Core::getDebugResource($value); + $options[CURLOPT_VERBOSE] = true; + } + break; + } + } + } + + /** + * This function ensures that a response was set on a transaction. If one + * was not set, then the request is retried if possible. This error + * typically means you are sending a payload, curl encountered a + * "Connection died, retrying a fresh connect" error, tried to rewind the + * stream, and then encountered a "necessary data rewind wasn't possible" + * error, causing the request to be sent through curl_multi_info_read() + * without an error status. + */ + private static function retryFailedRewind( + callable $handler, + array $request, + array $response + ) { + // If there is no body, then there is some other kind of issue. This + // is weird and should probably never happen. + if (!isset($request['body'])) { + $response['err_message'] = 'No response was received for a request ' + . 'with no body. This could mean that you are saturating your ' + . 'network.'; + return self::createErrorResponse($handler, $request, $response); + } + + if (!Core::rewindBody($request)) { + $response['err_message'] = 'The connection unexpectedly failed ' + . 'without providing an error. The request would have been ' + . 'retried, but attempting to rewind the request body failed.'; + return self::createErrorResponse($handler, $request, $response); + } + + // Retry no more than 3 times before giving up. + if (!isset($request['curl']['retries'])) { + $request['curl']['retries'] = 1; + } elseif ($request['curl']['retries'] == 2) { + $response['err_message'] = 'The cURL request was retried 3 times ' + . 'and did no succeed. cURL was unable to rewind the body of ' + . 'the request and subsequent retries resulted in the same ' + . 'error. Turn on the debug option to see what went wrong. ' + . 'See https://bugs.php.net/bug.php?id=47204 for more information.'; + return self::createErrorResponse($handler, $request, $response); + } else { + $request['curl']['retries']++; + } + + return $handler($request); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php new file mode 100755 index 000000000..e00aa4eac --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php @@ -0,0 +1,135 @@ +handles = $this->ownedHandles = []; + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] + : new CurlFactory(); + $this->maxHandles = isset($options['max_handles']) + ? $options['max_handles'] + : 5; + } + + public function __destruct() + { + foreach ($this->handles as $handle) { + if (is_resource($handle)) { + curl_close($handle); + } + } + } + + /** + * @param array $request + * + * @return CompletedFutureArray + */ + public function __invoke(array $request) + { + return new CompletedFutureArray( + $this->_invokeAsArray($request) + ); + } + + /** + * @internal + * + * @param array $request + * + * @return array + */ + public function _invokeAsArray(array $request) + { + $factory = $this->factory; + + // Ensure headers are by reference. They're updated elsewhere. + $result = $factory($request, $this->checkoutEasyHandle()); + $h = $result[0]; + $hd =& $result[1]; + $bd = $result[2]; + Core::doSleep($request); + curl_exec($h); + $response = ['transfer_stats' => curl_getinfo($h)]; + $response['curl']['error'] = curl_error($h); + $response['curl']['errno'] = curl_errno($h); + $response['transfer_stats'] = array_merge($response['transfer_stats'], $response['curl']); + $this->releaseEasyHandle($h); + + return CurlFactory::createResponse([$this, '_invokeAsArray'], $request, $response, $hd, $bd); + } + + private function checkoutEasyHandle() + { + // Find an unused handle in the cache + if (false !== ($key = array_search(false, $this->ownedHandles, true))) { + $this->ownedHandles[$key] = true; + return $this->handles[$key]; + } + + // Add a new handle + $handle = curl_init(); + $id = (int) $handle; + $this->handles[$id] = $handle; + $this->ownedHandles[$id] = true; + + return $handle; + } + + private function releaseEasyHandle($handle) + { + $id = (int) $handle; + if (count($this->ownedHandles) > $this->maxHandles) { + curl_close($this->handles[$id]); + unset($this->handles[$id], $this->ownedHandles[$id]); + } else { + // curl_reset doesn't clear these out for some reason + static $unsetValues = [ + CURLOPT_HEADERFUNCTION => null, + CURLOPT_WRITEFUNCTION => null, + CURLOPT_READFUNCTION => null, + CURLOPT_PROGRESSFUNCTION => null, + ]; + curl_setopt_array($handle, $unsetValues); + curl_reset($handle); + $this->ownedHandles[$id] = false; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php new file mode 100755 index 000000000..b45f6c397 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php @@ -0,0 +1,250 @@ +_mh = $options['mh']; + } + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] : new CurlFactory(); + $this->selectTimeout = isset($options['select_timeout']) + ? $options['select_timeout'] : 1; + $this->maxHandles = isset($options['max_handles']) + ? $options['max_handles'] : 100; + } + + public function __get($name) + { + if ($name === '_mh') { + return $this->_mh = curl_multi_init(); + } + + throw new \BadMethodCallException(); + } + + public function __destruct() + { + // Finish any open connections before terminating the script. + if ($this->handles) { + $this->execute(); + } + + if (isset($this->_mh)) { + curl_multi_close($this->_mh); + unset($this->_mh); + } + } + + public function __invoke(array $request) + { + $factory = $this->factory; + $result = $factory($request); + $entry = [ + 'request' => $request, + 'response' => [], + 'handle' => $result[0], + 'headers' => &$result[1], + 'body' => $result[2], + 'deferred' => new Deferred(), + ]; + + $id = (int) $result[0]; + + $future = new FutureArray( + $entry['deferred']->promise(), + [$this, 'execute'], + function () use ($id) { + return $this->cancel($id); + } + ); + + $this->addRequest($entry); + + // Transfer outstanding requests if there are too many open handles. + if (count($this->handles) >= $this->maxHandles) { + $this->execute(); + } + + return $future; + } + + /** + * Runs until all outstanding connections have completed. + */ + public function execute() + { + do { + + if ($this->active && + curl_multi_select($this->_mh, $this->selectTimeout) === -1 + ) { + // Perform a usleep if a select returns -1. + // See: https://bugs.php.net/bug.php?id=61141 + usleep(250); + } + + // Add any delayed futures if needed. + if ($this->delays) { + $this->addDelays(); + } + + do { + $mrc = curl_multi_exec($this->_mh, $this->active); + } while ($mrc === CURLM_CALL_MULTI_PERFORM); + + $this->processMessages(); + + // If there are delays but no transfers, then sleep for a bit. + if (!$this->active && $this->delays) { + usleep(500); + } + + } while ($this->active || $this->handles); + } + + private function addRequest(array &$entry) + { + $id = (int) $entry['handle']; + $this->handles[$id] = $entry; + + // If the request is a delay, then add the reques to the curl multi + // pool only after the specified delay. + if (isset($entry['request']['client']['delay'])) { + $this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000); + } elseif (empty($entry['request']['future'])) { + curl_multi_add_handle($this->_mh, $entry['handle']); + } else { + curl_multi_add_handle($this->_mh, $entry['handle']); + // "lazy" futures are only sent once the pool has many requests. + if ($entry['request']['future'] !== 'lazy') { + do { + $mrc = curl_multi_exec($this->_mh, $this->active); + } while ($mrc === CURLM_CALL_MULTI_PERFORM); + $this->processMessages(); + } + } + } + + private function removeProcessed($id) + { + if (isset($this->handles[$id])) { + curl_multi_remove_handle( + $this->_mh, + $this->handles[$id]['handle'] + ); + curl_close($this->handles[$id]['handle']); + unset($this->handles[$id], $this->delays[$id]); + } + } + + /** + * Cancels a handle from sending and removes references to it. + * + * @param int $id Handle ID to cancel and remove. + * + * @return bool True on success, false on failure. + */ + private function cancel($id) + { + // Cannot cancel if it has been processed. + if (!isset($this->handles[$id])) { + return false; + } + + $handle = $this->handles[$id]['handle']; + unset($this->delays[$id], $this->handles[$id]); + curl_multi_remove_handle($this->_mh, $handle); + curl_close($handle); + + return true; + } + + private function addDelays() + { + $currentTime = microtime(true); + + foreach ($this->delays as $id => $delay) { + if ($currentTime >= $delay) { + unset($this->delays[$id]); + curl_multi_add_handle( + $this->_mh, + $this->handles[$id]['handle'] + ); + } + } + } + + private function processMessages() + { + while ($done = curl_multi_info_read($this->_mh)) { + $id = (int) $done['handle']; + + if (!isset($this->handles[$id])) { + // Probably was cancelled. + continue; + } + + $entry = $this->handles[$id]; + $entry['response']['transfer_stats'] = curl_getinfo($done['handle']); + + if ($done['result'] !== CURLM_OK) { + $entry['response']['curl']['errno'] = $done['result']; + if (function_exists('curl_strerror')) { + $entry['response']['curl']['error'] = curl_strerror($done['result']); + } + } + + $result = CurlFactory::createResponse( + $this, + $entry['request'], + $entry['response'], + $entry['headers'], + $entry['body'] + ); + + $this->removeProcessed($id); + $entry['deferred']->resolve($result); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/Middleware.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/Middleware.php new file mode 100755 index 000000000..6fa7318ab --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/Middleware.php @@ -0,0 +1,58 @@ +result = $result; + } + + public function __invoke(array $request) + { + Core::doSleep($request); + $response = is_callable($this->result) + ? call_user_func($this->result, $request) + : $this->result; + + if (is_array($response)) { + $response = new CompletedFutureArray($response + [ + 'status' => null, + 'body' => null, + 'headers' => [], + 'reason' => null, + 'effective_url' => null, + ]); + } elseif (!$response instanceof FutureArrayInterface) { + throw new \InvalidArgumentException( + 'Response must be an array or FutureArrayInterface. Found ' + . Core::describeType($request) + ); + } + + return $response; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php new file mode 100755 index 000000000..4bacec133 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php @@ -0,0 +1,414 @@ +options = $options; + } + + public function __invoke(array $request) + { + $url = Core::url($request); + Core::doSleep($request); + + try { + // Does not support the expect header. + $request = Core::removeHeader($request, 'Expect'); + $stream = $this->createStream($url, $request); + return $this->createResponse($request, $url, $stream); + } catch (RingException $e) { + return $this->createErrorResponse($url, $e); + } + } + + private function createResponse(array $request, $url, $stream) + { + $hdrs = $this->lastHeaders; + $this->lastHeaders = null; + $parts = explode(' ', array_shift($hdrs), 3); + $response = [ + 'version' => substr($parts[0], 5), + 'status' => $parts[1], + 'reason' => isset($parts[2]) ? $parts[2] : null, + 'headers' => Core::headersFromLines($hdrs), + 'effective_url' => $url, + ]; + + $stream = $this->checkDecode($request, $response, $stream); + + // If not streaming, then drain the response into a stream. + if (empty($request['client']['stream'])) { + $dest = isset($request['client']['save_to']) + ? $request['client']['save_to'] + : fopen('php://temp', 'r+'); + $stream = $this->drain($stream, $dest); + } + + $response['body'] = $stream; + + return new CompletedFutureArray($response); + } + + private function checkDecode(array $request, array $response, $stream) + { + // Automatically decode responses when instructed. + if (!empty($request['client']['decode_content'])) { + switch (Core::firstHeader($response, 'Content-Encoding', true)) { + case 'gzip': + case 'deflate': + $stream = new InflateStream(Stream::factory($stream)); + break; + } + } + + return $stream; + } + + /** + * Drains the stream into the "save_to" client option. + * + * @param resource $stream + * @param string|resource|StreamInterface $dest + * + * @return Stream + * @throws \RuntimeException when the save_to option is invalid. + */ + private function drain($stream, $dest) + { + if (is_resource($stream)) { + if (!is_resource($dest)) { + $stream = Stream::factory($stream); + } else { + stream_copy_to_stream($stream, $dest); + fclose($stream); + rewind($dest); + return $dest; + } + } + + // Stream the response into the destination stream + $dest = is_string($dest) + ? new Stream(Utils::open($dest, 'r+')) + : Stream::factory($dest); + + Utils::copyToStream($stream, $dest); + $dest->seek(0); + $stream->close(); + + return $dest; + } + + /** + * Creates an error response for the given stream. + * + * @param string $url + * @param RingException $e + * + * @return array + */ + private function createErrorResponse($url, RingException $e) + { + // Determine if the error was a networking error. + $message = $e->getMessage(); + + // This list can probably get more comprehensive. + if (strpos($message, 'getaddrinfo') // DNS lookup failed + || strpos($message, 'Connection refused') + ) { + $e = new ConnectException($e->getMessage(), 0, $e); + } + + return new CompletedFutureArray([ + 'status' => null, + 'body' => null, + 'headers' => [], + 'effective_url' => $url, + 'error' => $e + ]); + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Callable that returns stream resource + * + * @return resource + * @throws \RuntimeException on error + */ + private function createResource(callable $callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = [ + 'message' => $msg, + 'file' => $file, + 'line' => $line + ]; + return true; + }); + + $resource = $callback(); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource: '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new RingException(trim($message)); + } + + return $resource; + } + + private function createStream($url, array $request) + { + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + // HTTP/1.1 streams using the PHP stream wrapper require a + // Connection: close header + if ((!isset($request['version']) || $request['version'] == '1.1') + && !Core::hasHeader($request, 'Connection') + ) { + $request['headers']['Connection'] = ['close']; + } + + // Ensure SSL is verified by default + if (!isset($request['client']['verify'])) { + $request['client']['verify'] = true; + } + + $params = []; + $options = $this->getDefaultOptions($request); + + if (isset($request['client'])) { + foreach ($request['client'] as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $options, $value, $params); + } + } + } + + return $this->createStreamResource( + $url, + $request, + $options, + $this->createContext($request, $options, $params) + ); + } + + private function getDefaultOptions(array $request) + { + $headers = ""; + foreach ($request['headers'] as $name => $value) { + foreach ((array) $value as $val) { + $headers .= "$name: $val\r\n"; + } + } + + $context = [ + 'http' => [ + 'method' => $request['http_method'], + 'header' => $headers, + 'protocol_version' => isset($request['version']) ? $request['version'] : 1.1, + 'ignore_errors' => true, + 'follow_location' => 0, + ], + ]; + + $body = Core::body($request); + if (isset($body)) { + $context['http']['content'] = $body; + // Prevent the HTTP handler from adding a Content-Type header. + if (!Core::hasHeader($request, 'Content-Type')) { + $context['http']['header'] .= "Content-Type:\r\n"; + } + } + + $context['http']['header'] = rtrim($context['http']['header']); + + return $context; + } + + private function add_proxy(array $request, &$options, $value, &$params) + { + if (!is_array($value)) { + $options['http']['proxy'] = $value; + } else { + $scheme = isset($request['scheme']) ? $request['scheme'] : 'http'; + if (isset($value[$scheme])) { + $options['http']['proxy'] = $value[$scheme]; + } + } + } + + private function add_timeout(array $request, &$options, $value, &$params) + { + $options['http']['timeout'] = $value; + } + + private function add_verify(array $request, &$options, $value, &$params) + { + if ($value === true) { + // PHP 5.6 or greater will find the system cert by default. When + // < 5.6, use the Guzzle bundled cacert. + if (PHP_VERSION_ID < 50600) { + $options['ssl']['cafile'] = ClientUtils::getDefaultCaBundle(); + } + } elseif (is_string($value)) { + $options['ssl']['cafile'] = $value; + if (!file_exists($value)) { + throw new RingException("SSL CA bundle not found: $value"); + } + } elseif ($value === false) { + $options['ssl']['verify_peer'] = false; + $options['ssl']['allow_self_signed'] = true; + return; + } else { + throw new RingException('Invalid verify request option'); + } + + $options['ssl']['verify_peer'] = true; + $options['ssl']['allow_self_signed'] = false; + } + + private function add_cert(array $request, &$options, $value, &$params) + { + if (is_array($value)) { + $options['ssl']['passphrase'] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new RingException("SSL certificate not found: {$value}"); + } + + $options['ssl']['local_cert'] = $value; + } + + private function add_progress(array $request, &$options, $value, &$params) + { + $fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) { + if ($code == STREAM_NOTIFY_PROGRESS) { + $value($total, $transferred, null, null); + } + }; + + // Wrap the existing function if needed. + $params['notification'] = isset($params['notification']) + ? Core::callArray([$params['notification'], $fn]) + : $fn; + } + + private function add_debug(array $request, &$options, $value, &$params) + { + if ($value === false) { + return; + } + + static $map = [ + STREAM_NOTIFY_CONNECT => 'CONNECT', + STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', + STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', + STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', + STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', + STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', + STREAM_NOTIFY_PROGRESS => 'PROGRESS', + STREAM_NOTIFY_FAILURE => 'FAILURE', + STREAM_NOTIFY_COMPLETED => 'COMPLETED', + STREAM_NOTIFY_RESOLVE => 'RESOLVE', + ]; + + static $args = ['severity', 'message', 'message_code', + 'bytes_transferred', 'bytes_max']; + + $value = Core::getDebugResource($value); + $ident = $request['http_method'] . ' ' . Core::url($request); + $fn = function () use ($ident, $value, $map, $args) { + $passed = func_get_args(); + $code = array_shift($passed); + fprintf($value, '<%s> [%s] ', $ident, $map[$code]); + foreach (array_filter($passed) as $i => $v) { + fwrite($value, $args[$i] . ': "' . $v . '" '); + } + fwrite($value, "\n"); + }; + + // Wrap the existing function if needed. + $params['notification'] = isset($params['notification']) + ? Core::callArray([$params['notification'], $fn]) + : $fn; + } + + private function applyCustomOptions(array $request, array &$options) + { + if (!isset($request['client']['stream_context'])) { + return; + } + + if (!is_array($request['client']['stream_context'])) { + throw new RingException('stream_context must be an array'); + } + + $options = array_replace_recursive( + $options, + $request['client']['stream_context'] + ); + } + + private function createContext(array $request, array $options, array $params) + { + $this->applyCustomOptions($request, $options); + return $this->createResource( + function () use ($request, $options, $params) { + return stream_context_create($options, $params); + }, + $request, + $options + ); + } + + private function createStreamResource( + $url, + array $request, + array $options, + $context + ) { + return $this->createResource( + function () use ($url, $context) { + if (false === strpos($url, 'http')) { + trigger_error("URL is invalid: {$url}", E_USER_WARNING); + return null; + } + $resource = fopen($url, 'r', null, $context); + $this->lastHeaders = $http_response_header; + return $resource; + }, + $request, + $options + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Core.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Core.php new file mode 100755 index 000000000..dd7d1a0c5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Core.php @@ -0,0 +1,364 @@ + $value) { + if (!strcasecmp($name, $header)) { + $result = array_merge($result, $value); + } + } + } + + return $result; + } + + /** + * Gets a header value from a message as a string or null + * + * This method searches through the "headers" key of a message for a header + * using a case-insensitive search. The lines of the header are imploded + * using commas into a single string return value. + * + * @param array $message Request or response hash. + * @param string $header Header to retrieve + * + * @return string|null Returns the header string if found, or null if not. + */ + public static function header($message, $header) + { + $match = self::headerLines($message, $header); + return $match ? implode(', ', $match) : null; + } + + /** + * Returns the first header value from a message as a string or null. If + * a header line contains multiple values separated by a comma, then this + * function will return the first value in the list. + * + * @param array $message Request or response hash. + * @param string $header Header to retrieve + * + * @return string|null Returns the value as a string if found. + */ + public static function firstHeader($message, $header) + { + if (!empty($message['headers'])) { + foreach ($message['headers'] as $name => $value) { + if (!strcasecmp($name, $header)) { + // Return the match itself if it is a single value. + $pos = strpos($value[0], ','); + return $pos ? substr($value[0], 0, $pos) : $value[0]; + } + } + } + + return null; + } + + /** + * Returns true if a message has the provided case-insensitive header. + * + * @param array $message Request or response hash. + * @param string $header Header to check + * + * @return bool + */ + public static function hasHeader($message, $header) + { + if (!empty($message['headers'])) { + foreach ($message['headers'] as $name => $value) { + if (!strcasecmp($name, $header)) { + return true; + } + } + } + + return false; + } + + /** + * Parses an array of header lines into an associative array of headers. + * + * @param array $lines Header lines array of strings in the following + * format: "Name: Value" + * @return array + */ + public static function headersFromLines($lines) + { + $headers = []; + + foreach ($lines as $line) { + $parts = explode(':', $line, 2); + $headers[trim($parts[0])][] = isset($parts[1]) + ? trim($parts[1]) + : null; + } + + return $headers; + } + + /** + * Removes a header from a message using a case-insensitive comparison. + * + * @param array $message Message that contains 'headers' + * @param string $header Header to remove + * + * @return array + */ + public static function removeHeader(array $message, $header) + { + if (isset($message['headers'])) { + foreach (array_keys($message['headers']) as $key) { + if (!strcasecmp($header, $key)) { + unset($message['headers'][$key]); + } + } + } + + return $message; + } + + /** + * Replaces any existing case insensitive headers with the given value. + * + * @param array $message Message that contains 'headers' + * @param string $header Header to set. + * @param array $value Value to set. + * + * @return array + */ + public static function setHeader(array $message, $header, array $value) + { + $message = self::removeHeader($message, $header); + $message['headers'][$header] = $value; + + return $message; + } + + /** + * Creates a URL string from a request. + * + * If the "url" key is present on the request, it is returned, otherwise + * the url is built up based on the scheme, host, uri, and query_string + * request values. + * + * @param array $request Request to get the URL from + * + * @return string Returns the request URL as a string. + * @throws \InvalidArgumentException if no Host header is present. + */ + public static function url(array $request) + { + if (isset($request['url'])) { + return $request['url']; + } + + $uri = (isset($request['scheme']) + ? $request['scheme'] : 'http') . '://'; + + if ($host = self::header($request, 'host')) { + $uri .= $host; + } else { + throw new \InvalidArgumentException('No Host header was provided'); + } + + if (isset($request['uri'])) { + $uri .= $request['uri']; + } + + if (isset($request['query_string'])) { + $uri .= '?' . $request['query_string']; + } + + return $uri; + } + + /** + * Reads the body of a message into a string. + * + * @param array|FutureArrayInterface $message Array containing a "body" key + * + * @return null|string Returns the body as a string or null if not set. + * @throws \InvalidArgumentException if a request body is invalid. + */ + public static function body($message) + { + if (!isset($message['body'])) { + return null; + } + + if ($message['body'] instanceof StreamInterface) { + return (string) $message['body']; + } + + switch (gettype($message['body'])) { + case 'string': + return $message['body']; + case 'resource': + return stream_get_contents($message['body']); + case 'object': + if ($message['body'] instanceof \Iterator) { + return implode('', iterator_to_array($message['body'])); + } elseif (method_exists($message['body'], '__toString')) { + return (string) $message['body']; + } + default: + throw new \InvalidArgumentException('Invalid request body: ' + . self::describeType($message['body'])); + } + } + + /** + * Rewind the body of the provided message if possible. + * + * @param array $message Message that contains a 'body' field. + * + * @return bool Returns true on success, false on failure + */ + public static function rewindBody($message) + { + if ($message['body'] instanceof StreamInterface) { + return $message['body']->seek(0); + } + + if ($message['body'] instanceof \Generator) { + return false; + } + + if ($message['body'] instanceof \Iterator) { + $message['body']->rewind(); + return true; + } + + if (is_resource($message['body'])) { + return rewind($message['body']); + } + + return is_string($message['body']) + || (is_object($message['body']) + && method_exists($message['body'], '__toString')); + } + + /** + * Debug function used to describe the provided value type and class. + * + * @param mixed $input + * + * @return string Returns a string containing the type of the variable and + * if a class is provided, the class name. + */ + public static function describeType($input) + { + switch (gettype($input)) { + case 'object': + return 'object(' . get_class($input) . ')'; + case 'array': + return 'array(' . count($input) . ')'; + default: + ob_start(); + var_dump($input); + // normalize float vs double + return str_replace('double(', 'float(', rtrim(ob_get_clean())); + } + } + + /** + * Sleep for the specified amount of time specified in the request's + * ['client']['delay'] option if present. + * + * This function should only be used when a non-blocking sleep is not + * possible. + * + * @param array $request Request to sleep + */ + public static function doSleep(array $request) + { + if (isset($request['client']['delay'])) { + usleep($request['client']['delay'] * 1000); + } + } + + /** + * Returns a proxied future that modifies the dereferenced value of another + * future using a promise. + * + * @param FutureArrayInterface $future Future to wrap with a new future + * @param callable $onFulfilled Invoked when the future fulfilled + * @param callable $onRejected Invoked when the future rejected + * @param callable $onProgress Invoked when the future progresses + * + * @return FutureArray + */ + public static function proxy( + FutureArrayInterface $future, + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return new FutureArray( + $future->then($onFulfilled, $onRejected, $onProgress), + [$future, 'wait'], + [$future, 'cancel'] + ); + } + + /** + * Returns a debug stream based on the provided variable. + * + * @param mixed $value Optional value + * + * @return resource + */ + public static function getDebugResource($value = null) + { + if (is_resource($value)) { + return $value; + } elseif (defined('STDOUT')) { + return STDOUT; + } else { + return fopen('php://output', 'w'); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php new file mode 100755 index 000000000..95b353acf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php @@ -0,0 +1,7 @@ +wrappedPromise = $promise; + $this->waitfn = $wait; + $this->cancelfn = $cancel; + } + + public function wait() + { + if (!$this->isRealized) { + $this->addShadow(); + if (!$this->isRealized && $this->waitfn) { + $this->invokeWait(); + } + if (!$this->isRealized) { + $this->error = new RingException('Waiting did not resolve future'); + } + } + + if ($this->error) { + throw $this->error; + } + + return $this->result; + } + + public function promise() + { + return $this->wrappedPromise; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return $this->wrappedPromise->then($onFulfilled, $onRejected, $onProgress); + } + + public function cancel() + { + if (!$this->isRealized) { + $cancelfn = $this->cancelfn; + $this->waitfn = $this->cancelfn = null; + $this->isRealized = true; + $this->error = new CancelledFutureAccessException(); + if ($cancelfn) { + $cancelfn($this); + } + } + } + + private function addShadow() + { + // Get the result and error when the promise is resolved. Note that + // calling this function might trigger the resolution immediately. + $this->wrappedPromise->then( + function ($value) { + $this->isRealized = true; + $this->result = $value; + $this->waitfn = $this->cancelfn = null; + }, + function ($error) { + $this->isRealized = true; + $this->error = $error; + $this->waitfn = $this->cancelfn = null; + } + ); + } + + private function invokeWait() + { + try { + $wait = $this->waitfn; + $this->waitfn = null; + $wait(); + } catch (\Exception $e) { + // Defer can throw to reject. + $this->error = $e; + $this->isRealized = true; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php new file mode 100755 index 000000000..0a90c939f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php @@ -0,0 +1,43 @@ +result[$offset]); + } + + public function offsetGet($offset) + { + return $this->result[$offset]; + } + + public function offsetSet($offset, $value) + { + $this->result[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->result[$offset]); + } + + public function count() + { + return count($this->result); + } + + public function getIterator() + { + return new \ArrayIterator($this->result); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php new file mode 100755 index 000000000..0d25af72d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php @@ -0,0 +1,57 @@ +result = $result; + $this->error = $e; + } + + public function wait() + { + if ($this->error) { + throw $this->error; + } + + return $this->result; + } + + public function cancel() {} + + public function promise() + { + if (!$this->cachedPromise) { + $this->cachedPromise = $this->error + ? new RejectedPromise($this->error) + : new FulfilledPromise($this->result); + } + + return $this->cachedPromise; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null, + callable $onProgress = null + ) { + return $this->promise()->then($onFulfilled, $onRejected, $onProgress); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php new file mode 100755 index 000000000..3d64c9643 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php @@ -0,0 +1,40 @@ +_value[$offset]); + } + + public function offsetGet($offset) + { + return $this->_value[$offset]; + } + + public function offsetSet($offset, $value) + { + $this->_value[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->_value[$offset]); + } + + public function count() + { + return count($this->_value); + } + + public function getIterator() + { + return new \ArrayIterator($this->_value); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php new file mode 100755 index 000000000..58f5f7367 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php @@ -0,0 +1,11 @@ +_value = $this->wait(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php new file mode 100755 index 000000000..ebde187cf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php @@ -0,0 +1,821 @@ + 200, + 'headers' => [ + 'Foo' => ['Bar'], + 'Baz' => ['bam'], + 'Content-Length' => [2], + ], + 'body' => 'hi', + ]]); + + $stream = Stream::factory(); + + $request = [ + 'http_method' => 'PUT', + 'headers' => [ + 'host' => [Server::$url], + 'Hi' => [' 123'], + ], + 'body' => 'testing', + 'client' => ['save_to' => $stream], + ]; + + $f = new CurlFactory(); + $result = $f($request); + $this->assertInternalType('array', $result); + $this->assertCount(3, $result); + $this->assertInternalType('resource', $result[0]); + $this->assertInternalType('array', $result[1]); + $this->assertSame($stream, $result[2]); + curl_close($result[0]); + + $this->assertEquals('PUT', $_SERVER['_curl'][CURLOPT_CUSTOMREQUEST]); + $this->assertEquals( + 'http://http://127.0.0.1:8125/', + $_SERVER['_curl'][CURLOPT_URL] + ); + // Sends via post fields when the request is small enough + $this->assertEquals('testing', $_SERVER['_curl'][CURLOPT_POSTFIELDS]); + $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_RETURNTRANSFER]); + $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_HEADER]); + $this->assertEquals(150, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT]); + $this->assertInstanceOf('Closure', $_SERVER['_curl'][CURLOPT_HEADERFUNCTION]); + + if (defined('CURLOPT_PROTOCOLS')) { + $this->assertEquals( + CURLPROTO_HTTP | CURLPROTO_HTTPS, + $_SERVER['_curl'][CURLOPT_PROTOCOLS] + ); + } + + $this->assertContains('Expect:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('Accept:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('Content-Type:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('Hi: 123', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + $this->assertContains('host: http://127.0.0.1:8125/', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); + } + + public function testSendsHeadRequests() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + ]); + $response->wait(); + $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]); + $checks = [CURLOPT_WRITEFUNCTION, CURLOPT_READFUNCTION, CURLOPT_FILE, CURLOPT_INFILE]; + foreach ($checks as $check) { + $this->assertArrayNotHasKey($check, $_SERVER['_curl']); + } + $this->assertEquals('HEAD', Server::received()[0]['http_method']); + } + + public function testCanAddCustomCurlOptions() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]], + ]); + $this->assertEquals(10, $_SERVER['_curl'][CURLOPT_LOW_SPEED_LIMIT]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage SSL CA bundle not found: /does/not/exist + */ + public function testValidatesVerify() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => '/does/not/exist'], + ]); + } + + public function testCanSetVerifyToFile() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => __FILE__], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_CAINFO]); + $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + } + + public function testAddsVerifyAsTrue() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => true], + ]); + $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + $this->assertArrayNotHasKey(CURLOPT_CAINFO, $_SERVER['_curl']); + } + + public function testCanDisableVerify() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['verify' => false], + ]); + $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(false, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); + } + + public function testAddsProxy() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['proxy' => 'http://bar.com'], + ]); + $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); + } + + public function testAddsViaScheme() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'scheme' => 'http', + 'headers' => ['host' => ['foo.com']], + 'client' => [ + 'proxy' => ['http' => 'http://bar.com', 'https' => 'https://t'], + ], + ]); + $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage SSL private key not found: /does/not/exist + */ + public function testValidatesSslKey() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['ssl_key' => '/does/not/exist'], + ]); + } + + public function testAddsSslKey() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['ssl_key' => __FILE__], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); + } + + public function testAddsSslKeyWithPassword() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['ssl_key' => [__FILE__, 'test']], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); + $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLKEYPASSWD]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage SSL certificate not found: /does/not/exist + */ + public function testValidatesCert() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['cert' => '/does/not/exist'], + ]); + } + + public function testAddsCert() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['cert' => __FILE__], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); + } + + public function testAddsCertWithPassword() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['cert' => [__FILE__, 'test']], + ]); + $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); + $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLCERTPASSWD]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage progress client option must be callable + */ + public function testValidatesProgress() + { + $f = new CurlFactory(); + $f([ + 'http_method' => 'GET', + 'headers' => ['host' => ['foo.com']], + 'client' => ['progress' => 'foo'], + ]); + } + + public function testEmitsDebugInfoToStream() + { + $res = fopen('php://memory', 'r+'); + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + 'client' => ['debug' => $res], + ]); + $response->wait(); + rewind($res); + $output = str_replace("\r", '', stream_get_contents($res)); + $this->assertContains( + "> HEAD / HTTP/1.1\nhost: 127.0.0.1:8125\n\n", + $output + ); + $this->assertContains("< HTTP/1.1 200", $output); + fclose($res); + } + + public function testEmitsProgressToFunction() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $called = []; + $response = $a([ + 'http_method' => 'HEAD', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'progress' => function () use (&$called) { + $called[] = func_get_args(); + }, + ], + ]); + $response->wait(); + $this->assertNotEmpty($called); + foreach ($called as $call) { + $this->assertCount(4, $call); + } + } + + private function addDecodeResponse($withEncoding = true) + { + $content = gzencode('test'); + $response = [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => ['Content-Length' => [strlen($content)]], + 'body' => $content, + ]; + + if ($withEncoding) { + $response['headers']['Content-Encoding'] = ['gzip']; + } + + Server::flush(); + Server::enqueue([$response]); + + return $content; + } + + public function testDecodesGzippedResponses() + { + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['decode_content' => true], + ]); + $response->wait(); + $this->assertEquals('test', Core::body($response)); + $this->assertEquals('', $_SERVER['_curl'][CURLOPT_ENCODING]); + $sent = Server::received()[0]; + $this->assertNull(Core::header($sent, 'Accept-Encoding')); + } + + public function testDecodesGzippedResponsesWithHeader() + { + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => [ + 'host' => [Server::$host], + 'Accept-Encoding' => ['gzip'], + ], + 'client' => ['decode_content' => true], + ]); + $response->wait(); + $this->assertEquals('gzip', $_SERVER['_curl'][CURLOPT_ENCODING]); + $sent = Server::received()[0]; + $this->assertEquals('gzip', Core::header($sent, 'Accept-Encoding')); + $this->assertEquals('test', Core::body($response)); + } + + public function testDoesNotForceDecode() + { + $content = $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['decode_content' => false], + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertNull(Core::header($sent, 'Accept-Encoding')); + $this->assertEquals($content, Core::body($response)); + } + + public function testProtocolVersion() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'version' => 1.0, + ]); + $this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesSaveTo() + { + $handler = new CurlMultiHandler(); + $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['save_to' => true], + ]); + } + + public function testSavesToStream() + { + $stream = fopen('php://memory', 'r+'); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'decode_content' => true, + 'save_to' => $stream, + ], + ]); + $response->wait(); + rewind($stream); + $this->assertEquals('test', stream_get_contents($stream)); + } + + public function testSavesToGuzzleStream() + { + $stream = Stream::factory(); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'decode_content' => true, + 'save_to' => $stream, + ], + ]); + $response->wait(); + $this->assertEquals('test', (string) $stream); + } + + public function testSavesToFileOnDisk() + { + $tmpfile = tempnam(sys_get_temp_dir(), 'testfile'); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'decode_content' => true, + 'save_to' => $tmpfile, + ], + ]); + $response->wait(); + $this->assertEquals('test', file_get_contents($tmpfile)); + unlink($tmpfile); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesBody() + { + $handler = new CurlMultiHandler(); + $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => false, + ]); + } + + public function testAddsLargePayloadFromStreamWithNoSizeUsingChunked() + { + $stream = Stream::factory('foo'); + $stream = FnStream::decorate($stream, [ + 'getSize' => function () { + return null; + } + ]); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => $stream, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding')); + $this->assertNull(Core::header($sent, 'Content-Length')); + $this->assertEquals('foo', $sent['body']); + } + + public function testAddsPayloadFromIterator() + { + $iter = new \ArrayIterator(['f', 'o', 'o']); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => $iter, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding')); + $this->assertNull(Core::header($sent, 'Content-Length')); + $this->assertEquals('foo', $sent['body']); + } + + public function testAddsPayloadFromResource() + { + $res = fopen('php://memory', 'r+'); + $data = str_repeat('.', 1000000); + fwrite($res, $data); + rewind($res); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => [ + 'host' => [Server::$host], + 'content-length' => [1000000], + ], + 'body' => $res, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertNull(Core::header($sent, 'Transfer-Encoding')); + $this->assertEquals(1000000, Core::header($sent, 'Content-Length')); + $this->assertEquals($data, $sent['body']); + } + + public function testAddsContentLengthFromStream() + { + $stream = Stream::factory('foo'); + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'body' => $stream, + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals(3, Core::header($sent, 'Content-Length')); + $this->assertNull(Core::header($sent, 'Transfer-Encoding')); + $this->assertEquals('foo', $sent['body']); + } + + public function testDoesNotAddMultipleContentLengthHeaders() + { + $this->addDecodeResponse(); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => [ + 'host' => [Server::$host], + 'content-length' => [3], + ], + 'body' => 'foo', + ]); + $response->wait(); + $sent = Server::received()[0]; + $this->assertEquals(3, Core::header($sent, 'Content-Length')); + $this->assertNull(Core::header($sent, 'Transfer-Encoding')); + $this->assertEquals('foo', $sent['body']); + } + + public function testSendsPostWithNoBodyOrDefaultContentType() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $handler = new CurlMultiHandler(); + $response = $handler([ + 'http_method' => 'POST', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + ]); + $response->wait(); + $received = Server::received()[0]; + $this->assertEquals('POST', $received['http_method']); + $this->assertNull(Core::header($received, 'content-type')); + $this->assertSame('0', Core::firstHeader($received, 'content-length')); + } + + public function testParseProtocolVersion() + { + $res = CurlFactory::createResponse( + function () {}, + [], + ['curl' => ['errno' => null]], + ['HTTP/1.1 200 Ok'], + null + ); + + $this->assertSame('1.1', $res['version']); + } + + public function testFailsWhenNoResponseAndNoBody() + { + $res = CurlFactory::createResponse(function () {}, [], [], [], null); + $this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']); + $this->assertContains( + 'No response was received for a request with no body', + $res['error']->getMessage() + ); + } + + public function testFailsWhenCannotRewindRetry() + { + $res = CurlFactory::createResponse(function () {}, [ + 'body' => new NoSeekStream(Stream::factory('foo')) + ], [], [], null); + $this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']); + $this->assertContains( + 'rewind the request body failed', + $res['error']->getMessage() + ); + } + + public function testRetriesWhenBodyCanBeRewound() + { + $callHandler = $called = false; + $res = CurlFactory::createResponse(function () use (&$callHandler) { + $callHandler = true; + return ['status' => 200]; + }, [ + 'body' => FnStream::decorate(Stream::factory('test'), [ + 'seek' => function () use (&$called) { + $called = true; + return true; + } + ]) + ], [], [], null); + + $this->assertTrue($callHandler); + $this->assertTrue($called); + $this->assertEquals('200', $res['status']); + } + + public function testFailsWhenRetryMoreThanThreeTimes() + { + $call = 0; + $mock = new MockHandler(function (array $request) use (&$mock, &$call) { + $call++; + return CurlFactory::createResponse($mock, $request, [], [], null); + }); + $response = $mock([ + 'http_method' => 'GET', + 'body' => 'test', + ]); + $this->assertEquals(3, $call); + $this->assertArrayHasKey('error', $response); + $this->assertContains( + 'The cURL request was retried 3 times', + $response['error']->getMessage() + ); + } + + public function testHandles100Continue() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => '200', + 'reason' => 'OK', + 'headers' => [ + 'Test' => ['Hello'], + 'Content-Length' => ['4'], + ], + 'body' => 'test', + ], + ]); + + $request = [ + 'http_method' => 'PUT', + 'headers' => [ + 'Host' => [Server::$host], + 'Expect' => ['100-Continue'], + ], + 'body' => 'test', + ]; + + $handler = new CurlMultiHandler(); + $response = $handler($request)->wait(); + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals(['Hello'], $response['headers']['Test']); + $this->assertEquals(['4'], $response['headers']['Content-Length']); + $this->assertEquals('test', Core::body($response)); + } + + public function testCreatesConnectException() + { + $m = new \ReflectionMethod('GuzzleHttp\Ring\Client\CurlFactory', 'createErrorResponse'); + $m->setAccessible(true); + $response = $m->invoke( + null, + function () {}, + [], + [ + 'err_message' => 'foo', + 'curl' => [ + 'errno' => CURLE_COULDNT_CONNECT, + ] + ] + ); + $this->assertInstanceOf('GuzzleHttp\Ring\Exception\ConnectException', $response['error']); + } + + public function testParsesLastResponseOnly() + { + $response1 = [ + 'status' => 301, + 'headers' => [ + 'Content-Length' => ['0'], + 'Location' => ['/foo'] + ] + ]; + + $response2 = [ + 'status' => 200, + 'headers' => [ + 'Content-Length' => ['0'], + 'Foo' => ['bar'] + ] + ]; + + Server::flush(); + Server::enqueue([$response1, $response2]); + + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['Host' => [Server::$host]], + 'client' => [ + 'curl' => [ + CURLOPT_FOLLOWLOCATION => true + ] + ] + ])->wait(); + + $this->assertEquals(1, $response['transfer_stats']['redirect_count']); + $this->assertEquals('http://127.0.0.1:8125/foo', $response['effective_url']); + $this->assertEquals(['bar'], $response['headers']['Foo']); + $this->assertEquals(200, $response['status']); + $this->assertFalse(Core::hasHeader($response, 'Location')); + } + + public function testMaintainsMultiHeaderOrder() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => 200, + 'headers' => [ + 'Content-Length' => ['0'], + 'Foo' => ['a', 'b'], + 'foo' => ['c', 'd'], + ] + ] + ]); + + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['Host' => [Server::$host]] + ])->wait(); + + $this->assertEquals( + ['a', 'b', 'c', 'd'], + Core::headerLines($response, 'Foo') + ); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Directory /path/to/does/not does not exist for save_to value of /path/to/does/not/exist.txt + */ + public function testThrowsWhenDirNotFound() + { + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$url]], + 'client' => ['save_to' => '/path/to/does/not/exist.txt'], + ]; + + $f = new CurlFactory(); + $f($request); + } +} + +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php new file mode 100755 index 000000000..ba03b8cd3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php @@ -0,0 +1,96 @@ +markTestSkipped('curl_reset() is not available'); + } + } + + protected function getHandler($factory = null, $options = []) + { + return new CurlHandler($options); + } + + public function testCanSetMaxHandles() + { + $a = new CurlHandler(['max_handles' => 10]); + $this->assertEquals(10, $this->readAttribute($a, 'maxHandles')); + } + + public function testCreatesCurlErrors() + { + $handler = new CurlHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => ['localhost:123']], + 'client' => ['timeout' => 0.001, 'connect_timeout' => 0.001], + ]); + $this->assertNull($response['status']); + $this->assertNull($response['reason']); + $this->assertEquals([], $response['headers']); + $this->assertInstanceOf( + 'GuzzleHttp\Ring\Exception\RingException', + $response['error'] + ); + + $this->assertEquals( + 1, + preg_match('/^cURL error \d+: .*$/', $response['error']->getMessage()) + ); + } + + public function testReleasesAdditionalEasyHandles() + { + Server::flush(); + $response = [ + 'status' => 200, + 'headers' => ['Content-Length' => [4]], + 'body' => 'test', + ]; + + Server::enqueue([$response, $response, $response, $response]); + $a = new CurlHandler(['max_handles' => 2]); + + $fn = function () use (&$calls, $a, &$fn) { + if (++$calls < 4) { + $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['progress' => $fn], + ]); + } + }; + + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => [ + 'progress' => $fn, + ], + ]; + + $a($request); + $this->assertCount(2, $this->readAttribute($a, 'handles')); + } + + public function testReusesHandles() + { + Server::flush(); + $response = ['status' => 200]; + Server::enqueue([$response, $response]); + $a = new CurlHandler(); + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]; + $a($request); + $a($request); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php new file mode 100755 index 000000000..9228f1cea --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php @@ -0,0 +1,165 @@ + 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(200, $response['status']); + $this->assertArrayHasKey('transfer_stats', $response); + $realUrl = trim($response['transfer_stats']['url'], '/'); + $this->assertEquals(trim(Server::$url, '/'), $realUrl); + $this->assertArrayHasKey('effective_url', $response); + $this->assertEquals( + trim(Server::$url, '/'), + trim($response['effective_url'], '/') + ); + } + + public function testCreatesErrorResponses() + { + $url = 'http://localhost:123/'; + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => ['localhost:123']], + ]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertNull($response['status']); + $this->assertNull($response['reason']); + $this->assertEquals([], $response['headers']); + $this->assertArrayHasKey('error', $response); + $this->assertContains('cURL error ', $response['error']->getMessage()); + $this->assertArrayHasKey('transfer_stats', $response); + $this->assertEquals( + trim($url, '/'), + trim($response['transfer_stats']['url'], '/') + ); + $this->assertArrayHasKey('effective_url', $response); + $this->assertEquals( + trim($url, '/'), + trim($response['effective_url'], '/') + ); + } + + public function testSendsFuturesWhenDestructed() + { + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $a->__destruct(); + $this->assertEquals(200, $response['status']); + } + + public function testCanSetMaxHandles() + { + $a = new CurlMultiHandler(['max_handles' => 2]); + $this->assertEquals(2, $this->readAttribute($a, 'maxHandles')); + } + + public function testCanSetSelectTimeout() + { + $a = new CurlMultiHandler(['select_timeout' => 2]); + $this->assertEquals(2, $this->readAttribute($a, 'selectTimeout')); + } + + public function testSendsFuturesWhenMaxHandlesIsReached() + { + $request = [ + 'http_method' => 'PUT', + 'headers' => ['host' => [Server::$host]], + 'future' => 'lazy', // passing this to control the test + ]; + $response = ['status' => 200]; + Server::flush(); + Server::enqueue([$response, $response, $response]); + $a = new CurlMultiHandler(['max_handles' => 3]); + for ($i = 0; $i < 5; $i++) { + $responses[] = $a($request); + } + $this->assertCount(3, Server::received()); + $responses[3]->cancel(); + $responses[4]->cancel(); + } + + public function testCanCancel() + { + Server::flush(); + $response = ['status' => 200]; + Server::enqueue(array_fill_keys(range(0, 10), $response)); + $a = new CurlMultiHandler(); + $responses = []; + + for ($i = 0; $i < 10; $i++) { + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'future' => 'lazy', + ]); + $response->cancel(); + $responses[] = $response; + } + + $this->assertCount(0, Server::received()); + + foreach ($responses as $response) { + $this->assertTrue($this->readAttribute($response, 'isRealized')); + } + } + + public function testCannotCancelFinished() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + ]); + $response->wait(); + $response->cancel(); + } + + public function testDelaysInParallel() + { + Server::flush(); + Server::enqueue([['status' => 200]]); + $a = new CurlMultiHandler(); + $expected = microtime(true) + (100 / 1000); + $response = $a([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'client' => ['delay' => 100], + ]); + $response->wait(); + $this->assertGreaterThanOrEqual($expected, microtime(true)); + } + + public function testSendsNonLazyFutures() + { + $request = [ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'future' => true, + ]; + Server::flush(); + Server::enqueue([['status' => 202]]); + $a = new CurlMultiHandler(); + $response = $a($request); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(202, $response['status']); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php new file mode 100755 index 000000000..a47bb30ba --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php @@ -0,0 +1,65 @@ + 200]); + $calledA = false; + $a = function (array $req) use (&$calledA, $future) { + $calledA = true; + return $future; + }; + $calledB = false; + $b = function (array $req) use (&$calledB) { $calledB = true; }; + $s = Middleware::wrapFuture($a, $b); + $s([]); + $this->assertTrue($calledA); + $this->assertFalse($calledB); + } + + public function testFutureCallsStreamingHandler() + { + $future = new CompletedFutureArray(['status' => 200]); + $calledA = false; + $a = function (array $req) use (&$calledA) { $calledA = true; }; + $calledB = false; + $b = function (array $req) use (&$calledB, $future) { + $calledB = true; + return $future; + }; + $s = Middleware::wrapFuture($a, $b); + $result = $s(['client' => ['future' => true]]); + $this->assertFalse($calledA); + $this->assertTrue($calledB); + $this->assertSame($future, $result); + } + + public function testStreamingCallsDefaultHandler() + { + $calledA = false; + $a = function (array $req) use (&$calledA) { $calledA = true; }; + $calledB = false; + $b = function (array $req) use (&$calledB) { $calledB = true; }; + $s = Middleware::wrapStreaming($a, $b); + $s([]); + $this->assertTrue($calledA); + $this->assertFalse($calledB); + } + + public function testStreamingCallsStreamingHandler() + { + $calledA = false; + $a = function (array $req) use (&$calledA) { $calledA = true; }; + $calledB = false; + $b = function (array $req) use (&$calledB) { $calledB = true; }; + $s = Middleware::wrapStreaming($a, $b); + $s(['client' => ['stream' => true]]); + $this->assertFalse($calledA); + $this->assertTrue($calledB); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php new file mode 100755 index 000000000..26bcd6cdc --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php @@ -0,0 +1,86 @@ + 200]); + $response = $mock([]); + $this->assertEquals(200, $response['status']); + $this->assertEquals([], $response['headers']); + $this->assertNull($response['body']); + $this->assertNull($response['reason']); + $this->assertNull($response['effective_url']); + } + + public function testReturnsFutures() + { + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred) { + $deferred->resolve(['status' => 200]); + } + ); + $mock = new MockHandler($future); + $response = $mock([]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(200, $response['status']); + } + + public function testReturnsFuturesWithThenCall() + { + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () use ($deferred) { + $deferred->resolve(['status' => 200]); + } + ); + $mock = new MockHandler($future); + $response = $mock([]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $this->assertEquals(200, $response['status']); + $req = null; + $promise = $response->then(function ($value) use (&$req) { + $req = $value; + $this->assertEquals(200, $req['status']); + }); + $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); + $this->assertEquals(200, $req['status']); + } + + public function testReturnsFuturesAndProxiesCancel() + { + $c = null; + $deferred = new Deferred(); + $future = new FutureArray( + $deferred->promise(), + function () {}, + function () use (&$c) { + $c = true; + return true; + } + ); + $mock = new MockHandler($future); + $response = $mock([]); + $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); + $response->cancel(); + $this->assertTrue($c); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Response must be an array or FutureArrayInterface. Found + */ + public function testEnsuresMockIsValid() + { + $mock = new MockHandler('foo'); + $mock([]); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/Server.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/Server.php new file mode 100755 index 000000000..14665a556 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/Server.php @@ -0,0 +1,183 @@ + [], 'reason' => '', 'body' => '']; + $data[] = $response; + } + + self::send('PUT', '/guzzle-server/responses', json_encode($data)); + } + + /** + * Get all of the received requests as a RingPHP request structure. + * + * @return array + * @throws \RuntimeException + */ + public static function received() + { + if (!self::$started) { + return []; + } + + $response = self::send('GET', '/guzzle-server/requests'); + $body = Core::body($response); + $result = json_decode($body, true); + if ($result === false) { + throw new \RuntimeException('Error decoding response: ' + . json_last_error()); + } + + foreach ($result as &$res) { + if (isset($res['uri'])) { + $res['resource'] = $res['uri']; + } + if (isset($res['query_string'])) { + $res['resource'] .= '?' . $res['query_string']; + } + if (!isset($res['resource'])) { + $res['resource'] = ''; + } + // Ensure that headers are all arrays + if (isset($res['headers'])) { + foreach ($res['headers'] as &$h) { + $h = (array) $h; + } + unset($h); + } + } + + unset($res); + return $result; + } + + /** + * Stop running the node.js server + */ + public static function stop() + { + if (self::$started) { + self::send('DELETE', '/guzzle-server'); + } + + self::$started = false; + } + + public static function wait($maxTries = 20) + { + $tries = 0; + while (!self::isListening() && ++$tries < $maxTries) { + usleep(100000); + } + + if (!self::isListening()) { + throw new \RuntimeException('Unable to contact node.js server'); + } + } + + public static function start() + { + if (self::$started) { + return; + } + + try { + self::wait(); + } catch (\Exception $e) { + exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' + . self::$port . ' >> /tmp/server.log 2>&1 &'); + self::wait(); + } + + self::$started = true; + } + + private static function isListening() + { + $response = self::send('GET', '/guzzle-server/perf', null, [ + 'connect_timeout' => 1, + 'timeout' => 1 + ]); + + return !isset($response['error']); + } + + private static function send( + $method, + $path, + $body = null, + array $client = [] + ) { + $handler = new StreamHandler(); + + $request = [ + 'http_method' => $method, + 'uri' => $path, + 'request_port' => 8125, + 'headers' => ['host' => ['127.0.0.1:8125']], + 'body' => $body, + 'client' => $client, + ]; + + if ($body) { + $request['headers']['content-length'] = [strlen($body)]; + } + + return $handler($request); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php new file mode 100755 index 000000000..3cb9a8e1e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php @@ -0,0 +1,480 @@ +queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => [ + 'host' => [Server::$host], + 'Foo' => ['Bar'], + ], + ]); + + $this->assertEquals('1.1', $response['version']); + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals(['Bar'], $response['headers']['Foo']); + $this->assertEquals(['8'], $response['headers']['Content-Length']); + $this->assertEquals('hi there', Core::body($response)); + + $sent = Server::received()[0]; + $this->assertEquals('GET', $sent['http_method']); + $this->assertEquals('/', $sent['resource']); + $this->assertEquals(['127.0.0.1:8125'], $sent['headers']['host']); + $this->assertEquals('Bar', Core::header($sent, 'foo')); + } + + public function testAddsErrorToResponse() + { + $handler = new StreamHandler(); + $result = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => ['localhost:123']], + 'client' => ['timeout' => 0.01], + ]); + $this->assertInstanceOf( + 'GuzzleHttp\Ring\Future\CompletedFutureArray', + $result + ); + $this->assertNull($result['status']); + $this->assertNull($result['body']); + $this->assertEquals([], $result['headers']); + $this->assertInstanceOf( + 'GuzzleHttp\Ring\Exception\RingException', + $result['error'] + ); + } + + public function testEnsuresTheHttpProtocol() + { + $handler = new StreamHandler(); + $result = $handler([ + 'http_method' => 'GET', + 'url' => 'ftp://localhost:123', + ]); + $this->assertArrayHasKey('error', $result); + $this->assertContains( + 'URL is invalid: ftp://localhost:123', + $result['error']->getMessage() + ); + } + + public function testStreamAttributeKeepsStreamOpen() + { + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'PUT', + 'uri' => '/foo', + 'query_string' => 'baz=bar', + 'headers' => [ + 'host' => [Server::$host], + 'Foo' => ['Bar'], + ], + 'body' => 'test', + 'client' => ['stream' => true], + ]); + + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals('8', Core::header($response, 'Content-Length')); + $body = $response['body']; + $this->assertTrue(is_resource($body)); + $this->assertEquals('http', stream_get_meta_data($body)['wrapper_type']); + $this->assertEquals('hi there', stream_get_contents($body)); + fclose($body); + $sent = Server::received()[0]; + $this->assertEquals('PUT', $sent['http_method']); + $this->assertEquals('/foo', $sent['uri']); + $this->assertEquals('baz=bar', $sent['query_string']); + $this->assertEquals('/foo?baz=bar', $sent['resource']); + $this->assertEquals('127.0.0.1:8125', Core::header($sent, 'host')); + $this->assertEquals('Bar', Core::header($sent, 'foo')); + } + + public function testDrainsResponseIntoTempStream() + { + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + ]); + $body = $response['body']; + $this->assertEquals('php://temp', stream_get_meta_data($body)['uri']); + $this->assertEquals('hi', fread($body, 2)); + fclose($body); + } + + public function testDrainsResponseIntoSaveToBody() + { + $r = fopen('php://temp', 'r+'); + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'client' => ['save_to' => $r], + ]); + $body = $response['body']; + $this->assertEquals('php://temp', stream_get_meta_data($body)['uri']); + $this->assertEquals('hi', fread($body, 2)); + $this->assertEquals(' there', stream_get_contents($r)); + fclose($r); + } + + public function testDrainsResponseIntoSaveToBodyAtPath() + { + $tmpfname = tempnam('/tmp', 'save_to_path'); + $this->queueRes(); + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'client' => ['save_to' => $tmpfname], + ]); + $body = $response['body']; + $this->assertInstanceOf('GuzzleHttp\Stream\StreamInterface', $body); + $this->assertEquals($tmpfname, $body->getMetadata('uri')); + $this->assertEquals('hi', $body->read(2)); + $body->close(); + unlink($tmpfname); + } + + public function testAutomaticallyDecompressGzip() + { + Server::flush(); + $content = gzencode('test'); + Server::enqueue([ + [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => [ + 'Content-Encoding' => ['gzip'], + 'Content-Length' => [strlen($content)], + ], + 'body' => $content, + ], + ]); + + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/', + 'client' => ['decode_content' => true], + ]); + $this->assertEquals('test', Core::body($response)); + } + + public function testDoesNotForceGzipDecode() + { + Server::flush(); + $content = gzencode('test'); + Server::enqueue([ + [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => [ + 'Content-Encoding' => ['gzip'], + 'Content-Length' => [strlen($content)], + ], + 'body' => $content, + ], + ]); + + $handler = new StreamHandler(); + $response = $handler([ + 'http_method' => 'GET', + 'headers' => ['host' => [Server::$host]], + 'uri' => '/', + 'client' => ['stream' => true, 'decode_content' => false], + ]); + $this->assertSame($content, Core::body($response)); + } + + public function testProtocolVersion() + { + $this->queueRes(); + $handler = new StreamHandler(); + $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'version' => 1.0, + ]); + + $this->assertEquals(1.0, Server::received()[0]['version']); + } + + protected function getSendResult(array $opts) + { + $this->queueRes(); + $handler = new StreamHandler(); + $opts['stream'] = true; + return $handler([ + 'http_method' => 'GET', + 'uri' => '/', + 'headers' => ['host' => [Server::$host]], + 'client' => $opts, + ]); + } + + public function testAddsProxy() + { + $res = $this->getSendResult(['stream' => true, 'proxy' => '127.0.0.1:8125']); + $opts = stream_context_get_options($res['body']); + $this->assertEquals('127.0.0.1:8125', $opts['http']['proxy']); + } + + public function testAddsTimeout() + { + $res = $this->getSendResult(['stream' => true, 'timeout' => 200]); + $opts = stream_context_get_options($res['body']); + $this->assertEquals(200, $opts['http']['timeout']); + } + + public function testVerifiesVerifyIsValidIfPath() + { + $res = $this->getSendResult(['verify' => '/does/not/exist']); + $this->assertContains( + 'SSL CA bundle not found: /does/not/exist', + (string) $res['error'] + ); + } + + public function testVerifyCanBeDisabled() + { + $res = $this->getSendResult(['verify' => false]); + $this->assertArrayNotHasKey('error', $res); + } + + public function testVerifiesCertIfValidPath() + { + $res = $this->getSendResult(['cert' => '/does/not/exist']); + $this->assertContains( + 'SSL certificate not found: /does/not/exist', + (string) $res['error'] + ); + } + + public function testVerifyCanBeSetToPath() + { + $path = $path = ClientUtils::getDefaultCaBundle(); + $res = $this->getSendResult(['verify' => $path]); + $this->assertArrayNotHasKey('error', $res); + $opts = stream_context_get_options($res['body']); + $this->assertEquals(true, $opts['ssl']['verify_peer']); + $this->assertEquals($path, $opts['ssl']['cafile']); + $this->assertTrue(file_exists($opts['ssl']['cafile'])); + } + + public function testUsesSystemDefaultBundle() + { + $path = $path = ClientUtils::getDefaultCaBundle(); + $res = $this->getSendResult(['verify' => true]); + $this->assertArrayNotHasKey('error', $res); + $opts = stream_context_get_options($res['body']); + if (PHP_VERSION_ID < 50600) { + $this->assertEquals($path, $opts['ssl']['cafile']); + } + } + + public function testEnsuresVerifyOptionIsValid() + { + $res = $this->getSendResult(['verify' => 10]); + $this->assertContains( + 'Invalid verify request option', + (string) $res['error'] + ); + } + + public function testCanSetPasswordWhenSettingCert() + { + $path = __FILE__; + $res = $this->getSendResult(['cert' => [$path, 'foo']]); + $opts = stream_context_get_options($res['body']); + $this->assertEquals($path, $opts['ssl']['local_cert']); + $this->assertEquals('foo', $opts['ssl']['passphrase']); + } + + public function testDebugAttributeWritesToStream() + { + $this->queueRes(); + $f = fopen('php://temp', 'w+'); + $this->getSendResult(['debug' => $f]); + fseek($f, 0); + $contents = stream_get_contents($f); + $this->assertContains(' [CONNECT]', $contents); + $this->assertContains(' [FILE_SIZE_IS]', $contents); + $this->assertContains(' [PROGRESS]', $contents); + } + + public function testDebugAttributeWritesStreamInfoToBuffer() + { + $called = false; + $this->queueRes(); + $buffer = fopen('php://temp', 'r+'); + $this->getSendResult([ + 'progress' => function () use (&$called) { $called = true; }, + 'debug' => $buffer, + ]); + fseek($buffer, 0); + $contents = stream_get_contents($buffer); + $this->assertContains(' [CONNECT]', $contents); + $this->assertContains(' [FILE_SIZE_IS] message: "Content-Length: 8"', $contents); + $this->assertContains(' [PROGRESS] bytes_max: "8"', $contents); + $this->assertTrue($called); + } + + public function testEmitsProgressInformation() + { + $called = []; + $this->queueRes(); + $this->getSendResult([ + 'progress' => function () use (&$called) { + $called[] = func_get_args(); + }, + ]); + $this->assertNotEmpty($called); + $this->assertEquals(8, $called[0][0]); + $this->assertEquals(0, $called[0][1]); + } + + public function testEmitsProgressInformationAndDebugInformation() + { + $called = []; + $this->queueRes(); + $buffer = fopen('php://memory', 'w+'); + $this->getSendResult([ + 'debug' => $buffer, + 'progress' => function () use (&$called) { + $called[] = func_get_args(); + }, + ]); + $this->assertNotEmpty($called); + $this->assertEquals(8, $called[0][0]); + $this->assertEquals(0, $called[0][1]); + rewind($buffer); + $this->assertNotEmpty(stream_get_contents($buffer)); + fclose($buffer); + } + + public function testAddsProxyByProtocol() + { + $url = str_replace('http', 'tcp', Server::$url); + $res = $this->getSendResult(['proxy' => ['http' => $url]]); + $opts = stream_context_get_options($res['body']); + $this->assertEquals($url, $opts['http']['proxy']); + } + + public function testPerformsShallowMergeOfCustomContextOptions() + { + $res = $this->getSendResult([ + 'stream_context' => [ + 'http' => [ + 'request_fulluri' => true, + 'method' => 'HEAD', + ], + 'socket' => [ + 'bindto' => '127.0.0.1:0', + ], + 'ssl' => [ + 'verify_peer' => false, + ], + ], + ]); + + $opts = stream_context_get_options($res['body']); + $this->assertEquals('HEAD', $opts['http']['method']); + $this->assertTrue($opts['http']['request_fulluri']); + $this->assertFalse($opts['ssl']['verify_peer']); + $this->assertEquals('127.0.0.1:0', $opts['socket']['bindto']); + } + + public function testEnsuresThatStreamContextIsAnArray() + { + $res = $this->getSendResult(['stream_context' => 'foo']); + $this->assertContains( + 'stream_context must be an array', + (string) $res['error'] + ); + } + + public function testDoesNotAddContentTypeByDefault() + { + $this->queueRes(); + $handler = new StreamHandler(); + $handler([ + 'http_method' => 'PUT', + 'uri' => '/', + 'headers' => ['host' => [Server::$host], 'content-length' => [3]], + 'body' => 'foo', + ]); + $req = Server::received()[0]; + $this->assertEquals('', Core::header($req, 'Content-Type')); + $this->assertEquals(3, Core::header($req, 'Content-Length')); + } + + private function queueRes() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => 200, + 'reason' => 'OK', + 'headers' => [ + 'Foo' => ['Bar'], + 'Content-Length' => [8], + ], + 'body' => 'hi there', + ], + ]); + } + + public function testSupports100Continue() + { + Server::flush(); + Server::enqueue([ + [ + 'status' => '200', + 'reason' => 'OK', + 'headers' => [ + 'Test' => ['Hello'], + 'Content-Length' => ['4'], + ], + 'body' => 'test', + ], + ]); + + $request = [ + 'http_method' => 'PUT', + 'headers' => [ + 'Host' => [Server::$host], + 'Expect' => ['100-Continue'], + ], + 'body' => 'test', + ]; + + $handler = new StreamHandler(); + $response = $handler($request); + $this->assertEquals(200, $response['status']); + $this->assertEquals('OK', $response['reason']); + $this->assertEquals(['Hello'], $response['headers']['Test']); + $this->assertEquals(['4'], $response['headers']['Content-Length']); + $this->assertEquals('test', Core::body($response)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/server.js b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/server.js new file mode 100755 index 000000000..6a03e33ab --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Client/server.js @@ -0,0 +1,241 @@ +/** + * Guzzle node.js test server to return queued responses to HTTP requests and + * expose a RESTful API for enqueueing responses and retrieving the requests + * that have been received. + * + * - Delete all requests that have been received: + * > DELETE /guzzle-server/requests + * > Host: 127.0.0.1:8125 + * + * - Enqueue responses + * > PUT /guzzle-server/responses + * > Host: 127.0.0.1:8125 + * > + * > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }] + * + * - Get the received requests + * > GET /guzzle-server/requests + * > Host: 127.0.0.1:8125 + * + * < HTTP/1.1 200 OK + * < + * < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}] + * + * - Attempt access to the secure area + * > GET /secure/by-digest/qop-auth/guzzle-server/requests + * > Host: 127.0.0.1:8125 + * + * < HTTP/1.1 401 Unauthorized + * < WWW-Authenticate: Digest realm="Digest Test", qop="auth", nonce="0796e98e1aeef43141fab2a66bf4521a", algorithm="MD5", stale="false" + * < + * < 401 Unauthorized + * + * - Shutdown the server + * > DELETE /guzzle-server + * > Host: 127.0.0.1:8125 + * + * @package Guzzle PHP + * @license See the LICENSE file that was distributed with this source code. + */ + +var http = require('http'); +var url = require('url'); + +/** + * Guzzle node.js server + * @class + */ +var GuzzleServer = function(port, log) { + + this.port = port; + this.log = log; + this.responses = []; + this.requests = []; + var that = this; + + var md5 = function(input) { + var crypto = require('crypto'); + var hasher = crypto.createHash('md5'); + hasher.update(input); + return hasher.digest('hex'); + } + + /** + * Node.js HTTP server authentication module. + * + * It is only initialized on demand (by loadAuthentifier). This avoids + * requiring the dependency to http-auth on standard operations, and the + * performance hit at startup. + */ + var auth; + + /** + * Provides authentication handlers (Basic, Digest). + */ + var loadAuthentifier = function(type, options) { + var typeId = type; + if (type == 'digest') { + typeId += '.'+(options && options.qop ? options.qop : 'none'); + } + if (!loadAuthentifier[typeId]) { + if (!auth) { + try { + auth = require('http-auth'); + } catch (e) { + if (e.code == 'MODULE_NOT_FOUND') { + return; + } + } + } + switch (type) { + case 'digest': + var digestParams = { + realm: 'Digest Test', + login: 'me', + password: 'test' + }; + if (options && options.qop) { + digestParams.qop = options.qop; + } + loadAuthentifier[typeId] = auth.digest(digestParams, function(username, callback) { + callback(md5(digestParams.login + ':' + digestParams.realm + ':' + digestParams.password)); + }); + break + } + } + return loadAuthentifier[typeId]; + }; + + var firewallRequest = function(request, req, res, requestHandlerCallback) { + var securedAreaUriParts = request.uri.match(/^\/secure\/by-(digest)(\/qop-([^\/]*))?(\/.*)$/); + if (securedAreaUriParts) { + var authentifier = loadAuthentifier(securedAreaUriParts[1], { qop: securedAreaUriParts[2] }); + if (!authentifier) { + res.writeHead(501, 'HTTP authentication not implemented', { 'Content-Length': 0 }); + res.end(); + return; + } + authentifier.check(req, res, function(req, res) { + req.url = securedAreaUriParts[4]; + requestHandlerCallback(request, req, res); + }); + } else { + requestHandlerCallback(request, req, res); + } + }; + + var controlRequest = function(request, req, res) { + if (req.url == '/guzzle-server/perf') { + res.writeHead(200, 'OK', {'Content-Length': 16}); + res.end('Body of response'); + } else if (req.method == 'DELETE') { + if (req.url == '/guzzle-server/requests') { + // Clear the received requests + that.requests = []; + res.writeHead(200, 'OK', { 'Content-Length': 0 }); + res.end(); + if (that.log) { + console.log('Flushing requests'); + } + } else if (req.url == '/guzzle-server') { + // Shutdown the server + res.writeHead(200, 'OK', { 'Content-Length': 0, 'Connection': 'close' }); + res.end(); + if (that.log) { + console.log('Shutting down'); + } + that.server.close(); + } + } else if (req.method == 'GET') { + if (req.url === '/guzzle-server/requests') { + if (that.log) { + console.log('Sending received requests'); + } + // Get received requests + var body = JSON.stringify(that.requests); + res.writeHead(200, 'OK', { 'Content-Length': body.length }); + res.end(body); + } + } else if (req.method == 'PUT' && req.url == '/guzzle-server/responses') { + if (that.log) { + console.log('Adding responses...'); + } + if (!request.body) { + if (that.log) { + console.log('No response data was provided'); + } + res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 }); + } else { + that.responses = eval('(' + request.body + ')'); + for (var i = 0; i < that.responses.length; i++) { + if (that.responses[i].body) { + that.responses[i].body = new Buffer(that.responses[i].body, 'base64'); + } + } + if (that.log) { + console.log(that.responses); + } + res.writeHead(200, 'OK', { 'Content-Length': 0 }); + } + res.end(); + } + }; + + var receivedRequest = function(request, req, res) { + if (req.url.indexOf('/guzzle-server') === 0) { + controlRequest(request, req, res); + } else if (req.url.indexOf('/guzzle-server') == -1 && !that.responses.length) { + res.writeHead(500); + res.end('No responses in queue'); + } else { + if (that.log) { + console.log('Returning response from queue and adding request'); + } + that.requests.push(request); + var response = that.responses.shift(); + res.writeHead(response.status, response.reason, response.headers); + res.end(response.body); + } + }; + + this.start = function() { + + that.server = http.createServer(function(req, res) { + + var parts = url.parse(req.url, false); + var request = { + http_method: req.method, + scheme: parts.scheme, + uri: parts.pathname, + query_string: parts.query, + headers: req.headers, + version: req.httpVersion, + body: '' + }; + + // Receive each chunk of the request body + req.addListener('data', function(chunk) { + request.body += chunk; + }); + + // Called when the request completes + req.addListener('end', function() { + firewallRequest(request, req, res, receivedRequest); + }); + }); + + that.server.listen(this.port, '127.0.0.1'); + + if (this.log) { + console.log('Server running at http://127.0.0.1:8125/'); + } + }; +}; + +// Get the port from the arguments +port = process.argv.length >= 3 ? process.argv[2] : 8125; +log = process.argv.length >= 4 ? process.argv[3] : false; + +// Start the server +server = new GuzzleServer(port, log); +server.start(); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/CoreTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/CoreTest.php new file mode 100755 index 000000000..49522f26b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/CoreTest.php @@ -0,0 +1,336 @@ +assertNull(Core::header([], 'Foo')); + $this->assertNull(Core::firstHeader([], 'Foo')); + } + + public function testChecksIfHasHeader() + { + $message = [ + 'headers' => [ + 'Foo' => ['Bar', 'Baz'], + 'foo' => ['hello'], + 'bar' => ['1'] + ] + ]; + $this->assertTrue(Core::hasHeader($message, 'Foo')); + $this->assertTrue(Core::hasHeader($message, 'foo')); + $this->assertTrue(Core::hasHeader($message, 'FoO')); + $this->assertTrue(Core::hasHeader($message, 'bar')); + $this->assertFalse(Core::hasHeader($message, 'barr')); + } + + public function testReturnsFirstHeaderWhenSimple() + { + $this->assertEquals('Bar', Core::firstHeader([ + 'headers' => ['Foo' => ['Bar', 'Baz']], + ], 'Foo')); + } + + public function testReturnsFirstHeaderWhenMultiplePerLine() + { + $this->assertEquals('Bar', Core::firstHeader([ + 'headers' => ['Foo' => ['Bar, Baz']], + ], 'Foo')); + } + + public function testExtractsCaseInsensitiveHeader() + { + $this->assertEquals( + 'hello', + Core::header(['headers' => ['foo' => ['hello']]], 'FoO') + ); + } + + public function testExtractsCaseInsensitiveHeaderLines() + { + $this->assertEquals( + ['a', 'b', 'c', 'd'], + Core::headerLines([ + 'headers' => [ + 'foo' => ['a', 'b'], + 'Foo' => ['c', 'd'] + ] + ], 'foo') + ); + } + + public function testExtractsHeaderLines() + { + $this->assertEquals( + ['bar', 'baz'], + Core::headerLines([ + 'headers' => [ + 'Foo' => ['bar', 'baz'], + ], + ], 'Foo') + ); + } + + public function testExtractsHeaderAsString() + { + $this->assertEquals( + 'bar, baz', + Core::header([ + 'headers' => [ + 'Foo' => ['bar', 'baz'], + ], + ], 'Foo', true) + ); + } + + public function testReturnsNullWhenHeaderNotFound() + { + $this->assertNull(Core::header(['headers' => []], 'Foo')); + } + + public function testRemovesHeaders() + { + $message = [ + 'headers' => [ + 'foo' => ['bar'], + 'Foo' => ['bam'], + 'baz' => ['123'], + ], + ]; + + $this->assertSame($message, Core::removeHeader($message, 'bam')); + $this->assertEquals([ + 'headers' => ['baz' => ['123']], + ], Core::removeHeader($message, 'foo')); + } + + public function testCreatesUrl() + { + $req = [ + 'scheme' => 'http', + 'headers' => ['host' => ['foo.com']], + 'uri' => '/', + ]; + + $this->assertEquals('http://foo.com/', Core::url($req)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage No Host header was provided + */ + public function testEnsuresHostIsAvailableWhenCreatingUrls() + { + Core::url([]); + } + + public function testCreatesUrlWithQueryString() + { + $req = [ + 'scheme' => 'http', + 'headers' => ['host' => ['foo.com']], + 'uri' => '/', + 'query_string' => 'foo=baz', + ]; + + $this->assertEquals('http://foo.com/?foo=baz', Core::url($req)); + } + + public function testUsesUrlIfSet() + { + $req = ['url' => 'http://foo.com']; + $this->assertEquals('http://foo.com', Core::url($req)); + } + + public function testReturnsNullWhenNoBody() + { + $this->assertNull(Core::body([])); + } + + public function testReturnsStreamAsString() + { + $this->assertEquals( + 'foo', + Core::body(['body' => Stream::factory('foo')]) + ); + } + + public function testReturnsString() + { + $this->assertEquals('foo', Core::body(['body' => 'foo'])); + } + + public function testReturnsResourceContent() + { + $r = fopen('php://memory', 'w+'); + fwrite($r, 'foo'); + rewind($r); + $this->assertEquals('foo', Core::body(['body' => $r])); + fclose($r); + } + + public function testReturnsIteratorContent() + { + $a = new \ArrayIterator(['a', 'b', 'cd', '']); + $this->assertEquals('abcd', Core::body(['body' => $a])); + } + + public function testReturnsObjectToString() + { + $this->assertEquals('foo', Core::body(['body' => new StrClass])); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresBodyIsValid() + { + Core::body(['body' => false]); + } + + public function testParsesHeadersFromLines() + { + $lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b']; + $this->assertEquals([ + 'Foo' => ['bar', 'baz'], + 'Abc' => ['123'], + 'Def' => ['a, b'], + ], Core::headersFromLines($lines)); + } + + public function testParsesHeadersFromLinesWithMultipleLines() + { + $lines = ['Foo: bar', 'Foo: baz', 'Foo: 123']; + $this->assertEquals([ + 'Foo' => ['bar', 'baz', '123'], + ], Core::headersFromLines($lines)); + } + + public function testCreatesArrayCallFunctions() + { + $called = []; + $a = function ($a, $b) use (&$called) { + $called['a'] = func_get_args(); + }; + $b = function ($a, $b) use (&$called) { + $called['b'] = func_get_args(); + }; + $c = Core::callArray([$a, $b]); + $c(1, 2); + $this->assertEquals([1, 2], $called['a']); + $this->assertEquals([1, 2], $called['b']); + } + + public function testRewindsGuzzleStreams() + { + $str = Stream::factory('foo'); + $this->assertTrue(Core::rewindBody(['body' => $str])); + } + + public function testRewindsStreams() + { + $str = Stream::factory('foo')->detach(); + $this->assertTrue(Core::rewindBody(['body' => $str])); + } + + public function testRewindsIterators() + { + $iter = new \ArrayIterator(['foo']); + $this->assertTrue(Core::rewindBody(['body' => $iter])); + } + + public function testRewindsStrings() + { + $this->assertTrue(Core::rewindBody(['body' => 'hi'])); + } + + public function testRewindsToStrings() + { + $this->assertTrue(Core::rewindBody(['body' => new StrClass()])); + } + + public function typeProvider() + { + return [ + ['foo', 'string(3) "foo"'], + [true, 'bool(true)'], + [false, 'bool(false)'], + [10, 'int(10)'], + [1.0, 'float(1)'], + [new StrClass(), 'object(GuzzleHttp\Tests\Ring\StrClass)'], + [['foo'], 'array(1)'] + ]; + } + + /** + * @dataProvider typeProvider + */ + public function testDescribesType($input, $output) + { + $this->assertEquals($output, Core::describeType($input)); + } + + public function testDoesSleep() + { + $t = microtime(true); + $expected = $t + (100 / 1000); + Core::doSleep(['client' => ['delay' => 100]]); + $this->assertGreaterThanOrEqual($expected, microtime(true)); + } + + public function testProxiesFuture() + { + $f = new CompletedFutureArray(['status' => 200]); + $res = null; + $proxied = Core::proxy($f, function ($value) use (&$res) { + $value['foo'] = 'bar'; + $res = $value; + return $value; + }); + $this->assertNotSame($f, $proxied); + $this->assertEquals(200, $f->wait()['status']); + $this->assertArrayNotHasKey('foo', $f->wait()); + $this->assertEquals('bar', $proxied->wait()['foo']); + $this->assertEquals(200, $proxied->wait()['status']); + } + + public function testProxiesDeferredFuture() + { + $d = new Deferred(); + $f = new FutureArray($d->promise()); + $f2 = Core::proxy($f); + $d->resolve(['foo' => 'bar']); + $this->assertEquals('bar', $f['foo']); + $this->assertEquals('bar', $f2['foo']); + } + + public function testProxiesDeferredFutureFailure() + { + $d = new Deferred(); + $f = new FutureArray($d->promise()); + $f2 = Core::proxy($f); + $d->reject(new \Exception('foo')); + try { + $f2['hello?']; + $this->fail('did not throw'); + } catch (\Exception $e) { + $this->assertEquals('foo', $e->getMessage()); + } + + } +} + +final class StrClass +{ + public function __toString() + { + return 'foo'; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php new file mode 100755 index 000000000..82d7efbf5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php @@ -0,0 +1,21 @@ + 'bar']); + $this->assertEquals('bar', $f['foo']); + $this->assertFalse(isset($f['baz'])); + $f['abc'] = '123'; + $this->assertTrue(isset($f['abc'])); + $this->assertEquals(['foo' => 'bar', 'abc' => '123'], iterator_to_array($f)); + $this->assertEquals(2, count($f)); + unset($f['abc']); + $this->assertEquals(1, count($f)); + $this->assertEquals(['foo' => 'bar'], iterator_to_array($f)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php new file mode 100755 index 000000000..6ded40dfb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php @@ -0,0 +1,46 @@ +assertEquals('hi', $f->wait()); + $f->cancel(); + + $a = null; + $f->then(function ($v) use (&$a) { + $a = $v; + }); + $this->assertSame('hi', $a); + } + + public function testThrows() + { + $ex = new \Exception('foo'); + $f = new CompletedFutureValue(null, $ex); + $f->cancel(); + try { + $f->wait(); + $this->fail('did not throw'); + } catch (\Exception $e) { + $this->assertSame($e, $ex); + } + } + + public function testMarksAsCancelled() + { + $ex = new CancelledFutureAccessException(); + $f = new CompletedFutureValue(null, $ex); + try { + $f->wait(); + $this->fail('did not throw'); + } catch (\Exception $e) { + $this->assertSame($e, $ex); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php new file mode 100755 index 000000000..0e09f5afa --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php @@ -0,0 +1,56 @@ +promise(), + function () use (&$c, $deferred) { + $c = true; + $deferred->resolve(['status' => 200]); + } + ); + $this->assertFalse($c); + $this->assertFalse($this->readAttribute($f, 'isRealized')); + $this->assertEquals(200, $f['status']); + $this->assertTrue($c); + } + + public function testActsLikeArray() + { + $deferred = new Deferred(); + $f = new FutureArray( + $deferred->promise(), + function () use (&$c, $deferred) { + $deferred->resolve(['status' => 200]); + } + ); + + $this->assertTrue(isset($f['status'])); + $this->assertEquals(200, $f['status']); + $this->assertEquals(['status' => 200], $f->wait()); + $this->assertEquals(1, count($f)); + $f['baz'] = 10; + $this->assertEquals(10, $f['baz']); + unset($f['baz']); + $this->assertFalse(isset($f['baz'])); + $this->assertEquals(['status' => 200], iterator_to_array($f)); + } + + /** + * @expectedException \RuntimeException + */ + public function testThrowsWhenAccessingInvalidProperty() + { + $deferred = new Deferred(); + $f = new FutureArray($deferred->promise(), function () {}); + $f->foo; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php new file mode 100755 index 000000000..d59c543d0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php @@ -0,0 +1,109 @@ +promise(), + function () use ($deferred, &$called) { + $called++; + $deferred->resolve('foo'); + } + ); + + $this->assertEquals('foo', $f->wait()); + $this->assertEquals(1, $called); + $this->assertEquals('foo', $f->wait()); + $this->assertEquals(1, $called); + $f->cancel(); + $this->assertTrue($this->readAttribute($f, 'isRealized')); + } + + /** + * @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException + */ + public function testThrowsWhenAccessingCancelled() + { + $f = new FutureValue( + (new Deferred())->promise(), + function () {}, + function () { return true; } + ); + $f->cancel(); + $f->wait(); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testThrowsWhenDerefFailure() + { + $called = false; + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () use(&$called) { + $called = true; + } + ); + $deferred->reject(new \OutOfBoundsException()); + $f->wait(); + $this->assertFalse($called); + } + + /** + * @expectedException \GuzzleHttp\Ring\Exception\RingException + * @expectedExceptionMessage Waiting did not resolve future + */ + public function testThrowsWhenDerefDoesNotResolve() + { + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () use(&$called) { + $called = true; + } + ); + $f->wait(); + } + + public function testThrowingCancelledFutureAccessExceptionCancels() + { + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () use ($deferred) { + throw new CancelledFutureAccessException(); + } + ); + try { + $f->wait(); + $this->fail('did not throw'); + } catch (CancelledFutureAccessException $e) {} + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage foo + */ + public function testThrowingExceptionInDerefMarksAsFailed() + { + $deferred = new Deferred(); + $f = new FutureValue( + $deferred->promise(), + function () { + throw new \Exception('foo'); + } + ); + $f->wait(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/bootstrap.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/bootstrap.php new file mode 100755 index 000000000..017610fe0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/ringphp/tests/bootstrap.php @@ -0,0 +1,11 @@ + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/Makefile b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/Makefile new file mode 100755 index 000000000..f4d42849e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/Makefile @@ -0,0 +1,19 @@ +all: clean coverage + +release: tag + git push origin --tags + +tag: + chag tag --sign --debug CHANGELOG.rst + +test: + vendor/bin/phpunit + +coverage: + vendor/bin/phpunit --coverage-html=artifacts/coverage + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/README.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/README.rst new file mode 100755 index 000000000..baff63b37 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/README.rst @@ -0,0 +1,36 @@ +============== +Guzzle Streams +============== + +Provides a simple abstraction over streams of data. + +This library is used in `Guzzle 5 `_, and is +(currently) compatible with the WIP PSR-7. + +Installation +============ + +This package can be installed easily using `Composer `_. +Simply add the following to the composer.json file at the root of your project: + +.. code-block:: javascript + + { + "require": { + "guzzlehttp/streams": "~3.0" + } + } + +Then install your dependencies using ``composer.phar install``. + +Documentation +============= + +The documentation for this package can be found on the main Guzzle website at +http://docs.guzzlephp.org/en/guzzle4/streams.html. + +Testing +======= + +This library is tested using PHPUnit. You'll need to install the dependencies +using `Composer `_ then run ``make test``. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/composer.json new file mode 100755 index 000000000..6d7034370 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzlehttp/streams", + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": ["stream", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "autoload": { + "psr-4": { "GuzzleHttp\\Stream\\": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/phpunit.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/phpunit.xml.dist new file mode 100755 index 000000000..6e758c192 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + + tests + + + + + src + + src/functions.php + + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AppendStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AppendStream.php new file mode 100755 index 000000000..94bda7173 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AppendStream.php @@ -0,0 +1,220 @@ +addStream($stream); + } + } + + public function __toString() + { + try { + $this->seek(0); + return $this->getContents(); + } catch (\Exception $e) { + return ''; + } + } + + /** + * Add a stream to the AppendStream + * + * @param StreamInterface $stream Stream to append. Must be readable. + * + * @throws \InvalidArgumentException if the stream is not readable + */ + public function addStream(StreamInterface $stream) + { + if (!$stream->isReadable()) { + throw new \InvalidArgumentException('Each stream must be readable'); + } + + // The stream is only seekable if all streams are seekable + if (!$stream->isSeekable()) { + $this->seekable = false; + } + + $this->streams[] = $stream; + } + + public function getContents() + { + return Utils::copyToString($this); + } + + /** + * Closes each attached stream. + * + * {@inheritdoc} + */ + public function close() + { + $this->pos = $this->current = 0; + + foreach ($this->streams as $stream) { + $stream->close(); + } + + $this->streams = []; + } + + /** + * Detaches each attached stream + * + * {@inheritdoc} + */ + public function detach() + { + $this->close(); + $this->detached = true; + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function tell() + { + return $this->pos; + } + + /** + * Tries to calculate the size by adding the size of each stream. + * + * If any of the streams do not return a valid number, then the size of the + * append stream cannot be determined and null is returned. + * + * {@inheritdoc} + */ + public function getSize() + { + $size = 0; + + foreach ($this->streams as $stream) { + $s = $stream->getSize(); + if ($s === null) { + return null; + } + $size += $s; + } + + return $size; + } + + public function eof() + { + return !$this->streams || + ($this->current >= count($this->streams) - 1 && + $this->streams[$this->current]->eof()); + } + + /** + * Attempts to seek to the given position. Only supports SEEK_SET. + * + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if (!$this->seekable || $whence !== SEEK_SET) { + return false; + } + + $success = true; + $this->pos = $this->current = 0; + + // Rewind each stream + foreach ($this->streams as $stream) { + if (!$stream->seek(0)) { + $success = false; + } + } + + if (!$success) { + return false; + } + + // Seek to the actual position by reading from each stream + while ($this->pos < $offset && !$this->eof()) { + $this->read(min(8096, $offset - $this->pos)); + } + + return $this->pos == $offset; + } + + /** + * Reads from all of the appended streams until the length is met or EOF. + * + * {@inheritdoc} + */ + public function read($length) + { + $buffer = ''; + $total = count($this->streams) - 1; + $remaining = $length; + + while ($remaining > 0) { + // Progress to the next stream if needed. + if ($this->streams[$this->current]->eof()) { + if ($this->current == $total) { + break; + } + $this->current++; + } + $buffer .= $this->streams[$this->current]->read($remaining); + $remaining = $length - strlen($buffer); + } + + $this->pos += strlen($buffer); + + return $buffer; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function write($string) + { + return false; + } + + public function getMetadata($key = null) + { + return $key ? null : []; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AsyncReadStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AsyncReadStream.php new file mode 100755 index 000000000..25ad96021 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/AsyncReadStream.php @@ -0,0 +1,207 @@ +isReadable() || !$buffer->isWritable()) { + throw new \InvalidArgumentException( + 'Buffer must be readable and writable' + ); + } + + if (isset($config['size'])) { + $this->size = $config['size']; + } + + static $callables = ['pump', 'drain']; + foreach ($callables as $check) { + if (isset($config[$check])) { + if (!is_callable($config[$check])) { + throw new \InvalidArgumentException( + $check . ' must be callable' + ); + } + $this->{$check} = $config[$check]; + } + } + + $this->hwm = $buffer->getMetadata('hwm'); + + // Cannot drain when there's no high water mark. + if ($this->hwm === null) { + $this->drain = null; + } + + $this->stream = $buffer; + } + + /** + * Factory method used to create new async stream and an underlying buffer + * if no buffer is provided. + * + * This function accepts the same options as AsyncReadStream::__construct, + * but added the following key value pairs: + * + * - buffer: (StreamInterface) Buffer used to buffer data. If none is + * provided, a default buffer is created. + * - hwm: (int) High water mark to use if a buffer is created on your + * behalf. + * - max_buffer: (int) If provided, wraps the utilized buffer in a + * DroppingStream decorator to ensure that buffer does not exceed a given + * length. When exceeded, the stream will begin dropping data. Set the + * max_buffer to 0, to use a NullStream which does not store data. + * - write: (callable) A function that is invoked when data is written + * to the underlying buffer. The function accepts the buffer as the first + * argument, and the data being written as the second. The function MUST + * return the number of bytes that were written or false to let writers + * know to slow down. + * - drain: (callable) See constructor documentation. + * - pump: (callable) See constructor documentation. + * + * @param array $options Associative array of options. + * + * @return array Returns an array containing the buffer used to buffer + * data, followed by the ready to use AsyncReadStream object. + */ + public static function create(array $options = []) + { + $maxBuffer = isset($options['max_buffer']) + ? $options['max_buffer'] + : null; + + if ($maxBuffer === 0) { + $buffer = new NullStream(); + } elseif (isset($options['buffer'])) { + $buffer = $options['buffer']; + } else { + $hwm = isset($options['hwm']) ? $options['hwm'] : 16384; + $buffer = new BufferStream($hwm); + } + + if ($maxBuffer > 0) { + $buffer = new DroppingStream($buffer, $options['max_buffer']); + } + + // Call the on_write callback if an on_write function was provided. + if (isset($options['write'])) { + $onWrite = $options['write']; + $buffer = FnStream::decorate($buffer, [ + 'write' => function ($string) use ($buffer, $onWrite) { + $result = $buffer->write($string); + $onWrite($buffer, $string); + return $result; + } + ]); + } + + return [$buffer, new self($buffer, $options)]; + } + + public function getSize() + { + return $this->size; + } + + public function isWritable() + { + return false; + } + + public function write($string) + { + return false; + } + + public function read($length) + { + if (!$this->needsDrain && $this->drain) { + $this->needsDrain = $this->stream->getSize() >= $this->hwm; + } + + $result = $this->stream->read($length); + + // If we need to drain, then drain when the buffer is empty. + if ($this->needsDrain && $this->stream->getSize() === 0) { + $this->needsDrain = false; + $drainFn = $this->drain; + $drainFn($this->stream); + } + + $resultLen = strlen($result); + + // If a pump was provided, the buffer is still open, and not enough + // data was given, then block until the data is provided. + if ($this->pump && $resultLen < $length) { + $pumpFn = $this->pump; + $result .= $pumpFn($length - $resultLen); + } + + return $result; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/BufferStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/BufferStream.php new file mode 100755 index 000000000..0fffbd63a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/BufferStream.php @@ -0,0 +1,138 @@ +hwm = $hwm; + } + + public function __toString() + { + return $this->getContents(); + } + + public function getContents() + { + $buffer = $this->buffer; + $this->buffer = ''; + + return $buffer; + } + + public function close() + { + $this->buffer = ''; + } + + public function detach() + { + $this->close(); + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function getSize() + { + return strlen($this->buffer); + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return true; + } + + public function isSeekable() + { + return false; + } + + public function seek($offset, $whence = SEEK_SET) + { + return false; + } + + public function eof() + { + return strlen($this->buffer) === 0; + } + + public function tell() + { + return false; + } + + /** + * Reads data from the buffer. + */ + public function read($length) + { + $currentLength = strlen($this->buffer); + + if ($length >= $currentLength) { + // No need to slice the buffer because we don't have enough data. + $result = $this->buffer; + $this->buffer = ''; + } else { + // Slice up the result to provide a subset of the buffer. + $result = substr($this->buffer, 0, $length); + $this->buffer = substr($this->buffer, $length); + } + + return $result; + } + + /** + * Writes data to the buffer. + */ + public function write($string) + { + $this->buffer .= $string; + + if (strlen($this->buffer) >= $this->hwm) { + return false; + } + + return strlen($string); + } + + public function getMetadata($key = null) + { + if ($key == 'hwm') { + return $this->hwm; + } + + return $key ? null : []; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/CachingStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/CachingStream.php new file mode 100755 index 000000000..60bb9056c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/CachingStream.php @@ -0,0 +1,122 @@ +remoteStream = $stream; + $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); + } + + public function getSize() + { + return max($this->stream->getSize(), $this->remoteStream->getSize()); + } + + /** + * {@inheritdoc} + * @throws SeekException When seeking with SEEK_END or when seeking + * past the total size of the buffer stream + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->tell(); + } else { + return false; + } + + // You cannot skip ahead past where you've read from the remote stream + if ($byte > $this->stream->getSize()) { + throw new SeekException( + $this, + $byte, + sprintf('Cannot seek to byte %d when the buffered stream only' + . ' contains %d bytes', $byte, $this->stream->getSize()) + ); + } + + return $this->stream->seek($byte); + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->stream->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have + // been filled from the remote stream, then we must skip bytes on + // the remote stream to emulate overwriting bytes from that + // position. This mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read( + $remaining + $this->skipReadBytes + ); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->stream->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want + // to skip bytes from being read from the remote stream to emulate + // other stream wrappers. Basically replacing bytes of data of a fixed + // length. + $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->stream->write($string); + } + + public function eof() + { + return $this->stream->eof() && $this->remoteStream->eof(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + $this->remoteStream->close() && $this->stream->close(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/DroppingStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/DroppingStream.php new file mode 100755 index 000000000..56ee80c12 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/DroppingStream.php @@ -0,0 +1,42 @@ +stream = $stream; + $this->maxLength = $maxLength; + } + + public function write($string) + { + $diff = $this->maxLength - $this->stream->getSize(); + + // Begin returning false when the underlying stream is too large. + if ($diff <= 0) { + return false; + } + + // Write the stream or a subset of the stream if needed. + if (strlen($string) < $diff) { + return $this->stream->write($string); + } + + $this->stream->write(substr($string, 0, $diff)); + + return false; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php new file mode 100755 index 000000000..e631b9fa4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php @@ -0,0 +1,4 @@ +stream = $stream; + $msg = $msg ?: 'Could not seek the stream to position ' . $pos; + parent::__construct($msg); + } + + /** + * @return StreamInterface + */ + public function getStream() + { + return $this->stream; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/FnStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/FnStream.php new file mode 100755 index 000000000..6b5872d7f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/FnStream.php @@ -0,0 +1,147 @@ +methods = $methods; + + // Create the functions on the class + foreach ($methods as $name => $fn) { + $this->{'_fn_' . $name} = $fn; + } + } + + /** + * Lazily determine which methods are not implemented. + * @throws \BadMethodCallException + */ + public function __get($name) + { + throw new \BadMethodCallException(str_replace('_fn_', '', $name) + . '() is not implemented in the FnStream'); + } + + /** + * The close method is called on the underlying stream only if possible. + */ + public function __destruct() + { + if (isset($this->_fn_close)) { + call_user_func($this->_fn_close); + } + } + + /** + * Adds custom functionality to an underlying stream by intercepting + * specific method calls. + * + * @param StreamInterface $stream Stream to decorate + * @param array $methods Hash of method name to a closure + * + * @return FnStream + */ + public static function decorate(StreamInterface $stream, array $methods) + { + // If any of the required methods were not provided, then simply + // proxy to the decorated stream. + foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { + $methods[$diff] = [$stream, $diff]; + } + + return new self($methods); + } + + public function __toString() + { + return call_user_func($this->_fn___toString); + } + + public function close() + { + return call_user_func($this->_fn_close); + } + + public function detach() + { + return call_user_func($this->_fn_detach); + } + + public function attach($stream) + { + return call_user_func($this->_fn_attach, $stream); + } + + public function getSize() + { + return call_user_func($this->_fn_getSize); + } + + public function tell() + { + return call_user_func($this->_fn_tell); + } + + public function eof() + { + return call_user_func($this->_fn_eof); + } + + public function isSeekable() + { + return call_user_func($this->_fn_isSeekable); + } + + public function seek($offset, $whence = SEEK_SET) + { + return call_user_func($this->_fn_seek, $offset, $whence); + } + + public function isWritable() + { + return call_user_func($this->_fn_isWritable); + } + + public function write($string) + { + return call_user_func($this->_fn_write, $string); + } + + public function isReadable() + { + return call_user_func($this->_fn_isReadable); + } + + public function read($length) + { + return call_user_func($this->_fn_read, $length); + } + + public function getContents() + { + return call_user_func($this->_fn_getContents); + } + + public function getMetadata($key = null) + { + return call_user_func($this->_fn_getMetadata, $key); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php new file mode 100755 index 000000000..4d049a693 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php @@ -0,0 +1,117 @@ +isReadable()) { + $mode = $stream->isWritable() ? 'r+' : 'r'; + } elseif ($stream->isWritable()) { + $mode = 'w'; + } else { + throw new \InvalidArgumentException('The stream must be readable, ' + . 'writable, or both.'); + } + + return fopen('guzzle://stream', $mode, null, stream_context_create([ + 'guzzle' => ['stream' => $stream] + ])); + } + + /** + * Registers the stream wrapper if needed + */ + public static function register() + { + if (!in_array('guzzle', stream_get_wrappers())) { + stream_wrapper_register('guzzle', __CLASS__); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) + { + $options = stream_context_get_options($this->context); + + if (!isset($options['guzzle']['stream'])) { + return false; + } + + $this->mode = $mode; + $this->stream = $options['guzzle']['stream']; + + return true; + } + + public function stream_read($count) + { + return $this->stream->read($count); + } + + public function stream_write($data) + { + return (int) $this->stream->write($data); + } + + public function stream_tell() + { + return $this->stream->tell(); + } + + public function stream_eof() + { + return $this->stream->eof(); + } + + public function stream_seek($offset, $whence) + { + return $this->stream->seek($offset, $whence); + } + + public function stream_stat() + { + static $modeMap = [ + 'r' => 33060, + 'r+' => 33206, + 'w' => 33188 + ]; + + return [ + 'dev' => 0, + 'ino' => 0, + 'mode' => $modeMap[$this->mode], + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => $this->stream->getSize() ?: 0, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0 + ]; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/InflateStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/InflateStream.php new file mode 100755 index 000000000..978af2103 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/InflateStream.php @@ -0,0 +1,27 @@ +stream = new Stream($resource); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LazyOpenStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LazyOpenStream.php new file mode 100755 index 000000000..6242ee7b5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LazyOpenStream.php @@ -0,0 +1,37 @@ +filename = $filename; + $this->mode = $mode; + } + + /** + * Creates the underlying stream lazily when required. + * + * @return StreamInterface + */ + protected function createStream() + { + return Stream::factory(Utils::open($this->filename, $this->mode)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LimitStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LimitStream.php new file mode 100755 index 000000000..e9fad9857 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/LimitStream.php @@ -0,0 +1,161 @@ +stream = $stream; + $this->setLimit($limit); + $this->setOffset($offset); + } + + public function eof() + { + // Always return true if the underlying stream is EOF + if ($this->stream->eof()) { + return true; + } + + // No limit and the underlying stream is not at EOF + if ($this->limit == -1) { + return false; + } + + $tell = $this->stream->tell(); + if ($tell === false) { + return false; + } + + return $tell >= $this->offset + $this->limit; + } + + /** + * Returns the size of the limited subset of data + * {@inheritdoc} + */ + public function getSize() + { + if (null === ($length = $this->stream->getSize())) { + return null; + } elseif ($this->limit == -1) { + return $length - $this->offset; + } else { + return min($this->limit, $length - $this->offset); + } + } + + /** + * Allow for a bounded seek on the read limited stream + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence !== SEEK_SET || $offset < 0) { + return false; + } + + $offset += $this->offset; + + if ($this->limit !== -1) { + if ($offset > $this->offset + $this->limit) { + $offset = $this->offset + $this->limit; + } + } + + return $this->stream->seek($offset); + } + + /** + * Give a relative tell() + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell() - $this->offset; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @return self + * @throws SeekException + */ + public function setOffset($offset) + { + $current = $this->stream->tell(); + + if ($current !== $offset) { + // If the stream cannot seek to the offset position, then read to it + if (!$this->stream->seek($offset)) { + if ($current > $offset) { + throw new SeekException($this, $offset); + } else { + $this->stream->read($offset - $current); + } + } + } + + $this->offset = $offset; + + return $this; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the + * stream. + * + * @param int $limit Number of bytes to allow to be read from the stream. + * Use -1 for no limit. + * @return self + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + public function read($length) + { + if ($this->limit == -1) { + return $this->stream->read($length); + } + + // Check if the current position is less than the total allowed + // bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->stream->tell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte + // limit is not exceeded + return $this->stream->read(min($remaining, $length)); + } else { + return false; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php new file mode 100755 index 000000000..c1433ad83 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php @@ -0,0 +1,11 @@ +stream->attach($stream); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/NullStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/NullStream.php new file mode 100755 index 000000000..41ee77668 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/NullStream.php @@ -0,0 +1,78 @@ +source = $source; + $this->size = isset($options['size']) ? $options['size'] : null; + $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; + $this->buffer = new BufferStream(); + } + + public function __toString() + { + return Utils::copyToString($this); + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->tellPos = false; + $this->source = null; + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function getSize() + { + return $this->size; + } + + public function tell() + { + return $this->tellPos; + } + + public function eof() + { + return !$this->source; + } + + public function isSeekable() + { + return false; + } + + public function seek($offset, $whence = SEEK_SET) + { + return false; + } + + public function isWritable() + { + return false; + } + + public function write($string) + { + return false; + } + + public function isReadable() + { + return true; + } + + public function read($length) + { + $data = $this->buffer->read($length); + $readLen = strlen($data); + $this->tellPos += $readLen; + $remaining = $length - $readLen; + + if ($remaining) { + $this->pump($remaining); + $data .= $this->buffer->read($remaining); + $this->tellPos += strlen($data) - $readLen; + } + + return $data; + } + + public function getContents() + { + $result = ''; + while (!$this->eof()) { + $result .= $this->read(1000000); + } + + return $result; + } + + public function getMetadata($key = null) + { + if (!$key) { + return $this->metadata; + } + + return isset($this->metadata[$key]) ? $this->metadata[$key] : null; + } + + private function pump($length) + { + if ($this->source) { + do { + $data = call_user_func($this->source, $length); + if ($data === false || $data === null) { + $this->source = null; + return; + } + $this->buffer->write($data); + $length -= strlen($data); + } while ($length > 0); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Stream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Stream.php new file mode 100755 index 000000000..7adbc5e3f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/Stream.php @@ -0,0 +1,261 @@ + [ + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, + 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a+' => true + ], + 'write' => [ + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, + 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, + 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ] + ]; + + /** + * Create a new stream based on the input type. + * + * This factory accepts the same associative array of options as described + * in the constructor. + * + * @param resource|string|StreamInterface $resource Entity body data + * @param array $options Additional options + * + * @return Stream + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ + public static function factory($resource = '', array $options = []) + { + $type = gettype($resource); + + if ($type == 'string') { + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + return new self($stream, $options); + } + + if ($type == 'resource') { + return new self($resource, $options); + } + + if ($resource instanceof StreamInterface) { + return $resource; + } + + if ($type == 'object' && method_exists($resource, '__toString')) { + return self::factory((string) $resource, $options); + } + + if (is_callable($resource)) { + return new PumpStream($resource, $options); + } + + if ($resource instanceof \Iterator) { + return new PumpStream(function () use ($resource) { + if (!$resource->valid()) { + return false; + } + $result = $resource->current(); + $resource->next(); + return $result; + }, $options); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . $type); + } + + /** + * This constructor accepts an associative array of options. + * + * - size: (int) If a read stream would otherwise have an indeterminate + * size, but the size is known due to foreknownledge, then you can + * provide that size, in bytes. + * - metadata: (array) Any additional metadata to return when the metadata + * of the stream is accessed. + * + * @param resource $stream Stream resource to wrap. + * @param array $options Associative array of options. + * + * @throws \InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $options = []) + { + if (!is_resource($stream)) { + throw new \InvalidArgumentException('Stream must be a resource'); + } + + if (isset($options['size'])) { + $this->size = $options['size']; + } + + $this->customMetadata = isset($options['metadata']) + ? $options['metadata'] + : []; + + $this->attach($stream); + } + + /** + * Closes the stream when the destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + if (!$this->stream) { + return ''; + } + + $this->seek(0); + + return (string) stream_get_contents($this->stream); + } + + public function getContents() + { + return $this->stream ? stream_get_contents($this->stream) : ''; + } + + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + + $this->detach(); + } + + public function detach() + { + $result = $this->stream; + $this->stream = $this->size = $this->uri = null; + $this->readable = $this->writable = $this->seekable = false; + + return $result; + } + + public function attach($stream) + { + $this->stream = $stream; + $meta = stream_get_meta_data($this->stream); + $this->seekable = $meta['seekable']; + $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); + $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); + $this->uri = $this->getMetadata('uri'); + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + if (!$this->stream) { + return null; + } + + // Clear the stat cache if the stream has a URI + if ($this->uri) { + clearstatcache(true, $this->uri); + } + + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } + + return null; + } + + public function isReadable() + { + return $this->readable; + } + + public function isWritable() + { + return $this->writable; + } + + public function isSeekable() + { + return $this->seekable; + } + + public function eof() + { + return !$this->stream || feof($this->stream); + } + + public function tell() + { + return $this->stream ? ftell($this->stream) : false; + } + + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->seekable + ? fseek($this->stream, $offset, $whence) === 0 + : false; + } + + public function read($length) + { + return $this->readable ? fread($this->stream, $length) : false; + } + + public function write($string) + { + // We can't know the size after writing anything + $this->size = null; + + return $this->writable ? fwrite($this->stream, $string) : false; + } + + public function getMetadata($key = null) + { + if (!$this->stream) { + return $key ? null : []; + } elseif (!$key) { + return $this->customMetadata + stream_get_meta_data($this->stream); + } elseif (isset($this->customMetadata[$key])) { + return $this->customMetadata[$key]; + } + + $meta = stream_get_meta_data($this->stream); + + return isset($meta[$key]) ? $meta[$key] : null; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php new file mode 100755 index 000000000..39c19c58c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php @@ -0,0 +1,143 @@ +stream = $stream; + } + + /** + * Magic method used to create a new stream if streams are not added in + * the constructor of a decorator (e.g., LazyOpenStream). + */ + public function __get($name) + { + if ($name == 'stream') { + $this->stream = $this->createStream(); + return $this->stream; + } + + throw new \UnexpectedValueException("$name not found on class"); + } + + public function __toString() + { + try { + $this->seek(0); + return $this->getContents(); + } catch (\Exception $e) { + // Really, PHP? https://bugs.php.net/bug.php?id=53648 + trigger_error('StreamDecorator::__toString exception: ' + . (string) $e, E_USER_ERROR); + return ''; + } + } + + public function getContents() + { + return Utils::copyToString($this); + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + $result = call_user_func_array(array($this->stream, $method), $args); + + // Always return the wrapped object if the result is a return $this + return $result === $this->stream ? $this : $result; + } + + public function close() + { + $this->stream->close(); + } + + public function getMetadata($key = null) + { + return $this->stream->getMetadata($key); + } + + public function detach() + { + return $this->stream->detach(); + } + + public function attach($stream) + { + throw new CannotAttachException(); + } + + public function getSize() + { + return $this->stream->getSize(); + } + + public function eof() + { + return $this->stream->eof(); + } + + public function tell() + { + return $this->stream->tell(); + } + + public function isReadable() + { + return $this->stream->isReadable(); + } + + public function isWritable() + { + return $this->stream->isWritable(); + } + + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->stream->seek($offset, $whence); + } + + public function read($length) + { + return $this->stream->read($length); + } + + public function write($string) + { + return $this->stream->write($string); + } + + /** + * Implement in subclasses to dynamically create streams when requested. + * + * @return StreamInterface + * @throws \BadMethodCallException + */ + protected function createStream() + { + throw new \BadMethodCallException('createStream() not implemented in ' + . get_class($this)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamInterface.php new file mode 100755 index 000000000..fd19c6f25 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/src/StreamInterface.php @@ -0,0 +1,159 @@ +eof()) { + $buf = $stream->read(1048576); + if ($buf === false) { + break; + } + $buffer .= $buf; + } + return $buffer; + } + + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + if ($buf === false) { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + + return $buffer; + } + + /** + * Copy the contents of a stream into another stream until the given number + * of bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + */ + public static function copyToStream( + StreamInterface $source, + StreamInterface $dest, + $maxLen = -1 + ) { + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read(1048576))) { + break; + } + } + return; + } + + $bytes = 0; + while (!$source->eof()) { + $buf = $source->read($maxLen - $bytes); + if (!($len = strlen($buf))) { + break; + } + $bytes += $len; + $dest->write($buf); + if ($bytes == $maxLen) { + break; + } + } + } + + /** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return string Returns the hash of the stream + * @throws SeekException + */ + public static function hash( + StreamInterface $stream, + $algo, + $rawOutput = false + ) { + $pos = $stream->tell(); + + if ($pos > 0 && !$stream->seek(0)) { + throw new SeekException($stream); + } + + $ctx = hash_init($algo); + while (!$stream->eof()) { + hash_update($ctx, $stream->read(1048576)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; + } + + /** + * Read a line from the stream up to the maximum allowed buffer length + * + * @param StreamInterface $stream Stream to read from + * @param int $maxLength Maximum buffer length + * + * @return string|bool + */ + public static function readline(StreamInterface $stream, $maxLength = null) + { + $buffer = ''; + $size = 0; + + while (!$stream->eof()) { + if (false === ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte == PHP_EOL || ++$size == $maxLength - 1) { + break; + } + } + + return $buffer; + } + + /** + * Alias of GuzzleHttp\Stream\Stream::factory. + * + * @param mixed $resource Resource to create + * @param array $options Associative array of stream options defined in + * {@see \GuzzleHttp\Stream\Stream::__construct} + * + * @return StreamInterface + * + * @see GuzzleHttp\Stream\Stream::factory + * @see GuzzleHttp\Stream\Stream::__construct + */ + public static function create($resource, array $options = []) + { + return Stream::factory($resource, $options); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AppendStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AppendStreamTest.php new file mode 100755 index 000000000..78798d9f7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AppendStreamTest.php @@ -0,0 +1,178 @@ +getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(false)); + $a->addStream($s); + } + + public function testValidatesSeekType() + { + $a = new AppendStream(); + $this->assertFalse($a->seek(100, SEEK_CUR)); + } + + public function testTriesToRewindOnSeek() + { + $a = new AppendStream(); + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable', 'seek', 'isSeekable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(true)); + $s->expects($this->once()) + ->method('seek') + ->will($this->returnValue(false)); + $a->addStream($s); + $this->assertFalse($a->seek(10)); + } + + public function testSeeksToPositionByReading() + { + $a = new AppendStream([ + Stream::factory('foo'), + Stream::factory('bar'), + Stream::factory('baz'), + ]); + + $this->assertTrue($a->seek(3)); + $this->assertEquals(3, $a->tell()); + $this->assertEquals('bar', $a->read(3)); + $a->seek(6); + $this->assertEquals(6, $a->tell()); + $this->assertEquals('baz', $a->read(3)); + } + + public function testDetachesEachStream() + { + $s1 = Stream::factory('foo'); + $s2 = Stream::factory('foo'); + $a = new AppendStream([$s1, $s2]); + $this->assertSame('foofoo', (string) $a); + $a->detach(); + $this->assertSame('', (string) $a); + $this->assertSame(0, $a->getSize()); + } + + public function testClosesEachStream() + { + $s1 = Stream::factory('foo'); + $a = new AppendStream([$s1]); + $a->close(); + $this->assertSame('', (string) $a); + } + + public function testIsNotWritable() + { + $a = new AppendStream([Stream::factory('foo')]); + $this->assertFalse($a->isWritable()); + $this->assertTrue($a->isSeekable()); + $this->assertTrue($a->isReadable()); + $this->assertFalse($a->write('foo')); + } + + public function testDoesNotNeedStreams() + { + $a = new AppendStream(); + $this->assertEquals('', (string) $a); + } + + public function testCanReadFromMultipleStreams() + { + $a = new AppendStream([ + Stream::factory('foo'), + Stream::factory('bar'), + Stream::factory('baz'), + ]); + $this->assertFalse($a->eof()); + $this->assertSame(0, $a->tell()); + $this->assertEquals('foo', $a->read(3)); + $this->assertEquals('bar', $a->read(3)); + $this->assertEquals('baz', $a->read(3)); + $this->assertTrue($a->eof()); + $this->assertSame(9, $a->tell()); + $this->assertEquals('foobarbaz', (string) $a); + } + + public function testCanDetermineSizeFromMultipleStreams() + { + $a = new AppendStream([ + Stream::factory('foo'), + Stream::factory('bar') + ]); + $this->assertEquals(6, $a->getSize()); + + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable', 'isReadable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(null)); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $a->addStream($s); + $this->assertNull($a->getSize()); + } + + public function testCatchesExceptionsWhenCastingToString() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['read', 'isReadable', 'eof']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('read') + ->will($this->throwException(new \RuntimeException('foo'))); + $s->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(true)); + $s->expects($this->any()) + ->method('eof') + ->will($this->returnValue(false)); + $a = new AppendStream([$s]); + $this->assertFalse($a->eof()); + $this->assertSame('', (string) $a); + } + + public function testCanDetach() + { + $s = new AppendStream(); + $s->detach(); + } + + public function testReturnsEmptyMetadata() + { + $s = new AppendStream(); + $this->assertEquals([], $s->getMetadata()); + $this->assertNull($s->getMetadata('foo')); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = new AppendStream(); + $p->attach('a'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php new file mode 100755 index 000000000..8c7899596 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php @@ -0,0 +1,186 @@ + function () { return false; }] + )); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Buffer must be readable and writable + */ + public function testValidatesWritableBuffer() + { + new AsyncReadStream(FnStream::decorate( + Stream::factory(), + ['isWritable' => function () { return false; }] + )); + } + + public function testValidatesHwmMetadata() + { + $a = new AsyncReadStream(Stream::factory(), [ + 'drain' => function() {} + ]); + $this->assertNull($this->readAttribute($a, 'drain')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage pump must be callable + */ + public function testValidatesPumpIsCallable() + { + new AsyncReadStream(new BufferStream(), ['pump' => true]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage drain must be callable + */ + public function testValidatesDrainIsCallable() + { + new AsyncReadStream(new BufferStream(), ['drain' => true]); + } + + public function testCanInitialize() + { + $buffer = new BufferStream(); + $a = new AsyncReadStream($buffer, [ + 'size' => 10, + 'drain' => function () {}, + 'pump' => function () {}, + ]); + $this->assertSame($buffer, $this->readAttribute($a, 'stream')); + $this->assertTrue(is_callable($this->readAttribute($a, 'drain'))); + $this->assertTrue(is_callable($this->readAttribute($a, 'pump'))); + $this->assertTrue($a->isReadable()); + $this->assertFalse($a->isSeekable()); + $this->assertFalse($a->isWritable()); + $this->assertFalse($a->write('foo')); + $this->assertEquals(10, $a->getSize()); + } + + public function testReadsFromBufferWithNoDrainOrPump() + { + $buffer = new BufferStream(); + $a = new AsyncReadStream($buffer); + $buffer->write('foo'); + $this->assertNull($a->getSize()); + $this->assertEquals('foo', $a->read(10)); + $this->assertEquals('', $a->read(10)); + } + + public function testCallsPumpForMoreDataWhenRequested() + { + $called = 0; + $buffer = new BufferStream(); + $a = new AsyncReadStream($buffer, [ + 'pump' => function ($size) use (&$called) { + $called++; + return str_repeat('.', $size); + } + ]); + $buffer->write('foobar'); + $this->assertEquals('foo', $a->read(3)); + $this->assertEquals(0, $called); + $this->assertEquals('bar.....', $a->read(8)); + $this->assertEquals(1, $called); + $this->assertEquals('..', $a->read(2)); + $this->assertEquals(2, $called); + } + + public function testCallsDrainWhenNeeded() + { + $called = 0; + $buffer = new BufferStream(5); + $a = new AsyncReadStream($buffer, [ + 'drain' => function (BufferStream $b) use (&$called, $buffer) { + $this->assertSame($b, $buffer); + $called++; + } + ]); + + $buffer->write('foobar'); + $this->assertEquals(6, $buffer->getSize()); + $this->assertEquals(0, $called); + + $a->read(3); + $this->assertTrue($this->readAttribute($a, 'needsDrain')); + $this->assertEquals(3, $buffer->getSize()); + $this->assertEquals(0, $called); + + $a->read(3); + $this->assertEquals(0, $buffer->getSize()); + $this->assertFalse($this->readAttribute($a, 'needsDrain')); + $this->assertEquals(1, $called); + } + + public function testCreatesBufferWithNoConfig() + { + list($buffer, $async) = AsyncReadStream::create(); + $this->assertInstanceOf('GuzzleHttp\Stream\BufferStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + } + + public function testCreatesBufferWithSpecifiedBuffer() + { + $buf = new BufferStream(); + list($buffer, $async) = AsyncReadStream::create(['buffer' => $buf]); + $this->assertSame($buf, $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + } + + public function testCreatesNullStream() + { + list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 0]); + $this->assertInstanceOf('GuzzleHttp\Stream\NullStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + } + + public function testCreatesDroppingStream() + { + list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 5]); + $this->assertInstanceOf('GuzzleHttp\Stream\DroppingStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + $buffer->write('12345678910'); + $this->assertEquals(5, $buffer->getSize()); + } + + public function testCreatesOnWriteStream() + { + $c = 0; + $b = new BufferStream(); + list($buffer, $async) = AsyncReadStream::create([ + 'buffer' => $b, + 'write' => function (BufferStream $buf, $data) use (&$c, $b) { + $this->assertSame($buf, $b); + $this->assertEquals('foo', $data); + $c++; + } + ]); + $this->assertInstanceOf('GuzzleHttp\Stream\FnStream', $buffer); + $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); + $this->assertEquals(0, $c); + $this->assertEquals(3, $buffer->write('foo')); + $this->assertEquals(1, $c); + $this->assertEquals(3, $buffer->write('foo')); + $this->assertEquals(2, $c); + $this->assertEquals('foofoo', (string) $buffer); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/BufferStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/BufferStreamTest.php new file mode 100755 index 000000000..f9bfea21d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/BufferStreamTest.php @@ -0,0 +1,69 @@ +assertTrue($b->isReadable()); + $this->assertTrue($b->isWritable()); + $this->assertFalse($b->isSeekable()); + $this->assertEquals(null, $b->getMetadata('foo')); + $this->assertEquals(10, $b->getMetadata('hwm')); + $this->assertEquals([], $b->getMetadata()); + } + + public function testRemovesReadDataFromBuffer() + { + $b = new BufferStream(); + $this->assertEquals(3, $b->write('foo')); + $this->assertEquals(3, $b->getSize()); + $this->assertFalse($b->eof()); + $this->assertEquals('foo', $b->read(10)); + $this->assertTrue($b->eof()); + $this->assertEquals('', $b->read(10)); + } + + public function testCanCastToStringOrGetContents() + { + $b = new BufferStream(); + $b->write('foo'); + $b->write('baz'); + $this->assertEquals('foo', $b->read(3)); + $b->write('bar'); + $this->assertEquals('bazbar', (string) $b); + $this->assertFalse($b->tell()); + } + + public function testDetachClearsBuffer() + { + $b = new BufferStream(); + $b->write('foo'); + $b->detach(); + $this->assertEquals(0, $b->tell()); + $this->assertTrue($b->eof()); + $this->assertEquals(3, $b->write('abc')); + $this->assertEquals('abc', $b->read(10)); + } + + public function testExceedingHighwaterMarkReturnsFalseButStillBuffers() + { + $b = new BufferStream(5); + $this->assertEquals(3, $b->write('hi ')); + $this->assertFalse($b->write('hello')); + $this->assertEquals('hi hello', (string) $b); + $this->assertEquals(4, $b->write('test')); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = new BufferStream(); + $p->attach('a'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/CachingStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/CachingStreamTest.php new file mode 100755 index 000000000..ea969b3ad --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/CachingStreamTest.php @@ -0,0 +1,136 @@ +decorated = Stream::factory('testing'); + $this->body = new CachingStream($this->decorated); + } + + public function tearDown() + { + $this->decorated->close(); + $this->body->close(); + } + + public function testUsesRemoteSizeIfPossible() + { + $body = Stream::factory('test'); + $caching = new CachingStream($body); + $this->assertEquals(4, $caching->getSize()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot seek to byte 10 + */ + public function testCannotSeekPastWhatHasBeenRead() + { + $this->body->seek(10); + } + + public function testCannotUseSeekEnd() + { + $this->assertFalse($this->body->seek(2, SEEK_END)); + } + + public function testRewindUsesSeek() + { + $a = Stream::factory('foo'); + $d = $this->getMockBuilder('GuzzleHttp\Stream\CachingStream') + ->setMethods(array('seek')) + ->setConstructorArgs(array($a)) + ->getMock(); + $d->expects($this->once()) + ->method('seek') + ->with(0) + ->will($this->returnValue(true)); + $d->seek(0); + } + + public function testCanSeekToReadBytes() + { + $this->assertEquals('te', $this->body->read(2)); + $this->body->seek(0); + $this->assertEquals('test', $this->body->read(4)); + $this->assertEquals(4, $this->body->tell()); + $this->body->seek(2); + $this->assertEquals(2, $this->body->tell()); + $this->body->seek(2, SEEK_CUR); + $this->assertEquals(4, $this->body->tell()); + $this->assertEquals('ing', $this->body->read(3)); + } + + public function testWritesToBufferStream() + { + $this->body->read(2); + $this->body->write('hi'); + $this->body->seek(0); + $this->assertEquals('tehiing', (string) $this->body); + } + + public function testSkipsOverwrittenBytes() + { + $decorated = Stream::factory( + implode("\n", array_map(function ($n) { + return str_pad($n, 4, '0', STR_PAD_LEFT); + }, range(0, 25))) + ); + + $body = new CachingStream($decorated); + + $this->assertEquals("0000\n", Utils::readline($body)); + $this->assertEquals("0001\n", Utils::readline($body)); + // Write over part of the body yet to be read, so skip some bytes + $this->assertEquals(5, $body->write("TEST\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + // Read, which skips bytes, then reads + $this->assertEquals("0003\n", Utils::readline($body)); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("0004\n", Utils::readline($body)); + $this->assertEquals("0005\n", Utils::readline($body)); + + // Overwrite part of the cached body (so don't skip any bytes) + $body->seek(5); + $this->assertEquals(5, $body->write("ABCD\n")); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("TEST\n", Utils::readline($body)); + $this->assertEquals("0003\n", Utils::readline($body)); + $this->assertEquals("0004\n", Utils::readline($body)); + $this->assertEquals("0005\n", Utils::readline($body)); + $this->assertEquals("0006\n", Utils::readline($body)); + $this->assertEquals(5, $body->write("1234\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + + // Seek to 0 and ensure the overwritten bit is replaced + $body->seek(0); + $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); + + // Ensure that casting it to a string does not include the bit that was overwritten + $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); + } + + public function testClosesBothStreams() + { + $s = fopen('php://temp', 'r'); + $a = Stream::factory($s); + $d = new CachingStream($a); + $d->close(); + $this->assertFalse(is_resource($s)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php new file mode 100755 index 000000000..bb2cb2204 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php @@ -0,0 +1,26 @@ +assertEquals(3, $drop->write('hel')); + $this->assertFalse($drop->write('lo')); + $this->assertEquals(5, $drop->getSize()); + $this->assertEquals('hello', $drop->read(5)); + $this->assertEquals(0, $drop->getSize()); + $drop->write('12345678910'); + $this->assertEquals(5, $stream->getSize()); + $this->assertEquals(5, $drop->getSize()); + $this->assertEquals('12345', (string) $drop); + $this->assertEquals(0, $drop->getSize()); + $drop->write('hello'); + $this->assertFalse($drop->write('test')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php new file mode 100755 index 000000000..fd8cd1ad2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php @@ -0,0 +1,16 @@ +assertSame($s, $e->getStream()); + $this->assertContains('10', $e->getMessage()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/FnStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/FnStreamTest.php new file mode 100755 index 000000000..6cc336b91 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/FnStreamTest.php @@ -0,0 +1,89 @@ +seek(1); + } + + public function testProxiesToFunction() + { + $s = new FnStream([ + 'read' => function ($len) { + $this->assertEquals(3, $len); + return 'foo'; + } + ]); + + $this->assertEquals('foo', $s->read(3)); + } + + public function testCanCloseOnDestruct() + { + $called = false; + $s = new FnStream([ + 'close' => function () use (&$called) { + $called = true; + } + ]); + unset($s); + $this->assertTrue($called); + } + + public function testDoesNotRequireClose() + { + $s = new FnStream([]); + unset($s); + } + + public function testDecoratesStream() + { + $a = Stream::factory('foo'); + $b = FnStream::decorate($a, []); + $this->assertEquals(3, $b->getSize()); + $this->assertEquals($b->isWritable(), true); + $this->assertEquals($b->isReadable(), true); + $this->assertEquals($b->isSeekable(), true); + $this->assertEquals($b->read(3), 'foo'); + $this->assertEquals($b->tell(), 3); + $this->assertEquals($a->tell(), 3); + $this->assertEquals($b->eof(), true); + $this->assertEquals($a->eof(), true); + $b->seek(0); + $this->assertEquals('foo', (string) $b); + $b->seek(0); + $this->assertEquals('foo', $b->getContents()); + $this->assertEquals($a->getMetadata(), $b->getMetadata()); + $b->seek(0, SEEK_END); + $b->write('bar'); + $this->assertEquals('foobar', (string) $b); + $this->assertInternalType('resource', $b->detach()); + $b->close(); + } + + public function testDecoratesWithCustomizations() + { + $called = false; + $a = Stream::factory('foo'); + $b = FnStream::decorate($a, [ + 'read' => function ($len) use (&$called, $a) { + $called = true; + return $a->read($len); + } + ]); + $this->assertEquals('foo', $b->read(3)); + $this->assertTrue($called); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php new file mode 100755 index 000000000..33c3eccb4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php @@ -0,0 +1,99 @@ +assertSame('foo', fread($handle, 3)); + $this->assertSame(3, ftell($handle)); + $this->assertSame(3, fwrite($handle, 'bar')); + $this->assertSame(0, fseek($handle, 0)); + $this->assertSame('foobar', fread($handle, 6)); + $this->assertTrue(feof($handle)); + + // This fails on HHVM for some reason + if (!defined('HHVM_VERSION')) { + $this->assertEquals([ + 'dev' => 0, + 'ino' => 0, + 'mode' => 33206, + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => 6, + 'atime' => 0, + 'mtime' => 0, + 'ctime' => 0, + 'blksize' => 0, + 'blocks' => 0, + 0 => 0, + 1 => 0, + 2 => 33206, + 3 => 0, + 4 => 0, + 5 => 0, + 6 => 0, + 7 => 6, + 8 => 0, + 9 => 0, + 10 => 0, + 11 => 0, + 12 => 0, + ], fstat($handle)); + } + + $this->assertTrue(fclose($handle)); + $this->assertSame('foobar', (string) $stream); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesStream() + { + $stream = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable', 'isWritable']) + ->getMockForAbstractClass(); + $stream->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(false)); + $stream->expects($this->once()) + ->method('isWritable') + ->will($this->returnValue(false)); + GuzzleStreamWrapper::getResource($stream); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Warning + */ + public function testReturnsFalseWhenStreamDoesNotExist() + { + fopen('guzzle://foo', 'r'); + } + + public function testCanOpenReadonlyStream() + { + $stream = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isReadable', 'isWritable']) + ->getMockForAbstractClass(); + $stream->expects($this->once()) + ->method('isReadable') + ->will($this->returnValue(false)); + $stream->expects($this->once()) + ->method('isWritable') + ->will($this->returnValue(true)); + $r = GuzzleStreamWrapper::getResource($stream); + $this->assertInternalType('resource', $r); + fclose($r); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/InflateStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/InflateStreamTest.php new file mode 100755 index 000000000..ead9356a5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/InflateStreamTest.php @@ -0,0 +1,16 @@ +assertEquals('test', (string) $b); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php new file mode 100755 index 000000000..79e0078e8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php @@ -0,0 +1,64 @@ +fname = tempnam('/tmp', 'tfile'); + + if (file_exists($this->fname)) { + unlink($this->fname); + } + } + + public function tearDown() + { + if (file_exists($this->fname)) { + unlink($this->fname); + } + } + + public function testOpensLazily() + { + $l = new LazyOpenStream($this->fname, 'w+'); + $l->write('foo'); + $this->assertInternalType('array', $l->getMetadata()); + $this->assertFileExists($this->fname); + $this->assertEquals('foo', file_get_contents($this->fname)); + $this->assertEquals('foo', (string) $l); + } + + public function testProxiesToFile() + { + file_put_contents($this->fname, 'foo'); + $l = new LazyOpenStream($this->fname, 'r'); + $this->assertEquals('foo', $l->read(4)); + $this->assertTrue($l->eof()); + $this->assertEquals(3, $l->tell()); + $this->assertTrue($l->isReadable()); + $this->assertTrue($l->isSeekable()); + $this->assertFalse($l->isWritable()); + $l->seek(1); + $this->assertEquals('oo', $l->getContents()); + $this->assertEquals('foo', (string) $l); + $this->assertEquals(3, $l->getSize()); + $this->assertInternalType('array', $l->getMetadata()); + $l->close(); + } + + public function testDetachesUnderlyingStream() + { + file_put_contents($this->fname, 'foo'); + $l = new LazyOpenStream($this->fname, 'r'); + $r = $l->detach(); + $this->assertInternalType('resource', $r); + fseek($r, 0); + $this->assertEquals('foo', stream_get_contents($r)); + fclose($r); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LimitStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LimitStreamTest.php new file mode 100755 index 000000000..efb1dc58f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/LimitStreamTest.php @@ -0,0 +1,133 @@ +decorated = Stream::factory(fopen(__FILE__, 'r')); + $this->body = new LimitStream($this->decorated, 10, 3); + } + + public function testReturnsSubset() + { + $body = new LimitStream(Stream::factory('foo'), -1, 1); + $this->assertEquals('oo', (string) $body); + $this->assertTrue($body->eof()); + $body->seek(0); + $this->assertFalse($body->eof()); + $this->assertEquals('oo', $body->read(100)); + $this->assertTrue($body->eof()); + } + + public function testReturnsSubsetWhenCastToString() + { + $body = Stream::factory('foo_baz_bar'); + $limited = new LimitStream($body, 3, 4); + $this->assertEquals('baz', (string) $limited); + } + + public function testReturnsSubsetOfEmptyBodyWhenCastToString() + { + $body = Stream::factory(''); + $limited = new LimitStream($body, 0, 10); + $this->assertEquals('', (string) $limited); + } + + public function testSeeksWhenConstructed() + { + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + } + + public function testAllowsBoundedSeek() + { + $this->assertEquals(true, $this->body->seek(100)); + $this->assertEquals(10, $this->body->tell()); + $this->assertEquals(13, $this->decorated->tell()); + $this->assertEquals(true, $this->body->seek(0)); + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + $this->assertEquals(false, $this->body->seek(-10)); + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + $this->assertEquals(true, $this->body->seek(5)); + $this->assertEquals(5, $this->body->tell()); + $this->assertEquals(8, $this->decorated->tell()); + $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); + } + + public function testReadsOnlySubsetOfData() + { + $data = $this->body->read(100); + $this->assertEquals(10, strlen($data)); + $this->assertFalse($this->body->read(1000)); + + $this->body->setOffset(10); + $newData = $this->body->read(100); + $this->assertEquals(10, strlen($newData)); + $this->assertNotSame($data, $newData); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\SeekException + * @expectedExceptionMessage Could not seek the stream to position 2 + */ + public function testThrowsWhenCurrentGreaterThanOffsetSeek() + { + $a = Stream::factory('foo_bar'); + $b = new NoSeekStream($a); + $c = new LimitStream($b); + $a->getContents(); + $c->setOffset(2); + } + + public function testClaimsConsumedWhenReadLimitIsReached() + { + $this->assertFalse($this->body->eof()); + $this->body->read(1000); + $this->assertTrue($this->body->eof()); + } + + public function testContentLengthIsBounded() + { + $this->assertEquals(10, $this->body->getSize()); + } + + public function testGetContentsIsBasedOnSubset() + { + $body = new LimitStream(Stream::factory('foobazbar'), 3, 3); + $this->assertEquals('baz', $body->getContents()); + } + + public function testReturnsNullIfSizeCannotBeDetermined() + { + $a = new FnStream([ + 'getSize' => function () { return null; }, + 'tell' => function () { return 0; }, + ]); + $b = new LimitStream($a); + $this->assertNull($b->getSize()); + } + + public function testLengthLessOffsetWhenNoLimitSize() + { + $a = Stream::factory('foo_bar'); + $b = new LimitStream($a, -1, 4); + $this->assertEquals(3, $b->getSize()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php new file mode 100755 index 000000000..21b7c6d22 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php @@ -0,0 +1,41 @@ +getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable', 'seek']) + ->getMockForAbstractClass(); + $s->expects($this->never())->method('seek'); + $s->expects($this->never())->method('isSeekable'); + $wrapped = new NoSeekStream($s); + $this->assertFalse($wrapped->isSeekable()); + $this->assertFalse($wrapped->seek(2)); + } + + public function testHandlesClose() + { + $s = Stream::factory('foo'); + $wrapped = new NoSeekStream($s); + $wrapped->close(); + $this->assertFalse($wrapped->write('foo')); + } + + public function testCanAttach() + { + $s1 = Stream::factory('foo'); + $s2 = Stream::factory('bar'); + $wrapped = new NoSeekStream($s1); + $wrapped->attach($s2->detach()); + $this->assertEquals('bar', (string) $wrapped); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NullStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NullStreamTest.php new file mode 100755 index 000000000..8e4143159 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/NullStreamTest.php @@ -0,0 +1,39 @@ +assertEquals('', $b->read(10)); + $this->assertEquals(4, $b->write('test')); + $this->assertEquals('', (string) $b); + $this->assertNull($b->getMetadata('a')); + $this->assertEquals([], $b->getMetadata()); + $this->assertEquals(0, $b->getSize()); + $this->assertEquals('', $b->getContents()); + $this->assertEquals(0, $b->tell()); + + $this->assertTrue($b->isReadable()); + $this->assertTrue($b->isWritable()); + $this->assertTrue($b->isSeekable()); + $this->assertFalse($b->seek(10)); + + $this->assertTrue($b->eof()); + $b->detach(); + $this->assertTrue($b->eof()); + $b->close(); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = new NullStream(); + $p->attach('a'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/PumpStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/PumpStreamTest.php new file mode 100755 index 000000000..2d20ce90c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/PumpStreamTest.php @@ -0,0 +1,77 @@ + ['foo' => 'bar'], + 'size' => 100 + ]); + + $this->assertEquals('bar', $p->getMetadata('foo')); + $this->assertEquals(['foo' => 'bar'], $p->getMetadata()); + $this->assertEquals(100, $p->getSize()); + } + + public function testCanReadFromCallable() + { + $p = Stream::factory(function ($size) { + return 'a'; + }); + $this->assertEquals('a', $p->read(1)); + $this->assertEquals(1, $p->tell()); + $this->assertEquals('aaaaa', $p->read(5)); + $this->assertEquals(6, $p->tell()); + } + + public function testStoresExcessDataInBuffer() + { + $called = []; + $p = Stream::factory(function ($size) use (&$called) { + $called[] = $size; + return 'abcdef'; + }); + $this->assertEquals('a', $p->read(1)); + $this->assertEquals('b', $p->read(1)); + $this->assertEquals('cdef', $p->read(4)); + $this->assertEquals('abcdefabc', $p->read(9)); + $this->assertEquals([1, 9, 3], $called); + } + + public function testInifiniteStreamWrappedInLimitStream() + { + $p = Stream::factory(function () { return 'a'; }); + $s = new LimitStream($p, 5); + $this->assertEquals('aaaaa', (string) $s); + } + + public function testDescribesCapabilities() + { + $p = Stream::factory(function () {}); + $this->assertTrue($p->isReadable()); + $this->assertFalse($p->isSeekable()); + $this->assertFalse($p->isWritable()); + $this->assertNull($p->getSize()); + $this->assertFalse($p->write('aa')); + $this->assertEquals('', $p->getContents()); + $this->assertEquals('', (string) $p); + $p->close(); + $this->assertEquals('', $p->read(10)); + $this->assertTrue($p->eof()); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttach() + { + $p = Stream::factory(function () {}); + $p->attach('a'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php new file mode 100755 index 000000000..2ba79addf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php @@ -0,0 +1,147 @@ +c = fopen('php://temp', 'r+'); + fwrite($this->c, 'foo'); + fseek($this->c, 0); + $this->a = Stream::factory($this->c); + $this->b = new Str($this->a); + } + + public function testCatchesExceptionsWhenCastingToString() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['read']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('read') + ->will($this->throwException(new \Exception('foo'))); + $msg = ''; + set_error_handler(function ($errNo, $str) use (&$msg) { $msg = $str; }); + echo new Str($s); + restore_error_handler(); + $this->assertContains('foo', $msg); + } + + public function testToString() + { + $this->assertEquals('foo', (string) $this->b); + } + + public function testHasSize() + { + $this->assertEquals(3, $this->b->getSize()); + $this->assertSame($this->b, $this->b->setSize(2)); + $this->assertEquals(2, $this->b->getSize()); + } + + public function testReads() + { + $this->assertEquals('foo', $this->b->read(10)); + } + + public function testCheckMethods() + { + $this->assertEquals($this->a->isReadable(), $this->b->isReadable()); + $this->assertEquals($this->a->isWritable(), $this->b->isWritable()); + $this->assertEquals($this->a->isSeekable(), $this->b->isSeekable()); + } + + public function testSeeksAndTells() + { + $this->assertTrue($this->b->seek(1)); + $this->assertEquals(1, $this->a->tell()); + $this->assertEquals(1, $this->b->tell()); + $this->assertTrue($this->b->seek(0)); + $this->assertEquals(0, $this->a->tell()); + $this->assertEquals(0, $this->b->tell()); + $this->assertTrue($this->b->seek(0, SEEK_END)); + $this->assertEquals(3, $this->a->tell()); + $this->assertEquals(3, $this->b->tell()); + } + + public function testGetsContents() + { + $this->assertEquals('foo', $this->b->getContents()); + $this->assertEquals('', $this->b->getContents()); + $this->b->seek(1); + $this->assertEquals('oo', $this->b->getContents(1)); + } + + public function testCloses() + { + $this->b->close(); + $this->assertFalse(is_resource($this->c)); + } + + public function testDetaches() + { + $this->b->detach(); + $this->assertFalse($this->b->isReadable()); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException + */ + public function testCannotAttachByDefault() + { + $this->b->attach('a'); + } + + public function testWrapsMetadata() + { + $this->assertSame($this->b->getMetadata(), $this->a->getMetadata()); + $this->assertSame($this->b->getMetadata('uri'), $this->a->getMetadata('uri')); + } + + public function testWrapsWrites() + { + $this->b->seek(0, SEEK_END); + $this->b->write('foo'); + $this->assertEquals('foofoo', (string) $this->a); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testThrowsWithInvalidGetter() + { + $this->b->foo; + } + + /** + * @expectedException \BadMethodCallException + */ + public function testThrowsWhenGetterNotImplemented() + { + $s = new BadStream(); + $s->stream; + } +} + +class BadStream +{ + use StreamDecoratorTrait; + + public function __construct() {} +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamTest.php new file mode 100755 index 000000000..2985bfbb1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/StreamTest.php @@ -0,0 +1,252 @@ +assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isSeekable()); + $this->assertEquals('php://temp', $stream->getMetadata('uri')); + $this->assertInternalType('array', $stream->getMetadata()); + $this->assertEquals(4, $stream->getSize()); + $this->assertFalse($stream->eof()); + $stream->close(); + } + + public function testStreamClosesHandleOnDestruct() + { + $handle = fopen('php://temp', 'r'); + $stream = new Stream($handle); + unset($stream); + $this->assertFalse(is_resource($handle)); + } + + public function testConvertsToString() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('data', (string) $stream); + $this->assertEquals('data', (string) $stream); + $stream->close(); + } + + public function testGetsContents() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('', $stream->getContents()); + $stream->seek(0); + $this->assertEquals('data', $stream->getContents()); + $this->assertEquals('', $stream->getContents()); + } + + public function testChecksEof() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertFalse($stream->eof()); + $stream->read(4); + $this->assertTrue($stream->eof()); + $stream->close(); + } + + public function testAllowsSettingManualSize() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $stream->setSize(10); + $this->assertEquals(10, $stream->getSize()); + $stream->close(); + } + + public function testGetSize() + { + $size = filesize(__FILE__); + $handle = fopen(__FILE__, 'r'); + $stream = new Stream($handle); + $this->assertEquals($size, $stream->getSize()); + // Load from cache + $this->assertEquals($size, $stream->getSize()); + $stream->close(); + } + + public function testEnsuresSizeIsConsistent() + { + $h = fopen('php://temp', 'w+'); + $this->assertEquals(3, fwrite($h, 'foo')); + $stream = new Stream($h); + $this->assertEquals(3, $stream->getSize()); + $this->assertEquals(4, $stream->write('test')); + $this->assertEquals(7, $stream->getSize()); + $this->assertEquals(7, $stream->getSize()); + $stream->close(); + } + + public function testProvidesStreamPosition() + { + $handle = fopen('php://temp', 'w+'); + $stream = new Stream($handle); + $this->assertEquals(0, $stream->tell()); + $stream->write('foo'); + $this->assertEquals(3, $stream->tell()); + $stream->seek(1); + $this->assertEquals(1, $stream->tell()); + $this->assertSame(ftell($handle), $stream->tell()); + $stream->close(); + } + + public function testKeepsPositionOfResource() + { + $h = fopen(__FILE__, 'r'); + fseek($h, 10); + $stream = Stream::factory($h); + $this->assertEquals(10, $stream->tell()); + $stream->close(); + } + + public function testCanDetachAndAttachStream() + { + $r = fopen('php://temp', 'w+'); + $stream = new Stream($r); + $stream->write('foo'); + $this->assertTrue($stream->isReadable()); + $this->assertSame($r, $stream->detach()); + $this->assertNull($stream->detach()); + + $this->assertFalse($stream->isReadable()); + $this->assertFalse($stream->read(10)); + $this->assertFalse($stream->isWritable()); + $this->assertFalse($stream->write('bar')); + $this->assertFalse($stream->isSeekable()); + $this->assertFalse($stream->seek(10)); + $this->assertFalse($stream->tell()); + $this->assertTrue($stream->eof()); + $this->assertNull($stream->getSize()); + $this->assertSame('', (string) $stream); + $this->assertSame('', $stream->getContents()); + + $stream->attach($r); + $stream->seek(0); + $this->assertEquals('foo', $stream->getContents()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isSeekable()); + + $stream->close(); + } + + public function testCloseClearProperties() + { + $handle = fopen('php://temp', 'r+'); + $stream = new Stream($handle); + $stream->close(); + + $this->assertEmpty($stream->getMetadata()); + $this->assertFalse($stream->isSeekable()); + $this->assertFalse($stream->isReadable()); + $this->assertFalse($stream->isWritable()); + $this->assertNull($stream->getSize()); + } + + public function testCreatesWithFactory() + { + $stream = Stream::factory('foo'); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $stream); + $this->assertEquals('foo', $stream->getContents()); + $stream->close(); + } + + public function testFactoryCreatesFromEmptyString() + { + $s = Stream::factory(); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + } + + public function testFactoryCreatesFromResource() + { + $r = fopen(__FILE__, 'r'); + $s = Stream::factory($r); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + $this->assertSame(file_get_contents(__FILE__), (string) $s); + } + + public function testFactoryCreatesFromObjectWithToString() + { + $r = new HasToString(); + $s = Stream::factory($r); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + $this->assertEquals('foo', (string) $s); + } + + public function testCreatePassesThrough() + { + $s = Stream::factory('foo'); + $this->assertSame($s, Stream::factory($s)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionForUnknown() + { + Stream::factory(new \stdClass()); + } + + public function testReturnsCustomMetadata() + { + $s = Stream::factory('foo', ['metadata' => ['hwm' => 3]]); + $this->assertEquals(3, $s->getMetadata('hwm')); + $this->assertArrayHasKey('hwm', $s->getMetadata()); + } + + public function testCanSetSize() + { + $s = Stream::factory('', ['size' => 10]); + $this->assertEquals(10, $s->getSize()); + } + + public function testCanCreateIteratorBasedStream() + { + $a = new \ArrayIterator(['foo', 'bar', '123']); + $p = Stream::factory($a); + $this->assertInstanceOf('GuzzleHttp\Stream\PumpStream', $p); + $this->assertEquals('foo', $p->read(3)); + $this->assertFalse($p->eof()); + $this->assertEquals('b', $p->read(1)); + $this->assertEquals('a', $p->read(1)); + $this->assertEquals('r12', $p->read(3)); + $this->assertFalse($p->eof()); + $this->assertEquals('3', $p->getContents()); + $this->assertTrue($p->eof()); + $this->assertEquals(9, $p->tell()); + } +} + +class HasToString +{ + public function __toString() { + return 'foo'; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/UtilsTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/UtilsTest.php new file mode 100755 index 000000000..6e3e3b216 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/guzzlehttp/streams/tests/UtilsTest.php @@ -0,0 +1,155 @@ +assertEquals('foobaz', Utils::copyToString($s)); + $s->seek(0); + $this->assertEquals('foo', Utils::copyToString($s, 3)); + $this->assertEquals('baz', Utils::copyToString($s, 3)); + $this->assertEquals('', Utils::copyToString($s)); + } + + public function testCopiesToStringStopsWhenReadFails() + { + $s1 = Stream::factory('foobaz'); + $s1 = FnStream::decorate($s1, [ + 'read' => function () { + return false; + } + ]); + $result = Utils::copyToString($s1); + $this->assertEquals('', $result); + } + + public function testCopiesToStream() + { + $s1 = Stream::factory('foobaz'); + $s2 = Stream::factory(''); + Utils::copyToStream($s1, $s2); + $this->assertEquals('foobaz', (string) $s2); + $s2 = Stream::factory(''); + $s1->seek(0); + Utils::copyToStream($s1, $s2, 3); + $this->assertEquals('foo', (string) $s2); + Utils::copyToStream($s1, $s2, 3); + $this->assertEquals('foobaz', (string) $s2); + } + + public function testStopsCopyToStreamWhenWriteFails() + { + $s1 = Stream::factory('foobaz'); + $s2 = Stream::factory(''); + $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); + Utils::copyToStream($s1, $s2); + $this->assertEquals('', (string) $s2); + } + + public function testStopsCopyToSteamWhenWriteFailsWithMaxLen() + { + $s1 = Stream::factory('foobaz'); + $s2 = Stream::factory(''); + $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); + Utils::copyToStream($s1, $s2, 10); + $this->assertEquals('', (string) $s2); + } + + public function testStopsCopyToSteamWhenReadFailsWithMaxLen() + { + $s1 = Stream::factory('foobaz'); + $s1 = FnStream::decorate($s1, ['read' => function () { return ''; }]); + $s2 = Stream::factory(''); + Utils::copyToStream($s1, $s2, 10); + $this->assertEquals('', (string) $s2); + } + + public function testReadsLines() + { + $s = Stream::factory("foo\nbaz\nbar"); + $this->assertEquals("foo\n", Utils::readline($s)); + $this->assertEquals("baz\n", Utils::readline($s)); + $this->assertEquals("bar", Utils::readline($s)); + } + + public function testReadsLinesUpToMaxLength() + { + $s = Stream::factory("12345\n"); + $this->assertEquals("123", Utils::readline($s, 4)); + $this->assertEquals("45\n", Utils::readline($s)); + } + + public function testReadsLineUntilFalseReturnedFromRead() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\Stream') + ->setMethods(['read', 'eof']) + ->disableOriginalConstructor() + ->getMock(); + $s->expects($this->exactly(2)) + ->method('read') + ->will($this->returnCallback(function () { + static $c = false; + if ($c) { + return false; + } + $c = true; + return 'h'; + })); + $s->expects($this->exactly(2)) + ->method('eof') + ->will($this->returnValue(false)); + $this->assertEquals("h", Utils::readline($s)); + } + + public function testCalculatesHash() + { + $s = Stream::factory('foobazbar'); + $this->assertEquals(md5('foobazbar'), Utils::hash($s, 'md5')); + } + + /** + * @expectedException \GuzzleHttp\Stream\Exception\SeekException + */ + public function testCalculatesHashThrowsWhenSeekFails() + { + $s = new NoSeekStream(Stream::factory('foobazbar')); + $s->read(2); + Utils::hash($s, 'md5'); + } + + public function testCalculatesHashSeeksToOriginalPosition() + { + $s = Stream::factory('foobazbar'); + $s->seek(4); + $this->assertEquals(md5('foobazbar'), Utils::hash($s, 'md5')); + $this->assertEquals(4, $s->tell()); + } + + public function testOpensFilesSuccessfully() + { + $r = Utils::open(__FILE__, 'r'); + $this->assertInternalType('resource', $r); + fclose($r); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to open /path/to/does/not/exist using mode r + */ + public function testThrowsExceptionNotWarning() + { + Utils::open('/path/to/does/not/exist', 'r'); + } + + public function testProxiesToFactory() + { + $this->assertEquals('foo', (string) Utils::create('foo')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.gitignore b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.gitignore new file mode 100755 index 000000000..36f67f452 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.gitignore @@ -0,0 +1,5 @@ +bin +composer.lock +composer.phar +phpunit.xml +vendor diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.travis.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.travis.yml new file mode 100755 index 000000000..1bfa528e1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/.travis.yml @@ -0,0 +1,25 @@ +language: php + +php: + - 5.4 + - 5.5 + +env: TEST_SERVER="http://127.0.0.1:8080/server.php" TEST_PROXY="127.0.0.1:3128" PHP_FCGI_CHILDREN=10 PHP_FCGI_MAX_REQUESTS=10 + +before_install: + - echo "" | sudo add-apt-repository ppa:nginx/stable > /dev/null 2>&1 + - sudo apt-get -qq update + - sudo apt-get -qq install nginx squid + - sudo stop squid3 + - curl --version + +before_script: + - sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf + - echo "cgi.fix_pathinfo = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - ~/.phpenv/versions/$(phpenv version-name)/sbin/php-fpm + - sudo nginx -p test -c etc/nginx.conf + - sudo squid3 -f test/etc/squid.conf + - composer self-update + - composer install --dev + +script: ./bin/phpunit diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/CHANGELOG.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/CHANGELOG.md new file mode 100755 index 000000000..26032c90c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.14 (June 2, 2015) + + * Changed default HTTP protocol version to 1.1 + * Added verify host control to `AbstractClient` diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/LICENSE new file mode 100755 index 000000000..e234f8007 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010-2011 Kris Wallsmith + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/README.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/README.md new file mode 100755 index 000000000..2cd6acfb7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/README.md @@ -0,0 +1,28 @@ +[![Build Status](https://secure.travis-ci.org/kriswallsmith/Buzz.png?branch=master)](http://travis-ci.org/kriswallsmith/Buzz) + +Buzz is a lightweight PHP 5.3 library for issuing HTTP requests. + +```php +get('http://www.google.com'); + +echo $browser->getLastRequest()."\n"; +echo $response; +``` + +You can also use the low-level HTTP classes directly. + +```php +send($request, $response); + +echo $request; +echo $response; +``` diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/composer.json new file mode 100755 index 000000000..725444fb6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/composer.json @@ -0,0 +1,32 @@ +{ + "name": "kriswallsmith/buzz", + "description": "Lightweight HTTP client", + "keywords": ["http client", "curl"], + "homepage": "https://github.com/kriswallsmith/Buzz", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "suggest": { + "ext-curl": "*" + }, + "config": { + "bin-dir": "bin" + }, + "autoload": { + "psr-0": { + "Buzz": "lib/" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Browser.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Browser.php new file mode 100755 index 000000000..dbc47ec89 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Browser.php @@ -0,0 +1,195 @@ +client = $client ?: new FileGetContents(); + $this->factory = $factory ?: new Factory(); + } + + public function get($url, $headers = array()) + { + return $this->call($url, RequestInterface::METHOD_GET, $headers); + } + + public function post($url, $headers = array(), $content = '') + { + return $this->call($url, RequestInterface::METHOD_POST, $headers, $content); + } + + public function head($url, $headers = array()) + { + return $this->call($url, RequestInterface::METHOD_HEAD, $headers); + } + + public function patch($url, $headers = array(), $content = '') + { + return $this->call($url, RequestInterface::METHOD_PATCH, $headers, $content); + } + + public function put($url, $headers = array(), $content = '') + { + return $this->call($url, RequestInterface::METHOD_PUT, $headers, $content); + } + + public function delete($url, $headers = array(), $content = '') + { + return $this->call($url, RequestInterface::METHOD_DELETE, $headers, $content); + } + + /** + * Sends a request. + * + * @param string $url The URL to call + * @param string $method The request method to use + * @param array $headers An array of request headers + * @param string $content The request content + * + * @return MessageInterface The response object + */ + public function call($url, $method, $headers = array(), $content = '') + { + $request = $this->factory->createRequest($method); + + if (!$url instanceof Url) { + $url = new Url($url); + } + + $url->applyToRequest($request); + + $request->addHeaders($headers); + $request->setContent($content); + + return $this->send($request); + } + + /** + * Sends a form request. + * + * @param string $url The URL to submit to + * @param array $fields An array of fields + * @param string $method The request method to use + * @param array $headers An array of request headers + * + * @return MessageInterface The response object + */ + public function submit($url, array $fields, $method = RequestInterface::METHOD_POST, $headers = array()) + { + $request = $this->factory->createFormRequest(); + + if (!$url instanceof Url) { + $url = new Url($url); + } + + $url->applyToRequest($request); + + $request->addHeaders($headers); + $request->setMethod($method); + $request->setFields($fields); + + return $this->send($request); + } + + /** + * Sends a request. + * + * @param RequestInterface $request A request object + * @param MessageInterface $response A response object + * + * @return MessageInterface The response + */ + public function send(RequestInterface $request, MessageInterface $response = null) + { + if (null === $response) { + $response = $this->factory->createResponse(); + } + + if ($this->listener) { + $this->listener->preSend($request); + } + + $this->client->send($request, $response); + + $this->lastRequest = $request; + $this->lastResponse = $response; + + if ($this->listener) { + $this->listener->postSend($request, $response); + } + + return $response; + } + + public function getLastRequest() + { + return $this->lastRequest; + } + + public function getLastResponse() + { + return $this->lastResponse; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + } + + public function getClient() + { + return $this->client; + } + + public function setMessageFactory(FactoryInterface $factory) + { + $this->factory = $factory; + } + + public function getMessageFactory() + { + return $this->factory; + } + + public function setListener(ListenerInterface $listener) + { + $this->listener = $listener; + } + + public function getListener() + { + return $this->listener; + } + + public function addListener(ListenerInterface $listener) + { + if (!$this->listener) { + $this->listener = $listener; + } elseif ($this->listener instanceof ListenerChain) { + $this->listener->addListener($listener); + } else { + $this->listener = new ListenerChain(array( + $this->listener, + $listener, + )); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractClient.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractClient.php new file mode 100755 index 000000000..f7c188ca1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractClient.php @@ -0,0 +1,73 @@ +ignoreErrors = $ignoreErrors; + } + + public function getIgnoreErrors() + { + return $this->ignoreErrors; + } + + public function setMaxRedirects($maxRedirects) + { + $this->maxRedirects = $maxRedirects; + } + + public function getMaxRedirects() + { + return $this->maxRedirects; + } + + public function setTimeout($timeout) + { + $this->timeout = $timeout; + } + + public function getTimeout() + { + return $this->timeout; + } + + public function setVerifyPeer($verifyPeer) + { + $this->verifyPeer = $verifyPeer; + } + + public function getVerifyPeer() + { + return $this->verifyPeer; + } + + public function getVerifyHost() + { + return $this->verifyHost; + } + + public function setVerifyHost($verifyHost) + { + $this->verifyHost = $verifyHost; + } + + public function setProxy($proxy) + { + $this->proxy = $proxy; + } + + public function getProxy() + { + return $this->proxy; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractCurl.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractCurl.php new file mode 100755 index 000000000..2c25fc6f5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractCurl.php @@ -0,0 +1,232 @@ +options = array( + CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, + CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, + ); + } + } + + /** + * Creates a new cURL resource. + * + * @see curl_init() + * + * @return resource A new cURL resource + * + * @throws ClientException If unable to create a cURL resource + */ + protected static function createCurlHandle() + { + if (false === $curl = curl_init()) { + throw new ClientException('Unable to create a new cURL handle'); + } + + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HEADER, true); + + return $curl; + } + + /** + * Populates a response object. + * + * @param resource $curl A cURL resource + * @param string $raw The raw response string + * @param MessageInterface $response The response object + */ + protected static function populateResponse($curl, $raw, MessageInterface $response) + { + // fixes bug https://sourceforge.net/p/curl/bugs/1204/ + $version = curl_version(); + if (version_compare($version['version'], '7.30.0', '<')) { + $pos = strlen($raw) - curl_getinfo($curl, CURLINFO_SIZE_DOWNLOAD); + } else { + $pos = curl_getinfo($curl, CURLINFO_HEADER_SIZE); + } + + $response->setHeaders(static::getLastHeaders(rtrim(substr($raw, 0, $pos)))); + $response->setContent(strlen($raw) > $pos ? substr($raw, $pos) : ''); + } + + /** + * Sets options on a cURL resource based on a request. + */ + private static function setOptionsFromRequest($curl, RequestInterface $request) + { + $options = array( + CURLOPT_HTTP_VERSION => $request->getProtocolVersion() == 1.0 ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => $request->getMethod(), + CURLOPT_URL => $request->getHost().$request->getResource(), + CURLOPT_HTTPHEADER => $request->getHeaders(), + ); + + switch ($request->getMethod()) { + case RequestInterface::METHOD_HEAD: + $options[CURLOPT_NOBODY] = true; + break; + + case RequestInterface::METHOD_GET: + $options[CURLOPT_HTTPGET] = true; + break; + + case RequestInterface::METHOD_POST: + case RequestInterface::METHOD_PUT: + case RequestInterface::METHOD_DELETE: + case RequestInterface::METHOD_PATCH: + case RequestInterface::METHOD_OPTIONS: + $options[CURLOPT_POSTFIELDS] = $fields = static::getPostFields($request); + + // remove the content-type header + if (is_array($fields)) { + $options[CURLOPT_HTTPHEADER] = array_filter($options[CURLOPT_HTTPHEADER], function($header) { + return 0 !== stripos($header, 'Content-Type: '); + }); + } + + break; + } + + curl_setopt_array($curl, $options); + } + + /** + * Returns a value for the CURLOPT_POSTFIELDS option. + * + * @return string|array A post fields value + */ + private static function getPostFields(RequestInterface $request) + { + if (!$request instanceof FormRequestInterface) { + return $request->getContent(); + } + + $fields = $request->getFields(); + $multipart = false; + + foreach ($fields as $name => $value) { + if (!$value instanceof FormUploadInterface) { + continue; + } + + if (!$file = $value->getFile()) { + return $request->getContent(); + } + + $multipart = true; + + if (version_compare(PHP_VERSION, '5.5', '>=')) { + $curlFile = new \CURLFile($file); + if ($contentType = $value->getContentType()) { + $curlFile->setMimeType($contentType); + } + + if (basename($file) != $value->getFilename()) { + $curlFile->setPostFilename($value->getFilename()); + } + + $fields[$name] = $curlFile; + } else { + // replace value with upload string + $fields[$name] = '@'.$file; + + if ($contentType = $value->getContentType()) { + $fields[$name] .= ';type='.$contentType; + } + if (basename($file) != $value->getFilename()) { + $fields[$name] .= ';filename='.$value->getFilename(); + } + } + } + + return $multipart ? $fields : http_build_query($fields, '', '&'); + } + + /** + * A helper for getting the last set of headers. + * + * @param string $raw A string of many header chunks + * + * @return array An array of header lines + */ + private static function getLastHeaders($raw) + { + $headers = array(); + foreach (preg_split('/(\\r?\\n)/', $raw) as $header) { + if ($header) { + $headers[] = $header; + } else { + $headers = array(); + } + } + + return $headers; + } + + /** + * Stashes a cURL option to be set on send, when the resource is created. + * + * If the supplied value it set to null the option will be removed. + * + * @param integer $option The option + * @param mixed $value The value + * + * @see curl_setopt() + */ + public function setOption($option, $value) + { + if (null === $value) { + unset($this->options[$option]); + } else { + $this->options[$option] = $value; + } + } + + /** + * Prepares a cURL resource to send a request. + */ + protected function prepare($curl, RequestInterface $request, array $options = array()) + { + static::setOptionsFromRequest($curl, $request); + + // apply settings from client + if ($this->getTimeout() < 1) { + curl_setopt($curl, CURLOPT_TIMEOUT_MS, $this->getTimeout() * 1000); + } else { + curl_setopt($curl, CURLOPT_TIMEOUT, $this->getTimeout()); + } + + if ($this->proxy) { + curl_setopt($curl, CURLOPT_PROXY, $this->proxy); + } + + $canFollow = !ini_get('safe_mode') && !ini_get('open_basedir'); + + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $canFollow && $this->getMaxRedirects() > 0); + curl_setopt($curl, CURLOPT_MAXREDIRS, $canFollow ? $this->getMaxRedirects() : 0); + curl_setopt($curl, CURLOPT_FAILONERROR, !$this->getIgnoreErrors()); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->getVerifyPeer()); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->getVerifyHost()); + + // apply additional options + curl_setopt_array($curl, $options + $this->options); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractStream.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractStream.php new file mode 100755 index 000000000..095b8c54e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractStream.php @@ -0,0 +1,45 @@ + array( + // values from the request + 'method' => $request->getMethod(), + 'header' => implode("\r\n", $request->getHeaders()), + 'content' => $request->getContent(), + 'protocol_version' => $request->getProtocolVersion(), + + // values from the current client + 'ignore_errors' => $this->getIgnoreErrors(), + 'follow_location' => $this->getMaxRedirects() > 0, + 'max_redirects' => $this->getMaxRedirects() + 1, + 'timeout' => $this->getTimeout(), + ), + 'ssl' => array( + 'verify_peer' => $this->getVerifyPeer(), + 'verify_host' => $this->getVerifyHost(), + ), + ); + + if ($this->proxy) { + $options['http']['proxy'] = $this->proxy; + $options['http']['request_fulluri'] = true; + } + + return $options; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/BatchClientInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/BatchClientInterface.php new file mode 100755 index 000000000..4d395dccf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/BatchClientInterface.php @@ -0,0 +1,27 @@ +lastCurl)) { + curl_close($this->lastCurl); + } + + $this->lastCurl = static::createCurlHandle(); + $this->prepare($this->lastCurl, $request, $options); + + $data = curl_exec($this->lastCurl); + + if (false === $data) { + $errorMsg = curl_error($this->lastCurl); + $errorNo = curl_errno($this->lastCurl); + + $e = new RequestException($errorMsg, $errorNo); + $e->setRequest($request); + + throw $e; + } + + static::populateResponse($this->lastCurl, $data, $response); + } + + /** + * Introspects the last cURL request. + * + * @see curl_getinfo() + * + * @throws LogicException If there is no cURL resource + */ + public function getInfo($opt = 0) + { + if (!is_resource($this->lastCurl)) { + throw new LogicException('There is no cURL resource'); + } + + return 0 === $opt ? curl_getinfo($this->lastCurl) : curl_getinfo($this->lastCurl, $opt); + } + + public function __destruct() + { + if (is_resource($this->lastCurl)) { + curl_close($this->lastCurl); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/FileGetContents.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/FileGetContents.php new file mode 100755 index 000000000..f344764d3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/FileGetContents.php @@ -0,0 +1,91 @@ +setCookieJar($cookieJar); + } + } + + /** + * @param CookieJar $cookieJar + */ + public function setCookieJar(CookieJar $cookieJar) + { + $this->cookieJar = $cookieJar; + } + + /** + * @return CookieJar + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * @see ClientInterface + * + * @throws ClientException If file_get_contents() fires an error + */ + public function send(RequestInterface $request, MessageInterface $response) + { + if ($cookieJar = $this->getCookieJar()) { + $cookieJar->clearExpiredCookies(); + $cookieJar->addCookieHeaders($request); + } + + $context = stream_context_create($this->getStreamContextArray($request)); + $url = $request->getHost().$request->getResource(); + + $level = error_reporting(0); + $content = file_get_contents($url, 0, $context); + error_reporting($level); + if (false === $content) { + $error = error_get_last(); + $e = new RequestException($error['message']); + $e->setRequest($request); + + throw $e; + } + + $response->setHeaders($this->filterHeaders((array) $http_response_header)); + $response->setContent($content); + + if ($cookieJar) { + $cookieJar->processSetCookieHeaders($request, $response); + } + } + + private function filterHeaders(array $headers) + { + $filtered = array(); + foreach ($headers as $header) { + if (0 === stripos($header, 'http/')) { + $filtered = array(); + } + + $filtered[] = $header; + } + + return $filtered; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/MultiCurl.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/MultiCurl.php new file mode 100755 index 000000000..ba2ea6996 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Client/MultiCurl.php @@ -0,0 +1,114 @@ +queue[] = array($request, $response, $options); + } + + public function count() + { + return count($this->queue); + } + + public function flush() + { + while ($this->queue) { + $this->proceed(); + } + } + + public function proceed() + { + if (!$this->queue) { + return; + } + + if (!$this->curlm && false === $this->curlm = curl_multi_init()) { + throw new ClientException('Unable to create a new cURL multi handle'); + } + + foreach (array_keys($this->queue) as $i) { + if (3 == count($this->queue[$i])) { + // prepare curl handle + list($request, , $options) = $this->queue[$i]; + $curl = static::createCurlHandle(); + + // remove custom option + unset($options['callback']); + + $this->prepare($curl, $request, $options); + $this->queue[$i][] = $curl; + curl_multi_add_handle($this->curlm, $curl); + } + } + + // process outstanding perform + $active = null; + do { + $mrc = curl_multi_exec($this->curlm, $active); + } while ($active && CURLM_CALL_MULTI_PERFORM == $mrc); + + // handle any completed requests + while ($done = curl_multi_info_read($this->curlm)) { + foreach (array_keys($this->queue) as $i) { + list($request, $response, $options, $curl) = $this->queue[$i]; + + if ($curl !== $done['handle']) { + continue; + } + + // populate the response object + if (CURLE_OK === $done['result']) { + static::populateResponse($curl, curl_multi_getcontent($curl), $response); + } + + // remove from queue + curl_multi_remove_handle($this->curlm, $curl); + curl_close($curl); + unset($this->queue[$i]); + + // callback + if (isset($options['callback'])) { + call_user_func($options['callback'], $this, $request, $response, $options, $done['result']); + } + } + } + + // cleanup + if (!$this->queue) { + curl_multi_close($this->curlm); + $this->curlm = null; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ClientException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ClientException.php new file mode 100755 index 000000000..187157ee8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ClientException.php @@ -0,0 +1,10 @@ +request; + } + + /** + * @param RequestInterface $request + */ + public function setRequest(RequestInterface $request) + { + $this->request = $request; + } + +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RuntimeException.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RuntimeException.php new file mode 100755 index 000000000..471f54bc3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RuntimeException.php @@ -0,0 +1,7 @@ +username = $username; + $this->password = $password; + } + + public function preSend(RequestInterface $request) + { + $request->addHeader('Authorization: Basic '.base64_encode($this->username.':'.$this->password)); + } + + public function postSend(RequestInterface $request, MessageInterface $response) + { + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CallbackListener.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CallbackListener.php new file mode 100755 index 000000000..77f7fa831 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CallbackListener.php @@ -0,0 +1,49 @@ +callable = $callable; + } + + public function preSend(RequestInterface $request) + { + call_user_func($this->callable, $request); + } + + public function postSend(RequestInterface $request, MessageInterface $response) + { + call_user_func($this->callable, $request, $response); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Entry.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Entry.php new file mode 100755 index 000000000..d7d21066a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Entry.php @@ -0,0 +1,42 @@ +request = $request; + $this->response = $response; + $this->duration = $duration; + } + + public function getRequest() + { + return $this->request; + } + + public function getResponse() + { + return $this->response; + } + + public function getDuration() + { + return $this->duration; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Journal.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Journal.php new file mode 100755 index 000000000..3e6e700d2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Journal.php @@ -0,0 +1,76 @@ +addEntry(new Entry($request, $response, $duration)); + } + + public function addEntry(Entry $entry) + { + array_push($this->entries, $entry); + $this->entries = array_slice($this->entries, $this->getLimit() * -1); + end($this->entries); + } + + public function getEntries() + { + return $this->entries; + } + + public function getLast() + { + return end($this->entries); + } + + public function getLastRequest() + { + return $this->getLast()->getRequest(); + } + + public function getLastResponse() + { + return $this->getLast()->getResponse(); + } + + public function clear() + { + $this->entries = array(); + } + + public function count() + { + return count($this->entries); + } + + public function setLimit($limit) + { + $this->limit = $limit; + } + + public function getLimit() + { + return $this->limit; + } + + public function getIterator() + { + return new \ArrayIterator(array_reverse($this->entries)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/HistoryListener.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/HistoryListener.php new file mode 100755 index 000000000..cd1e6276a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/HistoryListener.php @@ -0,0 +1,33 @@ +journal = $journal; + } + + public function getJournal() + { + return $this->journal; + } + + public function preSend(RequestInterface $request) + { + $this->startTime = microtime(true); + } + + public function postSend(RequestInterface $request, MessageInterface $response) + { + $this->journal->record($request, $response, microtime(true) - $this->startTime); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerChain.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerChain.php new file mode 100755 index 000000000..85024a633 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerChain.php @@ -0,0 +1,40 @@ +listeners = $listeners; + } + + public function addListener(ListenerInterface $listener) + { + $this->listeners[] = $listener; + } + + public function getListeners() + { + return $this->listeners; + } + + public function preSend(RequestInterface $request) + { + foreach ($this->listeners as $listener) { + $listener->preSend($request); + } + } + + public function postSend(RequestInterface $request, MessageInterface $response) + { + foreach ($this->listeners as $listener) { + $listener->postSend($request, $response); + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerInterface.php new file mode 100755 index 000000000..6b0815c1c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerInterface.php @@ -0,0 +1,12 @@ +logger = $logger; + $this->prefix = $prefix; + } + + public function preSend(RequestInterface $request) + { + $this->startTime = microtime(true); + } + + public function postSend(RequestInterface $request, MessageInterface $response) + { + $seconds = microtime(true) - $this->startTime; + + call_user_func($this->logger, sprintf('%sSent "%s %s%s" in %dms', $this->prefix, $request->getMethod(), $request->getHost(), $request->getResource(), round($seconds * 1000))); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/AbstractMessage.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/AbstractMessage.php new file mode 100755 index 000000000..b4f292c99 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/AbstractMessage.php @@ -0,0 +1,154 @@ +getHeaders() as $header) { + if (0 === stripos($header, $needle)) { + $values[] = trim(substr($header, strlen($needle))); + } + } + + if (false === $glue) { + return $values; + } else { + return count($values) ? implode($glue, $values) : null; + } + } + + /** + * Returns a header's attributes. + * + * @param string $name A header name + * + * @return array An associative array of attributes + */ + public function getHeaderAttributes($name) + { + $attributes = array(); + foreach ($this->getHeader($name, false) as $header) { + if (false !== strpos($header, ';')) { + // remove header value + list(, $header) = explode(';', $header, 2); + + // loop through attribute key=value pairs + foreach (array_map('trim', explode(';', trim($header))) as $pair) { + list($key, $value) = explode('=', $pair); + $attributes[$key] = $value; + } + } + } + + return $attributes; + } + + /** + * Returns the value of a particular header attribute. + * + * @param string $header A header name + * @param string $attribute An attribute name + * + * @return string|null The value of the attribute or null if it isn't set + */ + public function getHeaderAttribute($header, $attribute) + { + $attributes = $this->getHeaderAttributes($header); + + if (isset($attributes[$attribute])) { + return $attributes[$attribute]; + } + } + + /** + * Returns the current message as a DOMDocument. + * + * @return \DOMDocument + */ + public function toDomDocument() + { + $revert = libxml_use_internal_errors(true); + + $document = new \DOMDocument('1.0', $this->getHeaderAttribute('Content-Type', 'charset') ?: 'UTF-8'); + if (0 === strpos($this->getHeader('Content-Type'), 'text/xml')) { + $document->loadXML($this->getContent()); + } else { + $document->loadHTML($this->getContent()); + } + + libxml_use_internal_errors($revert); + + return $document; + } + + public function setHeaders(array $headers) + { + $this->headers = $this->flattenHeaders($headers); + } + + public function addHeader($header) + { + $this->headers[] = $header; + } + + public function addHeaders(array $headers) + { + $this->headers = array_merge($this->headers, $this->flattenHeaders($headers)); + } + + public function getHeaders() + { + return $this->headers; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getContent() + { + return $this->content; + } + + public function __toString() + { + $string = implode("\r\n", $this->getHeaders())."\r\n"; + + if ($content = $this->getContent()) { + $string .= "\r\n$content\r\n"; + } + + return $string; + } + + protected function flattenHeaders(array $headers) + { + $flattened = array(); + foreach ($headers as $key => $header) { + if (is_int($key)) { + $flattened[] = $header; + } else { + $flattened[] = $key.': '.$header; + } + } + + return $flattened; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Factory/Factory.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Factory/Factory.php new file mode 100755 index 000000000..d601dad33 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Factory/Factory.php @@ -0,0 +1,26 @@ +setField('user[name]', 'Kris Wallsmith'); + * $request->setField('user[image]', new FormUpload('/path/to/image.jpg')); + * + * @author Marc Weistroff + * @author Kris Wallsmith + */ +class FormRequest extends Request implements FormRequestInterface +{ + private $fields = array(); + private $boundary; + + /** + * Constructor. + * + * Defaults to POST rather than GET. + */ + public function __construct($method = self::METHOD_POST, $resource = '/', $host = null) + { + parent::__construct($method, $resource, $host); + } + + /** + * Sets the value of a form field. + * + * If the value is an array it will be flattened and one field value will + * be added for each leaf. + */ + public function setField($name, $value) + { + if (is_array($value)) { + $this->addFields(array($name => $value)); + + return; + } + + if ('[]' == substr($name, -2)) { + $this->fields[substr($name, 0, -2)][] = $value; + } else { + $this->fields[$name] = $value; + } + } + + public function addFields(array $fields) + { + foreach ($this->flattenArray($fields) as $name => $value) { + $this->setField($name, $value); + } + } + + public function setFields(array $fields) + { + $this->fields = array(); + $this->addFields($fields); + } + + public function getFields() + { + return $this->fields; + } + + public function getResource() + { + $resource = parent::getResource(); + + if (!$this->isSafe() || !$this->fields) { + return $resource; + } + + // append the query string + $resource .= false === strpos($resource, '?') ? '?' : '&'; + $resource .= http_build_query($this->fields); + + return $resource; + } + + public function setContent($content) + { + throw new \BadMethodCallException('It is not permitted to set the content.'); + } + + public function getHeaders() + { + $headers = parent::getHeaders(); + + if ($this->isSafe()) { + return $headers; + } + + if ($this->isMultipart()) { + $headers[] = 'Content-Type: multipart/form-data; boundary='.$this->getBoundary(); + } else { + $headers[] = 'Content-Type: application/x-www-form-urlencoded'; + } + + return $headers; + } + + public function getContent() + { + if ($this->isSafe()) { + return; + } + + if (!$this->isMultipart()) { + return http_build_query($this->fields, '', '&'); + } + + $content = ''; + + foreach ($this->fields as $name => $values) { + $content .= '--'.$this->getBoundary()."\r\n"; + if ($values instanceof FormUploadInterface) { + if (!$values->getFilename()) { + throw new LogicException(sprintf('Form upload at "%s" does not include a filename.', $name)); + } + + $values->setName($name); + $content .= (string) $values; + } else { + foreach (is_array($values) ? $values : array($values) as $value) { + $content .= "Content-Disposition: form-data; name=\"$name\"\r\n"; + $content .= "\r\n"; + $content .= $value."\r\n"; + } + } + } + + $content .= '--'.$this->getBoundary().'--'; + + return $content; + } + + // private + + private function flattenArray(array $values, $prefix = '', $format = '%s') + { + $flat = array(); + + foreach ($values as $name => $value) { + $flatName = $prefix.sprintf($format, $name); + + if (is_array($value)) { + $flat += $this->flattenArray($value, $flatName, '[%s]'); + } else { + $flat[$flatName] = $value; + } + } + + return $flat; + } + + private function isSafe() + { + return in_array($this->getMethod(), array(self::METHOD_GET, self::METHOD_HEAD)); + } + + private function isMultipart() + { + foreach ($this->fields as $name => $value) { + if (is_object($value) && $value instanceof FormUploadInterface) { + return true; + } + } + + return false; + } + + private function getBoundary() + { + if (!$this->boundary) { + $this->boundary = sha1(rand(11111, 99999).time().uniqid()); + } + + return $this->boundary; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormRequestInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormRequestInterface.php new file mode 100755 index 000000000..ed2bd4900 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormRequestInterface.php @@ -0,0 +1,27 @@ + + */ +interface FormRequestInterface extends RequestInterface +{ + /** + * Returns an array of field names and values. + * + * @return array A array of names and values + */ + public function getFields(); + + /** + * Sets the form fields for the current request. + * + * @param array $fields An array of field names and values + */ + public function setFields(array $fields); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUpload.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUpload.php new file mode 100755 index 000000000..22b932b9d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUpload.php @@ -0,0 +1,118 @@ +loadContent($file); + } + + $this->contentType = $contentType; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getFilename() + { + if ($this->filename) { + return $this->filename; + } elseif ($this->file) { + return basename($this->file); + } + } + + public function setFilename($filename) + { + $this->filename = $filename; + } + + public function getContentType() + { + return $this->contentType ?: $this->detectContentType() ?: 'application/octet-stream'; + } + + public function setContentType($contentType) + { + $this->contentType = $contentType; + } + + /** + * Prepends Content-Disposition and Content-Type headers. + */ + public function getHeaders() + { + $headers = array('Content-Disposition: form-data'); + + if ($name = $this->getName()) { + $headers[0] .= sprintf('; name="%s"', $name); + } + + if ($filename = $this->getFilename()) { + $headers[0] .= sprintf('; filename="%s"', $filename); + } + + if ($contentType = $this->getContentType()) { + $headers[] = 'Content-Type: '.$contentType; + } + + return array_merge($headers, parent::getHeaders()); + } + + /** + * Loads the content from a file. + */ + public function loadContent($file) + { + $this->file = $file; + + parent::setContent(null); + } + + public function setContent($content) + { + parent::setContent($content); + + $this->file = null; + } + + public function getFile() + { + return $this->file; + } + + public function getContent() + { + return $this->file ? file_get_contents($this->file) : parent::getContent(); + } + + // private + + private function detectContentType() + { + if (!class_exists('finfo', false)) { + return false; + } + + $finfo = new \finfo(FILEINFO_MIME_TYPE); + + return $this->file ? $finfo->file($this->file) : $finfo->buffer(parent::getContent()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUploadInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUploadInterface.php new file mode 100755 index 000000000..4d6066cc4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Form/FormUploadInterface.php @@ -0,0 +1,13 @@ + + */ +interface MessageInterface +{ + /** + * Returns a header value. + * + * @param string $name A header name + * @param string|boolean $glue Glue for implode, or false to return an array + * + * @return string|array|null The header value(s) + */ + public function getHeader($name, $glue = "\r\n"); + + /** + * Returns an array of header lines. + * + * @return array An array of header lines (integer indexes, e.g. ["Header: value"]) + */ + public function getHeaders(); + + /** + * Sets all headers on the current message. + * + * Headers can be complete ["Header: value"] pairs or an associative array ["Header" => "value"] + * + * @param array $headers An array of header lines + */ + public function setHeaders(array $headers); + + /** + * Adds a header to this message. + * + * @param string $header A header line + */ + public function addHeader($header); + + /** + * Adds a set of headers to this message. + * + * Headers can be complete ["Header: value"] pairs or an associative array ["Header" => "value"] + * + * @param array $headers Headers + */ + public function addHeaders(array $headers); + + /** + * Returns the content of the message. + * + * @return string The message content + */ + public function getContent(); + + /** + * Sets the content of the message. + * + * @param string $content The message content + */ + public function setContent($content); + + /** + * Returns the message document. + * + * @return string The message + */ + public function __toString(); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Request.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Request.php new file mode 100755 index 000000000..2472b5e3f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Request.php @@ -0,0 +1,174 @@ +method = strtoupper($method); + $this->resource = $resource; + $this->host = $host; + } + + public function setHeaders(array $headers) + { + parent::setHeaders(array()); + + foreach ($this->flattenHeaders($headers) as $header) { + $this->addHeader($header); + } + } + + public function addHeader($header) + { + if (0 === stripos(substr($header, -8), 'HTTP/1.') && 3 == count($parts = explode(' ', $header))) { + list($method, $resource, $protocolVersion) = $parts; + + $this->setMethod($method); + $this->setResource($resource); + $this->setProtocolVersion((float) substr($protocolVersion, 5)); + } else { + parent::addHeader($header); + } + } + + public function setMethod($method) + { + $this->method = strtoupper($method); + } + + public function getMethod() + { + return $this->method; + } + + public function setResource($resource) + { + $this->resource = $resource; + } + + public function getResource() + { + return $this->resource; + } + + public function setHost($host) + { + $this->host = $host; + } + + public function getHost() + { + return $this->host; + } + + public function setProtocolVersion($protocolVersion) + { + $this->protocolVersion = $protocolVersion; + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + /** + * A convenience method for getting the full URL of the current request. + * + * @return string + */ + public function getUrl() + { + return $this->getHost().$this->getResource(); + } + + /** + * A convenience method for populating the current request from a URL. + * + * @param Url|string $url An URL + */ + public function fromUrl($url) + { + if (!$url instanceof Url) { + $url = new Url($url); + } + + $url->applyToRequest($this); + } + + /** + * Returns true if the current request is secure. + * + * @return boolean + */ + public function isSecure() + { + return 'https' == parse_url($this->getHost(), PHP_URL_SCHEME); + } + + /** + * Merges cookie headers on the way out. + */ + public function getHeaders() + { + return $this->mergeCookieHeaders(parent::getHeaders()); + } + + /** + * Returns a string representation of the current request. + * + * @return string + */ + public function __toString() + { + $string = sprintf("%s %s HTTP/%.1f\r\n", $this->getMethod(), $this->getResource(), $this->getProtocolVersion()); + + if ($host = $this->getHost()) { + $string .= 'Host: '.$host."\r\n"; + } + + if ($parent = trim(parent::__toString())) { + $string .= $parent."\r\n"; + } + + return $string; + } + + // private + + private function mergeCookieHeaders(array $headers) + { + $cookieHeader = null; + $needle = 'Cookie:'; + + foreach ($headers as $i => $header) { + if (0 !== stripos($header, $needle)) { + continue; + } + + if (null === $cookieHeader) { + $cookieHeader = $i; + } else { + $headers[$cookieHeader] .= '; '.trim(substr($header, strlen($needle))); + unset($headers[$i]); + } + } + + return array_values($headers); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/RequestInterface.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/RequestInterface.php new file mode 100755 index 000000000..883c8e4d7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/RequestInterface.php @@ -0,0 +1,75 @@ + + */ +interface RequestInterface extends MessageInterface +{ + const METHOD_OPTIONS = 'OPTIONS'; + const METHOD_GET = 'GET'; + const METHOD_HEAD = 'HEAD'; + const METHOD_POST = 'POST'; + const METHOD_PUT = 'PUT'; + const METHOD_DELETE = 'DELETE'; + const METHOD_PATCH = 'PATCH'; + + /** + * Returns the HTTP method of the current request. + * + * @return string An HTTP method + */ + public function getMethod(); + + /** + * Sets the HTTP method of the current request. + * + * @param string $method The request method + */ + public function setMethod($method); + + /** + * Returns the resource portion of the request line. + * + * @return string The resource requested + */ + public function getResource(); + + /** + * Sets the resource for the current request. + * + * @param string $resource The resource being requested + */ + public function setResource($resource); + + /** + * Returns the protocol version of the current request. + * + * @return float The protocol version + */ + public function getProtocolVersion(); + + /** + * Returns the value of the host header. + * + * @return string|null The host + */ + public function getHost(); + + /** + * Sets the host for the current request. + * + * @param string $host The host + */ + public function setHost($host); + + /** + * Checks if the current request is secure. + * + * @return Boolean True if the request is secure + */ + public function isSecure(); +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Response.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Response.php new file mode 100755 index 000000000..453debda3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Message/Response.php @@ -0,0 +1,193 @@ +protocolVersion) { + $this->parseStatusLine(); + } + + return $this->protocolVersion ?: null; + } + + /** + * Returns the status code of the current response. + * + * @return integer + */ + public function getStatusCode() + { + if (null === $this->statusCode) { + $this->parseStatusLine(); + } + + return $this->statusCode ?: null; + } + + /** + * Returns the reason phrase for the current response. + * + * @return string + */ + public function getReasonPhrase() + { + if (null === $this->reasonPhrase) { + $this->parseStatusLine(); + } + + return $this->reasonPhrase ?: null; + } + + public function setHeaders(array $headers) + { + parent::setHeaders($headers); + + $this->resetStatusLine(); + } + + public function addHeader($header) + { + parent::addHeader($header); + + $this->resetStatusLine(); + } + + public function addHeaders(array $headers) + { + parent::addHeaders($headers); + + $this->resetStatusLine(); + } + + /** + * Is response invalid? + * + * @return Boolean + */ + public function isInvalid() + { + return $this->getStatusCode() < 100 || $this->getStatusCode() >= 600; + } + + /** + * Is response informative? + * + * @return Boolean + */ + public function isInformational() + { + return $this->getStatusCode() >= 100 && $this->getStatusCode() < 200; + } + + /** + * Is response successful? + * + * @return Boolean + */ + public function isSuccessful() + { + return $this->getStatusCode() >= 200 && $this->getStatusCode() < 300; + } + + /** + * Is the response a redirect? + * + * @return Boolean + */ + public function isRedirection() + { + return $this->getStatusCode() >= 300 && $this->getStatusCode() < 400; + } + + /** + * Is there a client error? + * + * @return Boolean + */ + public function isClientError() + { + return $this->getStatusCode() >= 400 && $this->getStatusCode() < 500; + } + + /** + * Was there a server side error? + * + * @return Boolean + */ + public function isServerError() + { + return $this->getStatusCode() >= 500 && $this->getStatusCode() < 600; + } + + /** + * Is the response OK? + * + * @return Boolean + */ + public function isOk() + { + return 200 === $this->getStatusCode(); + } + + /** + * Is the reponse forbidden? + * + * @return Boolean + */ + public function isForbidden() + { + return 403 === $this->getStatusCode(); + } + + /** + * Is the response a not found error? + * + * @return Boolean + */ + public function isNotFound() + { + return 404 === $this->getStatusCode(); + } + + /** + * Is the response empty? + * + * @return Boolean + */ + public function isEmpty() + { + return in_array($this->getStatusCode(), array(201, 204, 304)); + } + + // private + + private function parseStatusLine() + { + $headers = $this->getHeaders(); + + if (isset($headers[0]) && 3 == count($parts = explode(' ', $headers[0], 3))) { + $this->protocolVersion = (float) substr($parts[0], 5); + $this->statusCode = (integer) $parts[1]; + $this->reasonPhrase = $parts[2]; + } else { + $this->protocolVersion = $this->statusCode = $this->reasonPhrase = false; + } + } + + private function resetStatusLine() + { + $this->protocolVersion = $this->statusCode = $this->reasonPhrase = null; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Cookie.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Cookie.php new file mode 100755 index 000000000..21d7c12a7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Cookie.php @@ -0,0 +1,216 @@ +createdAt = time(); + } + + /** + * Returns true if the current cookie matches the supplied request. + * + * @return boolean + */ + public function matchesRequest(RequestInterface $request) + { + // domain + if (!$this->matchesDomain(parse_url($request->getHost(), PHP_URL_HOST))) { + return false; + } + + // path + if (!$this->matchesPath($request->getResource())) { + return false; + } + + // secure + if ($this->hasAttribute(static::ATTR_SECURE) && !$request->isSecure()) { + return false; + } + + return true; + } + + /** + * Returns true of the current cookie has expired. + * + * Checks the max-age and expires attributes. + * + * @return boolean Whether the current cookie has expired + */ + public function isExpired() + { + $maxAge = $this->getAttribute(static::ATTR_MAX_AGE); + if ($maxAge && time() - $this->getCreatedAt() > $maxAge) { + return true; + } + + $expires = $this->getAttribute(static::ATTR_EXPIRES); + if ($expires && strtotime($expires) < time()) { + return true; + } + + return false; + } + + /** + * Returns true if the current cookie matches the supplied domain. + * + * @param string $domain A domain hostname + * + * @return boolean + */ + public function matchesDomain($domain) + { + $cookieDomain = $this->getAttribute(static::ATTR_DOMAIN); + + if (0 === strpos($cookieDomain, '.')) { + $pattern = '/\b'.preg_quote(substr($cookieDomain, 1), '/').'$/i'; + + return (boolean) preg_match($pattern, $domain); + } else { + return 0 == strcasecmp($cookieDomain, $domain); + } + } + + /** + * Returns true if the current cookie matches the supplied path. + * + * @param string $path A path + * + * @return boolean + */ + public function matchesPath($path) + { + $needle = $this->getAttribute(static::ATTR_PATH); + + return null === $needle || 0 === strpos($path, $needle); + } + + /** + * Populates the current cookie with data from the supplied Set-Cookie header. + * + * @param string $header A Set-Cookie header + * @param string $issuingDomain The domain that issued the header + */ + public function fromSetCookieHeader($header, $issuingDomain) + { + list($this->name, $header) = explode('=', $header, 2); + if (false === strpos($header, ';')) { + $this->value = $header; + $header = null; + } else { + list($this->value, $header) = explode(';', $header, 2); + } + + $this->clearAttributes(); + foreach (array_map('trim', explode(';', trim($header))) as $pair) { + if (false === strpos($pair, '=')) { + $name = $pair; + $value = null; + } else { + list($name, $value) = explode('=', $pair); + } + + $this->setAttribute($name, $value); + } + + if (!$this->getAttribute(static::ATTR_DOMAIN)) { + $this->setAttribute(static::ATTR_DOMAIN, $issuingDomain); + } + } + + /** + * Formats a Cookie header for the current cookie. + * + * @return string An HTTP request Cookie header + */ + public function toCookieHeader() + { + return 'Cookie: '.$this->getName().'='.$this->getValue(); + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setValue($value) + { + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } + + public function setAttributes(array $attributes) + { + // attributes are case insensitive + $this->attributes = array_change_key_case($attributes); + } + + public function setAttribute($name, $value) + { + $this->attributes[strtolower($name)] = $value; + } + + public function getAttributes() + { + return $this->attributes; + } + + public function getAttribute($name) + { + $name = strtolower($name); + + if (isset($this->attributes[$name])) { + return $this->attributes[$name]; + } + } + + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + public function clearAttributes() + { + $this->setAttributes(array()); + } + + public function setCreatedAt($createdAt) + { + $this->createdAt = $createdAt; + } + + public function getCreatedAt() + { + return $this->createdAt; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/CookieJar.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/CookieJar.php new file mode 100755 index 000000000..4b6889b19 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/CookieJar.php @@ -0,0 +1,79 @@ +cookies = array(); + foreach ($cookies as $cookie) { + $this->addCookie($cookie); + } + } + + public function getCookies() + { + return $this->cookies; + } + + /** + * Adds a cookie to the current cookie jar. + * + * @param Cookie $cookie A cookie object + */ + public function addCookie(Cookie $cookie) + { + $this->cookies[] = $cookie; + } + + /** + * Adds Cookie headers to the supplied request. + * + * @param RequestInterface $request A request object + */ + public function addCookieHeaders(RequestInterface $request) + { + foreach ($this->cookies as $cookie) { + if ($cookie->matchesRequest($request)) { + $request->addHeader($cookie->toCookieHeader()); + } + } + } + + /** + * Processes Set-Cookie headers from a request/response pair. + * + * @param RequestInterface $request A request object + * @param MessageInterface $response A response object + */ + public function processSetCookieHeaders(RequestInterface $request, MessageInterface $response) + { + foreach ($response->getHeader('Set-Cookie', false) as $header) { + $cookie = new Cookie(); + $cookie->fromSetCookieHeader($header, parse_url($request->getHost(), PHP_URL_HOST)); + + $this->addCookie($cookie); + } + } + + /** + * Removes expired cookies. + */ + public function clearExpiredCookies() + { + foreach ($this->cookies as $i => $cookie) { + if ($cookie->isExpired()) { + unset($this->cookies[$i]); + } + } + + // reset array keys + $this->cookies = array_values($this->cookies); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Url.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Url.php new file mode 100755 index 000000000..e9be3c28b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/lib/Buzz/Util/Url.php @@ -0,0 +1,190 @@ + 80, + 'https' => 443, + ); + + private $url; + private $components; + + /** + * Constructor. + * + * @param string $url The URL + * + * @throws InvalidArgumentException If the URL is invalid + */ + public function __construct($url) + { + $components = parse_url($url); + + if (false === $components) { + throw new InvalidArgumentException(sprintf('The URL "%s" is invalid.', $url)); + } + + // support scheme-less URLs + if (!isset($components['host']) && isset($components['path'])) { + $pos = strpos($components['path'], '/'); + if (false === $pos) { + $components['host'] = $components['path']; + unset($components['path']); + } elseif (0 !== $pos) { + list($host, $path) = explode('/', $components['path'], 2); + $components['host'] = $host; + $components['path'] = '/'.$path; + } + } + + // default port + if (isset($components['scheme']) && !isset($components['port']) && isset(self::$defaultPorts[$components['scheme']])) { + $components['port'] = self::$defaultPorts[$components['scheme']]; + } + + $this->url = $url; + $this->components = $components; + } + + public function getScheme() + { + return $this->parseUrl('scheme'); + } + + public function getHostname() + { + return $this->parseUrl('host'); + } + + public function getPort() + { + return $this->parseUrl('port'); + } + + public function getUser() + { + return $this->parseUrl('user'); + } + + public function getPassword() + { + return $this->parseUrl('pass'); + } + + public function getPath() + { + return $this->parseUrl('path'); + } + + public function getQueryString() + { + return $this->parseUrl('query'); + } + + public function getFragment() + { + return $this->parseUrl('fragment'); + } + + /** + * Returns a host string that combines scheme, hostname and port. + * + * @return string A host value for an HTTP message + */ + public function getHost() + { + if ($hostname = $this->parseUrl('host')) { + $host = $scheme = $this->parseUrl('scheme', 'http'); + $host .= '://'; + $host .= $hostname; + + $port = $this->parseUrl('port'); + if ($port && (!isset(self::$defaultPorts[$scheme]) || self::$defaultPorts[$scheme] != $port)) { + $host .= ':'.$port; + } + + return $host; + } + } + + /** + * Returns a resource string that combines path and query string. + * + * @return string A resource value for an HTTP message + */ + public function getResource() + { + $resource = $this->parseUrl('path', '/'); + + if ($query = $this->parseUrl('query')) { + $resource .= '?'.$query; + } + + return $resource; + } + + /** + * Returns a formatted URL. + */ + public function format($pattern) + { + static $map = array( + 's' => 'getScheme', + 'u' => 'getUser', + 'a' => 'getPassword', + 'h' => 'getHostname', + 'o' => 'getPort', + 'p' => 'getPath', + 'q' => 'getQueryString', + 'f' => 'getFragment', + 'H' => 'getHost', + 'R' => 'getResource', + ); + + $url = ''; + + $parts = str_split($pattern); + while ($part = current($parts)) { + if (isset($map[$part])) { + $method = $map[$part]; + $url .= $this->$method(); + } elseif ('\\' == $part) { + $url .= next($parts); + } elseif (!ctype_alpha($part)) { + $url .= $part; + } else { + throw new InvalidArgumentException(sprintf('The format character "%s" is invalid.', $part)); + } + + next($parts); + } + + return $url; + } + + /** + * Applies the current URL to the supplied request. + */ + public function applyToRequest(RequestInterface $request) + { + $request->setResource($this->getResource()); + $request->setHost($this->getHost()); + } + + private function parseUrl($component = null, $default = null) + { + if (null === $component) { + return $this->components; + } elseif (isset($this->components[$component])) { + return $this->components[$component]; + } else { + return $default; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/phpunit.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/phpunit.xml.dist new file mode 100755 index 000000000..bc0103b53 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./test/Buzz/ + + + + + + + + + + + ./lib/Buzz/ + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/BrowserTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/BrowserTest.php new file mode 100755 index 000000000..ad99a57ec --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/BrowserTest.php @@ -0,0 +1,182 @@ +client = $this->getMock('Buzz\Client\ClientInterface'); + $this->factory = $this->getMock('Buzz\Message\Factory\FactoryInterface'); + + $this->browser = new Browser($this->client, $this->factory); + } + + /** + * @dataProvider provideMethods + */ + public function testBasicMethods($method, $content) + { + $request = $this->getMock('Buzz\Message\RequestInterface'); + $response = $this->getMock('Buzz\Message\MessageInterface'); + $headers = array('X-Foo: bar'); + + $this->factory->expects($this->once()) + ->method('createRequest') + ->with(strtoupper($method)) + ->will($this->returnValue($request)); + $request->expects($this->once()) + ->method('setHost') + ->with('http://google.com'); + $request->expects($this->once()) + ->method('setResource') + ->with('/'); + $request->expects($this->once()) + ->method('addHeaders') + ->with($headers); + $request->expects($this->once()) + ->method('setContent') + ->with($content); + $this->factory->expects($this->once()) + ->method('createResponse') + ->will($this->returnValue($response)); + $this->client->expects($this->once()) + ->method('send') + ->with($request, $response); + + $actual = $this->browser->$method('http://google.com/', $headers, $content); + + $this->assertSame($response, $actual); + } + + public function provideMethods() + { + return array( + array('get', ''), + array('head', ''), + array('post', 'content'), + array('put', 'content'), + array('delete', 'content'), + ); + } + + public function testSubmit() + { + $request = $this->getMock('Buzz\Message\Form\FormRequestInterface'); + $response = $this->getMock('Buzz\Message\MessageInterface'); + $headers = array('X-Foo: bar'); + + $this->factory->expects($this->once()) + ->method('createFormRequest') + ->will($this->returnValue($request)); + $request->expects($this->once()) + ->method('setMethod') + ->with('PUT'); + $request->expects($this->once()) + ->method('setHost') + ->with('http://google.com'); + $request->expects($this->once()) + ->method('setResource') + ->with('/'); + $request->expects($this->once()) + ->method('addHeaders') + ->with($headers); + $request->expects($this->once()) + ->method('setFields') + ->with(array('foo' => 'bar', 'bar' => 'foo')); + $this->factory->expects($this->once()) + ->method('createResponse') + ->will($this->returnValue($response)); + $this->client->expects($this->once()) + ->method('send') + ->with($request, $response); + + $actual = $this->browser->submit('http://google.com', array('foo' => 'bar', 'bar' => 'foo'), 'PUT', $headers); + + $this->assertSame($response, $actual); + } + + public function testListener() + { + $listener = $this->getMock('Buzz\Listener\ListenerInterface'); + $request = $this->getMock('Buzz\Message\RequestInterface'); + $response = $this->getMock('Buzz\Message\MessageInterface'); + + $listener->expects($this->once()) + ->method('preSend') + ->with($request); + $listener->expects($this->once()) + ->method('postSend') + ->with($request, $response); + + $this->browser->setListener($listener); + $this->assertSame($listener, $this->browser->getListener()); + + $this->browser->send($request, $response); + } + + public function testLastMessages() + { + $request = $this->getMock('Buzz\Message\RequestInterface'); + $response = $this->getMock('Buzz\Message\MessageInterface'); + + $this->browser->send($request, $response); + + $this->assertSame($request, $this->browser->getLastRequest()); + $this->assertSame($response, $this->browser->getLastResponse()); + } + + public function testClientMethods() + { + $client = $this->getMock('Buzz\Client\ClientInterface'); + $this->browser->setClient($client); + $this->assertSame($client, $this->browser->getClient()); + } + + public function testFactoryMethods() + { + $factory = $this->getMock('Buzz\Message\Factory\FactoryInterface'); + $this->browser->setMessageFactory($factory); + $this->assertSame($factory, $this->browser->getMessageFactory()); + } + + public function testAddFirstListener() + { + $listener = $this->getMock('Buzz\Listener\ListenerInterface'); + $this->browser->addListener($listener); + $this->assertEquals($listener, $this->browser->getListener()); + } + + public function testAddSecondListener() + { + $listener = $this->getMock('Buzz\Listener\ListenerInterface'); + + $this->browser->addListener($listener); + $this->browser->addListener($listener); + + $listenerChain = $this->browser->getListener(); + + $this->assertInstanceOf('Buzz\Listener\ListenerChain', $listenerChain); + $this->assertEquals(2, count($listenerChain->getListeners())); + } + + public function testAddThirdListener() + { + $listener = $this->getMock('Buzz\Listener\ListenerInterface'); + + $this->browser->addListener($listener); + $this->browser->addListener($listener); + $this->browser->addListener($listener); + + $listenerChain = $this->browser->getListener(); + + $this->assertInstanceOf('Buzz\Listener\ListenerChain', $listenerChain); + $this->assertEquals(3, count($listenerChain->getListeners())); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/AbstractStreamTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/AbstractStreamTest.php new file mode 100755 index 000000000..05ceeab88 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/AbstractStreamTest.php @@ -0,0 +1,58 @@ +addHeader('Content-Type: application/x-www-form-urlencoded'); + $request->addHeader('Content-Length: 15'); + $request->setContent('foo=bar&bar=baz'); + + $client = new StreamClient(); + $client->setMaxRedirects(5); + $client->setIgnoreErrors(false); + $client->setTimeout(10); + $expected = array( + 'http' => array( + 'method' => 'POST', + 'header' => "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: 15", + 'content' => 'foo=bar&bar=baz', + 'protocol_version' => 1.1, + 'ignore_errors' => false, + 'follow_location' => true, + 'max_redirects' => 6, + 'timeout' => 10, + ), + 'ssl' => array( + 'verify_peer' => true, + 'verify_host' => 2, + ), + ); + + $this->assertEquals($expected, $client->getStreamContextArray($request)); + + $client->setVerifyPeer(true); + $expected['ssl']['verify_peer'] = true; + $this->assertEquals($expected, $client->getStreamContextArray($request)); + + $client->setMaxRedirects(0); + $expected['http']['follow_location'] = false; + $expected['http']['max_redirects'] = 1; + $this->assertEquals($expected, $client->getStreamContextArray($request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/ClientTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/ClientTest.php new file mode 100755 index 000000000..5d40fad7b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/ClientTest.php @@ -0,0 +1,36 @@ +setExpectedException('Buzz\\Exception\\ClientException'); + + $request = new Message\Request(); + $request->fromUrl('http://'.$host.':12345'); + + $response = new Message\Response(); + + $client = new $client(); + $client->setTimeout(0.05); + $client->send($request, $response); + } + + public function provideInvalidHosts() + { + return array( + array('invalid_domain', 'Buzz\\Client\\Curl'), + array('invalid_domain.buzz', 'Buzz\\Client\\Curl'), + + array('invalid_domain', 'Buzz\\Client\\FileGetContents'), + array('invalid_domain.buzz', 'Buzz\\Client\\FileGetContents'), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/FunctionalTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/FunctionalTest.php new file mode 100755 index 000000000..408954cf1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Client/FunctionalTest.php @@ -0,0 +1,287 @@ +markTestSkipped('The test server is not configured.'); + } + } + + /** + * @dataProvider provideClientAndMethod + */ + public function testRequestMethods($client, $method) + { + $request = new Request($method); + $request->fromUrl($_SERVER['TEST_SERVER']); + $request->setContent('test'); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertEquals($method, $data['SERVER']['REQUEST_METHOD']); + } + + /** + * @dataProvider provideClient + */ + public function testGetContentType($client) + { + $request = new Request(); + $request->fromUrl($_SERVER['TEST_SERVER']); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertArrayNotHasKey('CONTENT_TYPE', $data['SERVER']); + } + + /** + * @dataProvider provideClient + */ + public function testFormPost($client) + { + $request = new FormRequest(); + $request->fromUrl($_SERVER['TEST_SERVER']); + $request->setField('company[name]', 'Google'); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertStringStartsWith('application/x-www-form-urlencoded', $data['SERVER']['CONTENT_TYPE']); + $this->assertEquals('Google', $data['POST']['company']['name']); + } + + /** + * @dataProvider provideClient + */ + public function testFormGet($client) + { + $request = new FormRequest(FormRequest::METHOD_GET); + $request->fromUrl($_SERVER['TEST_SERVER']); + $request->setField('search[query]', 'cats'); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertArrayNotHasKey('CONTENT_TYPE', $data['SERVER']); + $this->assertEquals('cats', $data['GET']['search']['query']); + } + + /** + * @dataProvider provideClientAndUpload + */ + public function testFileUpload($client, $upload) + { + $request = new FormRequest(); + $request->fromUrl($_SERVER['TEST_SERVER']); + $request->setField('company[name]', 'Google'); + $request->setField('company[logo]', $upload); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertStringStartsWith('multipart/form-data', $data['SERVER']['CONTENT_TYPE']); + $this->assertEquals('Google', $data['POST']['company']['name']); + $this->assertEquals('google.png', $data['FILES']['company']['name']['logo']); + } + + /** + * @dataProvider provideClient + */ + public function testJsonPayload($client) + { + $request = new Request(RequestInterface::METHOD_POST); + $request->fromUrl($_SERVER['TEST_SERVER']); + $request->addHeader('Content-Type: application/json'); + $request->setContent(json_encode(array('foo' => 'bar'))); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertEquals('application/json', $data['SERVER']['CONTENT_TYPE']); + $this->assertEquals('{"foo":"bar"}', $data['INPUT']); + } + + /** + * @dataProvider provideClient + */ + public function testConsecutiveRequests($client) + { + // request 1 + $request = new Request(RequestInterface::METHOD_PUT); + $request->fromUrl($_SERVER['TEST_SERVER']); + $request->addHeader('Content-Type: application/json'); + $request->setContent(json_encode(array('foo' => 'bar'))); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertEquals('PUT', $data['SERVER']['REQUEST_METHOD']); + $this->assertEquals('application/json', $data['SERVER']['CONTENT_TYPE']); + $this->assertEquals('{"foo":"bar"}', $data['INPUT']); + + // request 2 + $request = new Request(RequestInterface::METHOD_GET); + $request->fromUrl($_SERVER['TEST_SERVER']); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + + $this->assertEquals('GET', $data['SERVER']['REQUEST_METHOD']); + $this->assertEmpty($data['INPUT']); + } + + /** + * @dataProvider provideClient + */ + public function testPlus($client) + { + $request = new FormRequest(); + $request->fromUrl($_SERVER['TEST_SERVER']); + $request->setField('math', '1+1=2'); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + parse_str($data['INPUT'], $fields); + + $this->assertEquals(array('math' => '1+1=2'), $fields); + } + + /** + * @dataProvider provideClient + */ + public function testRedirectedResponse($client) + { + $request = new Request(); + $request->fromUrl($_SERVER['TEST_SERVER'].'?redirect_to='.$_SERVER['TEST_SERVER']); + $response = $this->send($client, $request); + + $headers = $response->getHeaders(); + $this->assertContains('200', $headers[0]); + } + + /** + * @dataProvider provideClient + */ + public function testProxy($client) + { + if (!isset($_SERVER['TEST_PROXY'])) { + $this->markTestSkipped('The proxy server is not configured.'); + } + + $client->setProxy($_SERVER['TEST_PROXY']); + + $request = new Request(); + $request->fromUrl($_SERVER['TEST_SERVER']); + $response = $this->send($client, $request); + + $data = json_decode($response->getContent(), true); + $this->assertArrayHasKey('HTTP_VIA', $data['SERVER']); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Protocol pop3 not supported or disabled in libcurl + */ + public function testRedirectedToForbiddenProtocol() + { + $client = new Curl(); + $request = new Request(); + $request->fromUrl($_SERVER['TEST_SERVER'].'?redirect_to=pop3://localhost/'); + $response = $this->send($client, $request); + } + + public function testMultiCurlExecutesRequestsConcurently() + { + $client = new MultiCurl(); + $client->setTimeout(10); + + $calls = array(); + $callback = function($client, $request, $response, $options, $error) use(&$calls) { + $calls[] = func_get_args(); + }; + + for ($i = 3; $i > 0; $i--) { + $request = new Request(); + $request->fromUrl($_SERVER['TEST_SERVER'].'?delay='.$i); + $client->send($request, new Response(), array('callback' => $callback)); + } + + $client->flush(); + $this->assertCount(3, $calls); + } + + public function provideClient() + { + return array( + array(new Curl()), + array(new FileGetContents()), + array(new MultiCurl()), + ); + } + + public function provideClientAndMethod() + { + // HEAD is intentionally omitted + // http://stackoverflow.com/questions/2603104/does-mod-php-honor-head-requests-properly + + $methods = array('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'); + $clients = $this->provideClient(); + + $data = array(); + foreach ($clients as $client) { + foreach ($methods as $method) { + $data[] = array($client[0], $method); + } + } + + return $data; + } + + public function provideClientAndUpload() + { + $stringUpload = new FormUpload(); + $stringUpload->setFilename('google.png'); + $stringUpload->setContent(file_get_contents(__DIR__.'/../Message/Fixtures/google.png')); + + $uploads = array($stringUpload, new FormUpload(__DIR__.'/../Message/Fixtures/google.png')); + $clients = $this->provideClient(); + + $data = array(); + foreach ($clients as $client) { + foreach ($uploads as $upload) { + $data[] = array($client[0], $upload); + } + } + + return $data; + } + + private function send(ClientInterface $client, RequestInterface $request) + { + $response = new Response(); + $client->send($request, $response); + + if ($client instanceof BatchClientInterface) { + $client->flush(); + } + + return $response; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/BasicAuthListenerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/BasicAuthListenerTest.php new file mode 100755 index 000000000..c8de7a8a2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/BasicAuthListenerTest.php @@ -0,0 +1,20 @@ +assertEmpty($request->getHeader('Authorization')); + + $listener = new BasicAuthListener('foo', 'bar'); + $listener->preSend($request); + + $this->assertEquals('Basic '.base64_encode('foo:bar'), $request->getHeader('Authorization')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/CallbackListenerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/CallbackListenerTest.php new file mode 100755 index 000000000..ae623e8e0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/CallbackListenerTest.php @@ -0,0 +1,34 @@ +preSend($request); + $listener->postSend($request, $response); + + $this->assertEquals(array( + array($request), + array($request, $response), + ), $calls); + } + + public function testInvalidCallback() + { + $this->setExpectedException('Buzz\Exception\InvalidArgumentException'); + $listener = new CallbackListener(array(1, 2, 3)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/EntryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/EntryTest.php new file mode 100755 index 000000000..7f9d8b94f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/EntryTest.php @@ -0,0 +1,15 @@ +assertEquals(123, $entry->getDuration()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/JournalTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/JournalTest.php new file mode 100755 index 000000000..7b520b1f6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/History/JournalTest.php @@ -0,0 +1,119 @@ +request1 = new Message\Request(); + $this->request1->setContent('request1'); + $this->request2 = new Message\Request(); + $this->request2->setContent('request2'); + $this->request3 = new Message\Request(); + $this->request3->setContent('request3'); + + $this->response1 = new Message\Response(); + $this->response1->setContent('response1'); + $this->response2 = new Message\Response(); + $this->response2->setContent('response2'); + $this->response3 = new Message\Response(); + $this->response3->setContent('response3'); + } + + protected function tearDown() + { + $this->request1 = null; + $this->request2 = null; + $this->request3 = null; + + $this->response1 = null; + $this->response2 = null; + $this->response3 = null; + } + + public function testRecordEnforcesLimit() + { + $journal = new Journal(); + $journal->setLimit(2); + + $journal->record($this->request1, $this->response1); + $journal->record($this->request2, $this->response2); + $journal->record($this->request3, $this->response3); + + $this->assertEquals(2, count($journal)); + } + + public function testGetLastReturnsTheLastEntry() + { + $journal = new Journal(); + + $journal->record($this->request1, $this->response1); + $journal->record($this->request2, $this->response2); + + $this->assertEquals($this->request2, $journal->getLast()->getRequest()); + + return $journal; + } + + /** + * @depends testGetLastReturnsTheLastEntry + */ + public function testGetLastRequestReturnsTheLastRequest(Journal $journal) + { + $this->assertEquals($this->request2, $journal->getLastRequest()); + } + + /** + * @depends testGetLastReturnsTheLastEntry + */ + public function testGetLastResponseReturnsTheLastResponse(Journal $journal) + { + $this->assertEquals($this->response2, $journal->getLastResponse()); + } + + /** + * @depends testGetLastReturnsTheLastEntry + */ + public function testClearRemovesEntries(Journal $journal) + { + $journal->clear(); + $this->assertEquals(0, count($journal)); + } + + /** + * @depends testGetLastReturnsTheLastEntry + */ + public function testForeachIteratesReversedEntries(Journal $journal) + { + $requests = array($this->request2, $this->request1); + $responses = array($this->response2, $this->response1); + + foreach ($journal as $index => $entry) { + $this->assertEquals($requests[$index], $entry->getRequest()); + $this->assertEquals($responses[$index], $entry->getResponse()); + } + } + + /** + * @depends testGetLastReturnsTheLastEntry + */ + public function testDuration() + { + $journal = new Journal(); + $journal->record($this->request1, $this->response1, 100); + + $this->assertEquals($journal->getLast()->getDuration(), 100); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/HistoryListenerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/HistoryListenerTest.php new file mode 100755 index 000000000..ca50521f2 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/HistoryListenerTest.php @@ -0,0 +1,39 @@ +journal = $this->getMockBuilder('Buzz\Listener\History\Journal') + ->disableOriginalConstructor() + ->getMock(); + + $this->listener = new HistoryListener($this->journal); + } + + public function testHistory() + { + $request = new Message\Request(); + $response = new Message\Response(); + + $this->journal->expects($this->once()) + ->method('record') + ->with($request, $response, $this->isType('float')); + + $this->listener->preSend($request); + $this->listener->postSend($request, $response); + } + + public function testGetter() + { + $this->assertSame($this->journal, $this->listener->getJournal()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/ListenerChainTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/ListenerChainTest.php new file mode 100755 index 000000000..b86cdfb24 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/ListenerChainTest.php @@ -0,0 +1,36 @@ +getMock('Buzz\Listener\ListenerInterface'))); + $this->assertEquals(1, count($listener->getListeners())); + + $listener->addListener($this->getMock('Buzz\Listener\ListenerInterface')); + $this->assertEquals(2, count($listener->getListeners())); + } + + public function testChain() + { + $delegate = $this->getMock('Buzz\Listener\ListenerInterface'); + $request = new Message\Request(); + $response = new Message\Response(); + + $delegate->expects($this->once()) + ->method('preSend') + ->with($request); + $delegate->expects($this->once()) + ->method('postSend') + ->with($request, $response); + + $listener = new ListenerChain(array($delegate)); + $listener->preSend($request); + $listener->postSend($request, $response); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/LoggerListenerTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/LoggerListenerTest.php new file mode 100755 index 000000000..aaa8aadfa --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Listener/LoggerListenerTest.php @@ -0,0 +1,31 @@ +assertRegExp('~^Sent "GET http://google.com/" in \d+ms$~', $line); + }; + + $request = new Message\Request(); + $request->fromUrl('http://google.com/'); + $response = new Message\Response(); + + $listener = new LoggerListener($logger); + $listener->preSend($request); + $listener->postSend($request, $response); + } + + public function testInvalidLogger() + { + $this->setExpectedException('Buzz\Exception\InvalidArgumentException'); + $listener = new LoggerListener(array(1, 2, 3)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/AbstractMessageTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/AbstractMessageTest.php new file mode 100755 index 000000000..a5b236a61 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/AbstractMessageTest.php @@ -0,0 +1,129 @@ +addHeader('X-My-Header: foo'); + $message->addHeader('X-My-Header: bar'); + + $this->assertEquals('foo'."\r\n".'bar', $message->getHeader('X-My-Header')); + $this->assertEquals('foo,bar', $message->getHeader('X-My-Header', ',')); + $this->assertEquals(array('foo', 'bar'), $message->getHeader('X-My-Header', false)); + } + + public function testGetHeaderReturnsNullIfHeaderDoesNotExist() + { + $message = new Message(); + + $this->assertNull($message->getHeader('X-Nonexistant')); + } + + public function testGetHeaderIsCaseInsensitive() + { + $message = new Message(); + $message->addHeader('X-zomg: test'); + + $this->assertEquals('test', $message->getHeader('X-ZOMG')); + } + + public function testToStringFormatsTheMessage() + { + $message = new Message(); + $message->addHeader('Foo: Bar'); + $message->setContent('==CONTENT=='); + + $expected = "Foo: Bar\r\n\r\n==CONTENT==\r\n"; + + $this->assertEquals($expected, (string) $message); + } + + public function testGetHeaderAttributesReturnsHeaderAttributes() + { + $message = new Message(); + $message->addHeader('Content-Type: text/xml; charset=utf8'); + + $this->assertEquals('utf8', $message->getHeaderAttribute('Content-Type', 'charset')); + } + + public function testGetNotFoundHeaderAttribute() + { + $message = new Message(); + $this->assertNull($message->getHeaderAttribute('Content-Type', 'charset')); + } + + public function testAddHeaders() + { + $message = new Message(); + $message->addHeaders(array('Content-Type: text/xml; charset=utf8', 'Foo' => 'test')); + $message->addHeaders(array('Test' => 'foo', 'Foo' => 'test')); + + $expected = array('Content-Type: text/xml; charset=utf8', 'Foo: test', 'Test: foo', 'Foo: test'); + $this->assertEquals($expected, $message->getHeaders()); + } + + public function testSetHeaders() + { + $message = new Message(); + $message->setHeaders(array('Content-Type: text/xml; charset=utf8', 'Foo' => 'test')); + $message->setHeaders(array('Test: foo', 'Foo' => 'test')); + + $expected = array('Test: foo', 'Foo: test'); + $this->assertEquals($expected, $message->getHeaders()); + } + + public function testToDomDocumentWithContentTypeTextXmlReturnsDomDocument() + { + $message = new Message(); + + $message->setHeaders(array('Content-Type: text/xml')); + $message->setContent(''); + $this->assertInstanceOf('DOMDocument', $message->toDomDocument()); + } + + public function testToDomDocumentWithContentTypeTextHtmlReturnsDomDocument() + { + $message = new Message(); + + $message->setHeaders(array('Content-Type: text/html')); + $message->setContent(''); + $this->assertInstanceOf('DOMDocument', $message->toDomDocument()); + } + + public function testToDomDocumentWithContentTypeTextXmlReturnsXmlString() + { + $message = new Message(); + $expected = << + + +XML; + + $message->setHeaders(array('Content-Type: text/xml')); + $message->setContent(''); + $this->assertEquals($expected, $message->toDomDocument()->saveXML()); + } + + public function testToDomDocumentWithContentTypeTextHTMLReturnsHTMLString() + { + $message = new Message(); + $expected = << + + +HTML; + + $message->setHeaders(array('Content-Type: text/html')); + $message->setContent(''); + $this->assertEquals($expected, $message->toDomDocument()->saveHTML()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FactoryTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FactoryTest.php new file mode 100755 index 000000000..d4bd9c653 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FactoryTest.php @@ -0,0 +1,61 @@ +factory = new Factory(); + } + + public function testCreateRequestDefaults() + { + $request = $this->factory->createRequest(); + + $this->assertInstanceOf('Buzz\Message\Request', $request); + $this->assertEquals(RequestInterface::METHOD_GET, $request->getMethod()); + $this->assertEquals('/', $request->getResource()); + $this->assertNull($request->getHost()); + } + + public function testCreateRequestArguments() + { + $request = $this->factory->createRequest(RequestInterface::METHOD_POST, '/foo', 'http://example.com'); + + $this->assertEquals(RequestInterface::METHOD_POST, $request->getMethod()); + $this->assertEquals('/foo', $request->getResource()); + $this->assertEquals('http://example.com', $request->getHost()); + } + + public function testCreateFormRequestDefaults() + { + $request = $this->factory->createFormRequest(); + + $this->assertInstanceOf('Buzz\Message\Form\FormRequest', $request); + $this->assertEquals(RequestInterface::METHOD_POST, $request->getMethod()); + $this->assertEquals('/', $request->getResource()); + $this->assertNull($request->getHost()); + } + + public function testCreateFormRequestArguments() + { + $request = $this->factory->createFormRequest(RequestInterface::METHOD_PUT, '/foo', 'http://example.com'); + + $this->assertInstanceOf('Buzz\Message\Form\FormRequest', $request); + $this->assertEquals(RequestInterface::METHOD_PUT, $request->getMethod()); + $this->assertEquals('/foo', $request->getResource()); + $this->assertEquals('http://example.com', $request->getHost()); + } + + public function testCreateResponse() + { + $this->assertInstanceOf('Buzz\Message\Response', $this->factory->createResponse()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/Fixtures/google.png b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/Fixtures/google.png new file mode 100755 index 0000000000000000000000000000000000000000..99b37d55bf77f370d0f0743deb3e8cb22fed16a3 GIT binary patch literal 7007 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 literal 0 HcmV?d00001 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormRequestTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormRequestTest.php new file mode 100755 index 000000000..e8bac61c9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormRequestTest.php @@ -0,0 +1,137 @@ + + */ +class FormRequestTest extends \PHPUnit_Framework_TestCase +{ + public function testGetContentGeneratesContent() + { + $message = new FormRequest(); + $message->setField('foo', 'bar'); + $message->setField('bar', 'foo'); + + $expected = "foo=bar&bar=foo"; + $this->assertEquals($expected, $message->getContent()); + } + + public function testGetContentGeneratesContentWithCustomArgSeparatorOutput() + { + + $argSeparatorOutput = ini_get('arg_separator.output'); + ini_set('arg_separator.output', '&'); + + $message = new FormRequest(); + $message->setField('foo', 'bar'); + $message->setField('bar', 'foo'); + + $expected = "foo=bar&bar=foo"; + $this->assertEquals($expected, $message->getContent()); + + ini_set('arg_separator.output', $argSeparatorOutput); + } + + + public function testAddDataAddsData() + { + $message = new FormRequest(); + $message->setField('foo', 'bar'); + + $this->assertEquals(array('foo' => 'bar'), $message->getFields()); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testSetContentIsNotPermitted() + { + $message = new FormRequest(); + $message->setContent('foobar'); + } + + public function testSetFields() + { + $request = new FormRequest(); + $request->setFields(array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getFields()); + } + + public function testContentType() + { + $request = new FormRequest(); + $this->assertEquals('application/x-www-form-urlencoded', $request->getHeader('Content-Type')); + } + + public function testDeepArray() + { + $request = new FormRequest(); + $request->setField('person', array('fname' => 'John', 'lname' => 'Doe')); + + $this->assertEquals('person%5Bfname%5D=John&person%5Blname%5D=Doe', $request->getContent()); + } + + public function testFieldPush() + { + $request = new FormRequest(); + $request->setField('colors[]', 'red'); + $request->setField('colors[]', 'blue'); + + $this->assertEquals('colors%5B0%5D=red&colors%5B1%5D=blue', $request->getContent()); + } + + public function testMultipartHeaders() + { + $request = new FormRequest(); + $request->setField('foo', array('bar' => new FormUpload())); + + $headers = $request->getHeaders(); + + $this->assertStringStartsWith('Content-Type: multipart/form-data; boundary=', $headers[0]); + } + + public function testMultipartContent() + { + $upload = new FormUpload(); + $upload->setFilename('image.jpg'); + $upload->setContent('foobar'); + + $request = new FormRequest(); + $request->setField('user[name]', 'Kris'); + $request->setField('user[image]', $upload); + + $content = $request->getContent(); + + $this->assertContains("Content-Disposition: form-data; name=\"user[name]\"\r\n\r\nKris\r\n", $content); + $this->assertContains("Content-Disposition: form-data; name=\"user[image]\"; filename=\"image.jpg\"\r\nContent-Type: text/plain\r\n\r\nfoobar\r\n", $content); + } + + public function testFilenamelessUpload() + { + $this->setExpectedException('LogicException'); + + $upload = new FormUpload(); + $upload->setContent('foobar'); + + $request = new FormRequest(); + $request->setField('user[name]', 'Kris'); + $request->setField('user[image]', $upload); + + $content = $request->getContent(); + } + + public function testGetRequest() + { + $request = new FormRequest(FormRequest::METHOD_GET, '/search'); + $request->setField('q', 'cats'); + + $this->assertEquals('/search?q=cats', $request->getResource()); + $this->assertEmpty($request->getContent()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormUploadTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormUploadTest.php new file mode 100755 index 000000000..eab61baf9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/FormUploadTest.php @@ -0,0 +1,44 @@ +setName('company[logo]'); + $upload->setFilename('google.png'); + $upload->setContent(file_get_contents(__DIR__.'/Fixtures/google.png')); + + $this->assertEquals(array( + 'Content-Disposition: form-data; name="company[logo]"; filename="google.png"', + 'Content-Type: image/png', + ), $upload->getHeaders()); + } + + public function testFileContent() + { + $upload = new FormUpload(__DIR__.'/Fixtures/google.png'); + $upload->setName('company[logo]'); + + $this->assertEquals(array( + 'Content-Disposition: form-data; name="company[logo]"; filename="google.png"', + 'Content-Type: image/png', + ), $upload->getHeaders()); + } + + public function testContentType() + { + $upload = new FormUpload(__DIR__.'/Fixtures/google.png'); + $upload->setName('company[logo]'); + $upload->setContentType('foo/bar'); + + $this->assertEquals(array( + 'Content-Disposition: form-data; name="company[logo]"; filename="google.png"', + 'Content-Type: foo/bar', + ), $upload->getHeaders()); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/RequestTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/RequestTest.php new file mode 100755 index 000000000..31920fd61 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/RequestTest.php @@ -0,0 +1,141 @@ +assertEquals('HEAD', $request->getMethod()); + $this->assertEquals('/resource/123', $request->getResource()); + $this->assertEquals('http://example.com', $request->getHost()); + } + + public function testGetUrlFormatsAUrl() + { + $request = new Request(); + $request->setHost('http://example.com'); + $request->setResource('/resource/123'); + + $this->assertEquals('http://example.com/resource/123', $request->getUrl()); + } + + public function testFromUrlSetsRequestValues() + { + $request = new Request(); + $request->fromUrl('http://example.com/resource/123?foo=bar#foobar'); + + $this->assertEquals('http://example.com', $request->getHost()); + $this->assertEquals('/resource/123?foo=bar', $request->getResource()); + } + + public function testFromUrlSetsADefaultResource() + { + $request = new Request(); + $request->fromUrl('http://example.com'); + + $this->assertEquals('/', $request->getResource()); + + $request = new Request(); + $request->fromUrl('http://example.com?foo=bar'); + + $this->assertEquals('/?foo=bar', $request->getResource()); + } + + public function testFromUrlSetsADefaultScheme() + { + $request = new Request(); + $request->fromUrl('example.com/foo/bar'); + + $this->assertEquals('http://example.com', $request->getHost()); + $this->assertEquals('/foo/bar', $request->getResource()); + } + + public function testFromUrlLeaveHostEmptyIfNoneIsProvided() + { + $request = new Request(); + $request->fromUrl('/foo'); + + $this->assertNull($request->getHost()); + } + + public function testFromUrlAcceptsPort() + { + $request = new Request(); + $request->fromUrl('http://localhost:3000/foo'); + + $this->assertEquals('http://localhost:3000', $request->getHost()); + $this->assertEquals('/foo', $request->getResource()); + } + + public function testFromUrlRejectsInvalidUrl() + { + $this->setExpectedException('InvalidArgumentException'); + + // port number is too high + $request = new Request(); + $request->fromUrl('http://localhost:123456'); + } + + public function testIsSecureChecksScheme() + { + $request = new Request('GET', '/resource/123', 'http://example.com'); + $this->assertFalse($request->isSecure()); + + $request = new Request('GET', '/resource/123', 'https://example.com'); + $this->assertTrue($request->isSecure()); + } + + public function testToStringFormatsTheRequest() + { + $request = new Request('POST', '/resource/123', 'http://example.com'); + $request->setProtocolVersion(1.1); + $request->addHeader('Content-Type: application/x-www-form-urlencoded'); + $request->setContent('foo=bar&bar=baz'); + + $expected = "POST /resource/123 HTTP/1.1\r\n"; + $expected .= "Host: http://example.com\r\n"; + $expected .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $expected .= "\r\n"; + $expected .= "foo=bar&bar=baz\r\n"; + + $this->assertEquals($expected, (string) $request); + } + + public function testMethodIsAlwaysUppercased() + { + $request = new Request('post', '/resource/123', 'http://example.com'); + + $this->assertEquals('POST', $request->getMethod()); + } + + public function testSetMethod() + { + $request = new Request(); + $request->setMethod('get'); + $this->assertEquals('GET', $request->getMethod()); + } + + public function testCookieMerge() + { + $request = new Request(); + $request->addHeader('Cookie: foo=bar'); + $request->addHeader('Content-Type: text/plain'); + $request->addHeader('Cookie: bar=foo'); + + $this->assertEquals(array( + 'Cookie: foo=bar; bar=foo', + 'Content-Type: text/plain', + ), $request->getHeaders()); + + $expected = "GET / HTTP/1.1\r\n"; + $expected .= "Cookie: foo=bar; bar=foo\r\n"; + $expected .= "Content-Type: text/plain\r\n"; + + $this->assertEquals($expected, (string) $request); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/ResponseTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/ResponseTest.php new file mode 100755 index 000000000..dccda3910 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Message/ResponseTest.php @@ -0,0 +1,114 @@ +assertNull($response->getProtocolVersion()); + + $response->addHeader('HTTP/1.0 200 OK'); + + $this->assertEquals(1.0, $response->getProtocolVersion()); + } + + public function testGetStatusCodeReturnsTheStatusCode() + { + $response = new Response(); + + $this->assertNull($response->getStatusCode()); + + $response->addHeader('HTTP/1.0 200 OK'); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testGetReasonPhraseReturnsTheReasonPhrase() + { + $response = new Response(); + + $this->assertEquals($response->getReasonPhrase(), null); + + $response->addHeader('HTTP/1.0 200 OK'); + + $this->assertEquals('OK', $response->getReasonPhrase()); + } + + public function testGetReasonPhraseReturnsAMultiwordReasonPhrase() + { + $response = new Response(); + + $this->assertNull($response->getReasonPhrase()); + + $response->addHeader('HTTP/1.0 500 Internal Server Error'); + + $this->assertEquals('Internal Server Error', $response->getReasonPhrase()); + } + + public function testAddHeadersResetsStatusLine() + { + $response = new Response(); + $this->assertNull($response->getStatusCode()); + $response->addHeaders(array('HTTP/1.0 200 OK')); + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @dataProvider statusProvider + * + * + */ + public function testIssers($code, $method, $expected) + { + $response = new Response(); + $response->addHeaders(array('HTTP/1.0 '.$code.' Status')); + $this->assertEquals($expected, $response->{$method}()); + } + + public function statusProvider() + { + return array( + array(50, 'isInvalid', true), + array(700, 'isInvalid', true), + array(100, 'isInvalid', false), + + array(100, 'isInformational', true), + array(199, 'isInformational', true), + array(200, 'isInformational', false), + + array(200, 'isSuccessful', true), + array(299, 'isSuccessful', true), + array(300, 'isSuccessful', false), + + array(301, 'isRedirection', true), + array(302, 'isRedirection', true), + array(400, 'isRedirection', false), + + array(404, 'isClientError', true), + array(401, 'isClientError', true), + array(500, 'isClientError', false), + + array(500, 'isServerError', true), + array(400, 'isServerError', false), + + array(200, 'isOk', true), + array(201, 'isOk', false), + + array(403, 'isForbidden', true), + array(404, 'isForbidden', false), + + array(404, 'isNotFound', true), + array(403, 'isNotFound', false), + + array(201, 'isEmpty', true), + array(204, 'isEmpty', true), + array(304, 'isEmpty', true), + array(203, 'isEmpty', false), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieJarTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieJarTest.php new file mode 100755 index 000000000..b4ce9761e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieJarTest.php @@ -0,0 +1,73 @@ +setHost('http://www.example.com'); + + $response = new Message\Response(); + $response->addHeader('Set-Cookie: SESSION2=qwerty'); + $response->addHeader('Set-Cookie: SESSION1=asdf'); + + $jar = new CookieJar(); + $jar->processSetCookieHeaders($request, $response); + + $cookies = $jar->getCookies(); + + $this->assertEquals(2, count($cookies)); + foreach ($cookies as $cookie) { + $this->assertEquals('www.example.com', $cookie->getAttribute(Cookie::ATTR_DOMAIN)); + $this->assertTrue(in_array($cookie->getName(), array('SESSION1', 'SESSION2'))); + } + } + + public function testAddCookieHeadersAddsCookieHeaders() + { + $request = new Message\Request(); + $request->setHost('http://www.example.com'); + + $cookie = new Cookie(); + $cookie->setName('SESSION'); + $cookie->setValue('asdf'); + $cookie->setAttribute(Cookie::ATTR_DOMAIN, '.example.com'); + + $jar = new CookieJar(); + $jar->setCookies(array($cookie)); + $jar->addCookieHeaders($request); + + $this->assertEquals('SESSION=asdf', $request->getHeader('Cookie')); + } + + public function testClearExpiredCookiesRemovesExpiredCookies() + { + $cookie = new Cookie(); + $cookie->setName('SESSION'); + $cookie->setValue('asdf'); + $cookie->setAttribute(Cookie::ATTR_EXPIRES, 'Fri, 01-Dec-1999 00:00:00 GMT'); + + $jar = new CookieJar(); + $jar->addCookie($cookie); + $jar->clearExpiredCookies(); + + $this->assertEquals(0, count($jar->getCookies())); + + $cookie = new Cookie(); + $cookie->setName('SESSION'); + $cookie->setValue('asdf'); + $cookie->setAttribute(Cookie::ATTR_MAX_AGE, '-60'); + + $jar = new CookieJar(); + $jar->addCookie($cookie); + $jar->clearExpiredCookies(); + + $this->assertEquals(0, count($jar->getCookies())); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieTest.php new file mode 100755 index 000000000..6efe9fdad --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/CookieTest.php @@ -0,0 +1,151 @@ +fromSetCookieHeader('SESSION=asdf; expires='.date('r', strtotime('2000-01-01 00:00:00')).'; path=/; domain=.example.com; secure', 'www.example.com'); + + $this->assertEquals('SESSION', $cookie->getName()); + $this->assertEquals('asdf', $cookie->getValue()); + $this->assertEquals(array( + 'expires' => date('r', strtotime('2000-01-01 00:00:00')), + 'path' => '/', + 'domain' => '.example.com', + 'secure' => null, + ), $cookie->getAttributes()); + } + + public function testFromSetCookieHeaderFallsBackToIssuingDomain() + { + $cookie = new Cookie(); + $cookie->fromSetCookieHeader('SESSION=asdf', 'example.com'); + + $this->assertEquals('example.com', $cookie->getAttribute(Cookie::ATTR_DOMAIN)); + } + + public function testToCookieHeaderFormatsACookieHeader() + { + $cookie = new Cookie(); + $cookie->setName('SESSION'); + $cookie->setValue('asdf'); + + $this->assertEquals('Cookie: SESSION=asdf', $cookie->toCookieHeader()); + } + + public function testMatchesDomainMatchesSimpleDomains() + { + $cookie = new Cookie(); + $cookie->setAttribute('domain', 'nytimes.com'); + + $this->assertTrue($cookie->matchesDomain('nytimes.com')); + $this->assertFalse($cookie->matchesDomain('google.com')); + } + + public function testMatchesDomainMatchesSubdomains() + { + $cookie = new Cookie(); + $cookie->setAttribute('domain', '.nytimes.com'); + + $this->assertTrue($cookie->matchesDomain('nytimes.com')); + $this->assertTrue($cookie->matchesDomain('blogs.nytimes.com')); + $this->assertFalse($cookie->matchesDomain('google.com')); + } + + public function testIsExpiredChecksMaxAge() + { + $cookie = new Cookie(); + $cookie->setAttribute('max-age', 60); + + $this->assertFalse($cookie->isExpired()); + + $cookie = new Cookie(); + $cookie->setCreatedAt(strtotime('-1 hour')); + $cookie->setAttribute('max-age', 60); + + $this->assertTrue($cookie->isExpired()); + } + + public function testIsExpiredChecksExpires() + { + $cookie = new Cookie(); + $cookie->setAttribute('expires', date('r', strtotime('+1 week'))); + + $this->assertFalse($cookie->isExpired()); + + $cookie = new Cookie(); + $cookie->setAttribute('expires', date('r', strtotime('-1 month'))); + + $this->assertTrue($cookie->isExpired()); + } + + public function testMatchesPathChecksPath() + { + $cookie = new Cookie(); + $cookie->setAttribute('path', '/resource'); + + $this->assertTrue($cookie->matchesPath('/resource/123')); + $this->assertFalse($cookie->matchesPath('/login')); + + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesPath('/resource/123')); + } + + public function testMatchesRequestChecksDomain() + { + $request = new Message\Request(); + $request->setHost('http://example.com'); + + $cookie = new Cookie(); + $cookie->setAttribute(Cookie::ATTR_DOMAIN, 'example.com'); + + $this->assertTrue($cookie->matchesRequest($request)); + + $cookie = new Cookie(); + $cookie->setAttribute(Cookie::ATTR_DOMAIN, 'foo.com'); + + $this->assertFalse($cookie->matchesRequest($request)); + } + + public function testMatchesRequestChecksPath() + { + $request = new Message\Request(); + $request->setHost('http://example.com'); + $request->setResource('/foo/bar'); + + $cookie = new Cookie(); + $cookie->setAttribute(Cookie::ATTR_DOMAIN, 'example.com'); + $cookie->setAttribute(Cookie::ATTR_PATH, '/foo'); + + $this->assertTrue($cookie->matchesRequest($request)); + + $cookie = new Cookie(); + $cookie->setAttribute(Cookie::ATTR_DOMAIN, 'example.com'); + $cookie->setAttribute(Cookie::ATTR_PATH, '/foo/bar/baz'); + + $this->assertFalse($cookie->matchesRequest($request)); + } + + public function testMatchesRequestChecksSecureAttribute() + { + $request = new Message\Request(); + $request->setHost('https://example.com'); + + $cookie = new Cookie(); + $cookie->setAttribute(Cookie::ATTR_DOMAIN, 'example.com'); + $cookie->setAttribute(Cookie::ATTR_SECURE, null); + + $this->assertTrue($cookie->matchesRequest($request)); + + $request = new Message\Request(); + $request->setHost('http://example.com'); + + $this->assertFalse($cookie->matchesRequest($request)); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/UrlTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/UrlTest.php new file mode 100755 index 000000000..7d1f704bf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/Buzz/Test/Util/UrlTest.php @@ -0,0 +1,57 @@ +assertEquals($host, $url->getHost()); + $this->assertEquals($resource, $url->getResource()); + } + + public function provideUrlAndHost() + { + return array( + array('https://example.com/resource/123?foo=bar#foobar', 'https://example.com', '/resource/123?foo=bar'), + array('http://example.com', 'http://example.com', '/'), + array('http://example.com?foo=bar', 'http://example.com', '/?foo=bar'), + array('example.com/foo/bar', 'http://example.com', '/foo/bar'), + array('/foo', null, '/foo'), + array('http://localhost:3000/foo', 'http://localhost:3000', '/foo'), + ); + } + + public function testInvalidUrl() + { + $this->setExpectedException('InvalidArgumentException'); + new Url('http://localhost:123456'); + } + + /** + * @dataProvider providePattern + */ + public function testFormat($input, $pattern, $expected) + { + $url = new Url($input); + $this->assertEquals($expected, $url->format($pattern)); + } + + public function providePattern() + { + $full = 'http://foo:bar@example.com:123/asdf?tweedle=dee#dum'; + $host = 'example.com'; + + return array( + array($full, 's://u:a@h:op?q#f', 'http://foo:bar@example.com:123/asdf?tweedle=dee#dum'), + array($full, 'HR', 'http://example.com:123/asdf?tweedle=dee'), + array($host, 'HR', 'http://example.com/'), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/nginx.conf b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/nginx.conf new file mode 100755 index 000000000..8c1a4c288 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/nginx.conf @@ -0,0 +1,49 @@ +events { + worker_connections 1024; +} + +http { + keepalive_timeout 65; + + fastcgi_connect_timeout 60; + fastcgi_send_timeout 180; + fastcgi_read_timeout 180; + fastcgi_buffer_size 32k; + fastcgi_buffers 16 16k; + fastcgi_intercept_errors on; + + upstream php { + server 127.0.0.1:9000; + } + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + server { + listen 8080 default_server; + server_name .localhost; + + charset utf-8; + + root .; + + location ~ \.php$ { + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + + fastcgi_param QUERY_STRING $query_string; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param CONTENT_TYPE $content_type if_not_empty; + + fastcgi_param CONTENT_LENGTH $content_length; + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param SCRIPT_NAME $fastcgi_script_name; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param DOCUMENT_URI $document_uri; + fastcgi_param DOCUMENT_ROOT $document_root; + fastcgi_param SERVER_PROTOCOL $server_protocol; + + fastcgi_pass php; + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/squid.conf b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/squid.conf new file mode 100755 index 000000000..1feab63b9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/etc/squid.conf @@ -0,0 +1,2 @@ +http_access allow all +http_port 3128 diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/server.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/server.php new file mode 100755 index 000000000..bada944d0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/kriswallsmith/buzz/test/server.php @@ -0,0 +1,18 @@ + $_SERVER, + 'GET' => $_GET, + 'POST' => $_POST, + 'FILES' => $_FILES, + 'INPUT' => file_get_contents('php://input'), +)); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.gitignore b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.gitignore new file mode 100755 index 000000000..3ce5adbbd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.gitignore @@ -0,0 +1,2 @@ +.idea +vendor diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.travis.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.travis.yml new file mode 100755 index 000000000..eef782c42 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/.travis.yml @@ -0,0 +1,32 @@ +language: php +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + - hhvm-nightly + +matrix: + allow_failures: + - php: hhvm + - php: hhvm-nightly + +script: + - vendor/bin/phpunit + +before_script: + - sudo apt-get -qq update > /dev/null + - phpenv rehash > /dev/null + - composer selfupdate --quiet + - composer install --no-interaction --prefer-source --dev + - vendor/bin/phpunit + - composer update --no-interaction --prefer-source --dev + +notifications: + irc: "irc.freenode.org#phpdocumentor" + email: + - mike.vanriel@naenius.com + - ashnazg@php.net + - boen.robot@gmail.com diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/LICENSE new file mode 100755 index 000000000..792e4040f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2010 Mike van Riel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/README.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/README.md new file mode 100755 index 000000000..6405d1a10 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/README.md @@ -0,0 +1,57 @@ +The ReflectionDocBlock Component [![Build Status](https://secure.travis-ci.org/phpDocumentor/ReflectionDocBlock.png)](https://travis-ci.org/phpDocumentor/ReflectionDocBlock) +================================ + +Introduction +------------ + +The ReflectionDocBlock component of phpDocumentor provides a DocBlock parser +that is 100% compatible with the [PHPDoc standard](http://phpdoc.org/docs/latest). + +With this component, a library can provide support for annotations via DocBlocks +or otherwise retrieve information that is embedded in a DocBlock. + +> **Note**: *this is a core component of phpDocumentor and is constantly being +> optimized for performance.* + +Installation +------------ + +You can install the component in the following ways: + +* Use the official Github repository (https://github.com/phpDocumentor/ReflectionDocBlock) +* Via Composer (http://packagist.org/packages/phpdocumentor/reflection-docblock) + +Usage +----- + +The ReflectionDocBlock component is designed to work in an identical fashion to +PHP's own Reflection extension (http://php.net/manual/en/book.reflection.php). + +Parsing can be initiated by instantiating the +`\phpDocumentor\Reflection\DocBlock()` class and passing it a string containing +a DocBlock (including asterisks) or by passing an object supporting the +`getDocComment()` method. + +> *Examples of objects having the `getDocComment()` method are the +> `ReflectionClass` and the `ReflectionMethod` classes of the PHP +> Reflection extension* + +Example: + + $class = new ReflectionClass('MyClass'); + $phpdoc = new \phpDocumentor\Reflection\DocBlock($class); + +or + + $docblock = <<=5.3.3" + }, + "autoload": { + "psr-0": {"phpDocumentor": ["src/"]} + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/composer.lock b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/composer.lock new file mode 100755 index 000000000..4c6a8bb78 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/composer.lock @@ -0,0 +1,827 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "ea1734d11b8c878445c2c6e58de8b85f", + "packages": [], + "packages-dev": [ + { + "name": "ocramius/instantiator", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/Instantiator.git", + "reference": "a7abbb5fc9df6e7126af741dd6c140d1a7369435" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/Instantiator/zipball/a7abbb5fc9df6e7126af741dd6c140d1a7369435", + "reference": "a7abbb5fc9df6e7126af741dd6c140d1a7369435", + "shasum": "" + }, + "require": { + "ocramius/lazy-map": "1.0.*", + "php": "~5.3" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "2.0.*@ALPHA" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Instantiator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/Ocramius/Instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2014-08-14 15:10:55" + }, + { + "name": "ocramius/lazy-map", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/LazyMap.git", + "reference": "7fe3d347f5e618bcea7d39345ff83f3651d8b752" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/LazyMap/zipball/7fe3d347f5e618bcea7d39345ff83f3651d8b752", + "reference": "7fe3d347f5e618bcea7d39345ff83f3651d8b752", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "athletic/athletic": "~0.1.6", + "phpmd/phpmd": "1.5.*", + "phpunit/phpunit": ">=3.7", + "satooshi/php-coveralls": "~0.6", + "squizlabs/php_codesniffer": "1.4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "LazyMap\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/", + "role": "Developer" + } + ], + "description": "A library that provides lazy instantiation logic for a map of objects", + "homepage": "https://github.com/Ocramius/LazyMap", + "keywords": [ + "lazy", + "lazy instantiation", + "lazy loading", + "map", + "service location" + ], + "time": "2013-11-09 22:30:54" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "6d196af48e8c100a3ae881940123e693da5a9217" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6d196af48e8c100a3ae881940123e693da5a9217", + "reference": "6d196af48e8c100a3ae881940123e693da5a9217", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3.1", + "phpunit/php-text-template": "~1.2.0", + "phpunit/php-token-stream": "~1.2.2", + "sebastian/environment": "~1.0.0", + "sebastian/version": "~1.0.3" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4.0.14" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2014-08-06 06:39:42" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-10-10 15:34:57" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2014-01-30 17:20:04" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2014-03-03 05:10:30" + }, + { + "name": "phpunit/phpunit", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a33fa68ece9f8c68589bfc2da8d2794e27b820bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a33fa68ece9f8c68589bfc2da8d2794e27b820bc", + "reference": "a33fa68ece9f8c68589bfc2da8d2794e27b820bc", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": "~2.0", + "phpunit/php-file-iterator": "~1.3.1", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "~1.0.2", + "phpunit/phpunit-mock-objects": "~2.2", + "sebastian/comparator": "~1.0", + "sebastian/diff": "~1.1", + "sebastian/environment": "~1.0", + "sebastian/exporter": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2014-08-18 05:12:30" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "42e589e08bc86e3e9bdf20d385e948347788505b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/42e589e08bc86e3e9bdf20d385e948347788505b", + "reference": "42e589e08bc86e3e9bdf20d385e948347788505b", + "shasum": "" + }, + "require": { + "ocramius/instantiator": "~1.0", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "4.2.*@dev" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2014-08-02 13:50:58" + }, + { + "name": "sebastian/comparator", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", + "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.1", + "sebastian/exporter": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2014-05-02 07:05:58" + }, + { + "name": "sebastian/diff", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", + "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2013-08-03 16:46:33" + }, + { + "name": "sebastian/environment", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/79517609ec01139cd7e9fded0dd7ce08c952ef6a", + "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "4.0.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2014-02-18 16:17:19" + }, + { + "name": "sebastian/exporter", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", + "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "4.0.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net", + "role": "Lead" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2014-02-16 08:26:31" + }, + { + "name": "sebastian/version", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2014-03-07 15:35:33" + }, + { + "name": "symfony/yaml", + "version": "v2.5.3", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", + "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2014-08-05 09:00:40" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "platform": { + "php": ">=5.3.3" + }, + "platform-dev": [] +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/phpunit.xml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/phpunit.xml.dist new file mode 100755 index 000000000..f67ad2a20 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + + ./tests/ + + + + + ./src/ + + + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock.php new file mode 100755 index 000000000..02968b163 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock.php @@ -0,0 +1,468 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Context; +use phpDocumentor\Reflection\DocBlock\Location; + +/** + * Parses the DocBlock for any structure. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class DocBlock implements \Reflector +{ + /** @var string The opening line for this docblock. */ + protected $short_description = ''; + + /** + * @var DocBlock\Description The actual + * description for this docblock. + */ + protected $long_description = null; + + /** + * @var Tag[] An array containing all + * the tags in this docblock; except inline. + */ + protected $tags = array(); + + /** @var Context Information about the context of this DocBlock. */ + protected $context = null; + + /** @var Location Information about the location of this DocBlock. */ + protected $location = null; + + /** @var bool Is this DocBlock (the start of) a template? */ + protected $isTemplateStart = false; + + /** @var bool Does this DocBlock signify the end of a DocBlock template? */ + protected $isTemplateEnd = false; + + /** + * Parses the given docblock and populates the member fields. + * + * The constructor may also receive namespace information such as the + * current namespace and aliases. This information is used by some tags + * (e.g. @return, @param, etc.) to turn a relative Type into a FQCN. + * + * @param \Reflector|string $docblock A docblock comment (including + * asterisks) or reflector supporting the getDocComment method. + * @param Context $context The context in which the DocBlock + * occurs. + * @param Location $location The location within the file that this + * DocBlock occurs in. + * + * @throws \InvalidArgumentException if the given argument does not have the + * getDocComment method. + */ + public function __construct( + $docblock, + Context $context = null, + Location $location = null + ) { + if (is_object($docblock)) { + if (!method_exists($docblock, 'getDocComment')) { + throw new \InvalidArgumentException( + 'Invalid object passed; the given reflector must support ' + . 'the getDocComment method' + ); + } + + $docblock = $docblock->getDocComment(); + } + + $docblock = $this->cleanInput($docblock); + + list($templateMarker, $short, $long, $tags) = $this->splitDocBlock($docblock); + $this->isTemplateStart = $templateMarker === '#@+'; + $this->isTemplateEnd = $templateMarker === '#@-'; + $this->short_description = $short; + $this->long_description = new DocBlock\Description($long, $this); + $this->parseTags($tags); + + $this->context = $context; + $this->location = $location; + } + + /** + * Strips the asterisks from the DocBlock comment. + * + * @param string $comment String containing the comment text. + * + * @return string + */ + protected function cleanInput($comment) + { + $comment = trim( + preg_replace( + '#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', + '$1', + $comment + ) + ); + + // reg ex above is not able to remove */ from a single line docblock + if (substr($comment, -2) == '*/') { + $comment = trim(substr($comment, 0, -2)); + } + + // normalize strings + $comment = str_replace(array("\r\n", "\r"), "\n", $comment); + + return $comment; + } + + /** + * Splits the DocBlock into a template marker, summary, description and block of tags. + * + * @param string $comment Comment to split into the sub-parts. + * + * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. + * @author Mike van Riel for extending the regex with template marker support. + * + * @return string[] containing the template marker (if any), summary, description and a string containing the tags. + */ + protected function splitDocBlock($comment) + { + // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This + // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the + // performance impact of running a regular expression + if (strpos($comment, '@') === 0) { + return array('', '', '', $comment); + } + + // clears all extra horizontal whitespace from the line endings to prevent parsing issues + $comment = preg_replace('/\h*$/Sum', '', $comment); + + /* + * Splits the docblock into a template marker, short description, long description and tags section + * + * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may + * occur after it and will be stripped). + * - The short description is started from the first character until a dot is encountered followed by a + * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing + * errors). This is optional. + * - The long description, any character until a new line is encountered followed by an @ and word + * characters (a tag). This is optional. + * - Tags; the remaining characters + * + * Big thanks to RichardJ for contributing this Regular Expression + */ + preg_match( + '/ + \A + # 1. Extract the template marker + (?:(\#\@\+|\#\@\-)\n?)? + + # 2. Extract the summary + (?: + (?! @\pL ) # The summary may not start with an @ + ( + [^\n.]+ + (?: + (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines + [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line + [^\n.]+ # Include anything else + )* + \.? + )? + ) + + # 3. Extract the description + (?: + \s* # Some form of whitespace _must_ precede a description because a summary must be there + (?! @\pL ) # The description may not start with an @ + ( + [^\n]+ + (?: \n+ + (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line + [^\n]+ # Include anything else + )* + ) + )? + + # 4. Extract the tags (anything that follows) + (\s+ [\s\S]*)? # everything that follows + /ux', + $comment, + $matches + ); + array_shift($matches); + + while (count($matches) < 4) { + $matches[] = ''; + } + + return $matches; + } + + /** + * Creates the tag objects. + * + * @param string $tags Tag block to parse. + * + * @return void + */ + protected function parseTags($tags) + { + $result = array(); + $tags = trim($tags); + if ('' !== $tags) { + if ('@' !== $tags[0]) { + throw new \LogicException( + 'A tag block started with text instead of an actual tag,' + . ' this makes the tag block invalid: ' . $tags + ); + } + foreach (explode("\n", $tags) as $tag_line) { + if (isset($tag_line[0]) && ($tag_line[0] === '@')) { + $result[] = $tag_line; + } else { + $result[count($result) - 1] .= "\n" . $tag_line; + } + } + + // create proper Tag objects + foreach ($result as $key => $tag_line) { + $result[$key] = Tag::createInstance(trim($tag_line), $this); + } + } + + $this->tags = $result; + } + + /** + * Gets the text portion of the doc block. + * + * Gets the text portion (short and long description combined) of the doc + * block. + * + * @return string The text portion of the doc block. + */ + public function getText() + { + $short = $this->getShortDescription(); + $long = $this->getLongDescription()->getContents(); + + if ($long) { + return "{$short}\n\n{$long}"; + } else { + return $short; + } + } + + /** + * Set the text portion of the doc block. + * + * Sets the text portion (short and long description combined) of the doc + * block. + * + * @param string $docblock The new text portion of the doc block. + * + * @return $this This doc block. + */ + public function setText($comment) + { + list(,$short, $long) = $this->splitDocBlock($comment); + $this->short_description = $short; + $this->long_description = new DocBlock\Description($long, $this); + return $this; + } + /** + * Returns the opening line or also known as short description. + * + * @return string + */ + public function getShortDescription() + { + return $this->short_description; + } + + /** + * Returns the full description or also known as long description. + * + * @return DocBlock\Description + */ + public function getLongDescription() + { + return $this->long_description; + } + + /** + * Returns whether this DocBlock is the start of a Template section. + * + * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker + * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. + * + * An example of such an opening is: + * + * ``` + * /**#@+ + * * My DocBlock + * * / + * ``` + * + * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all + * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). + * + * @see self::isTemplateEnd() for the check whether a closing marker was provided. + * + * @return boolean + */ + public function isTemplateStart() + { + return $this->isTemplateStart; + } + + /** + * Returns whether this DocBlock is the end of a Template section. + * + * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. + * + * @return boolean + */ + public function isTemplateEnd() + { + return $this->isTemplateEnd; + } + + /** + * Returns the current context. + * + * @return Context + */ + public function getContext() + { + return $this->context; + } + + /** + * Returns the current location. + * + * @return Location + */ + public function getLocation() + { + return $this->location; + } + + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags() + { + return $this->tags; + } + + /** + * Returns an array of tags matching the given name. If no tags are found + * an empty array is returned. + * + * @param string $name String to search by. + * + * @return Tag[] + */ + public function getTagsByName($name) + { + $result = array(); + + /** @var Tag $tag */ + foreach ($this->getTags() as $tag) { + if ($tag->getName() != $name) { + continue; + } + + $result[] = $tag; + } + + return $result; + } + + /** + * Checks if a tag of a certain type is present in this DocBlock. + * + * @param string $name Tag name to check for. + * + * @return bool + */ + public function hasTag($name) + { + /** @var Tag $tag */ + foreach ($this->getTags() as $tag) { + if ($tag->getName() == $name) { + return true; + } + } + + return false; + } + + /** + * Appends a tag at the end of the list of tags. + * + * @param Tag $tag The tag to add. + * + * @return Tag The newly added tag. + * + * @throws \LogicException When the tag belongs to a different DocBlock. + */ + public function appendTag(Tag $tag) + { + if (null === $tag->getDocBlock()) { + $tag->setDocBlock($this); + } + + if ($tag->getDocBlock() === $this) { + $this->tags[] = $tag; + } else { + throw new \LogicException( + 'This tag belongs to a different DocBlock object.' + ); + } + + return $tag; + } + + + /** + * Builds a string representation of this object. + * + * @todo determine the exact format as used by PHP Reflection and + * implement it. + * + * @return string + * @codeCoverageIgnore Not yet implemented + */ + public static function export() + { + throw new \Exception('Not yet implemented'); + } + + /** + * Returns the exported information (we should use the export static method + * BUT this throws an exception at this point). + * + * @return string + * @codeCoverageIgnore Not yet implemented + */ + public function __toString() + { + return 'Not yet implemented'; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Context.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Context.php new file mode 100755 index 000000000..81aa83ce5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Context.php @@ -0,0 +1,154 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +/** + * The context in which a DocBlock occurs. + * + * @author Vasil Rangelov + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class Context +{ + /** @var string The current namespace. */ + protected $namespace = ''; + + /** @var array List of namespace aliases => Fully Qualified Namespace. */ + protected $namespace_aliases = array(); + + /** @var string Name of the structural element, within the namespace. */ + protected $lsen = ''; + + /** + * Cteates a new context. + * @param string $namespace The namespace where this DocBlock + * resides in. + * @param array $namespace_aliases List of namespace aliases => Fully + * Qualified Namespace. + * @param string $lsen Name of the structural element, within + * the namespace. + */ + public function __construct( + $namespace = '', + array $namespace_aliases = array(), + $lsen = '' + ) { + if (!empty($namespace)) { + $this->setNamespace($namespace); + } + $this->setNamespaceAliases($namespace_aliases); + $this->setLSEN($lsen); + } + + /** + * @return string The namespace where this DocBlock resides in. + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @return array List of namespace aliases => Fully Qualified Namespace. + */ + public function getNamespaceAliases() + { + return $this->namespace_aliases; + } + + /** + * Returns the Local Structural Element Name. + * + * @return string Name of the structural element, within the namespace. + */ + public function getLSEN() + { + return $this->lsen; + } + + /** + * Sets a new namespace. + * + * Sets a new namespace for the context. Leading and trailing slashes are + * trimmed, and the keywords "global" and "default" are treated as aliases + * to no namespace. + * + * @param string $namespace The new namespace to set. + * + * @return $this + */ + public function setNamespace($namespace) + { + if ('global' !== $namespace + && 'default' !== $namespace + ) { + // Srip leading and trailing slash + $this->namespace = trim((string)$namespace, '\\'); + } else { + $this->namespace = ''; + } + return $this; + } + + /** + * Sets the namespace aliases, replacing all previous ones. + * + * @param array $namespace_aliases List of namespace aliases => Fully + * Qualified Namespace. + * + * @return $this + */ + public function setNamespaceAliases(array $namespace_aliases) + { + $this->namespace_aliases = array(); + foreach ($namespace_aliases as $alias => $fqnn) { + $this->setNamespaceAlias($alias, $fqnn); + } + return $this; + } + + /** + * Adds a namespace alias to the context. + * + * @param string $alias The alias name (the part after "as", or the last + * part of the Fully Qualified Namespace Name) to add. + * @param string $fqnn The Fully Qualified Namespace Name for this alias. + * Any form of leading/trailing slashes are accepted, but what will be + * stored is a name, prefixed with a slash, and no trailing slash. + * + * @return $this + */ + public function setNamespaceAlias($alias, $fqnn) + { + $this->namespace_aliases[$alias] = '\\' . trim((string)$fqnn, '\\'); + return $this; + } + + /** + * Sets a new Local Structural Element Name. + * + * Sets a new Local Structural Element Name. A local name also contains + * punctuation determining the kind of structural element (e.g. trailing "(" + * and ")" for functions and methods). + * + * @param string $lsen The new local name of a structural element. + * + * @return $this + */ + public function setLSEN($lsen) + { + $this->lsen = (string)$lsen; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Description.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Description.php new file mode 100755 index 000000000..d41142e28 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Description.php @@ -0,0 +1,223 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock; + +/** + * Parses a Description of a DocBlock or tag. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class Description implements \Reflector +{ + /** @var string */ + protected $contents = ''; + + /** @var array The contents, as an array of strings and Tag objects. */ + protected $parsedContents = null; + + /** @var DocBlock The DocBlock which this description belongs to. */ + protected $docblock = null; + + /** + * Populates the fields of a description. + * + * @param string $content The description's conetnts. + * @param DocBlock $docblock The DocBlock which this description belongs to. + */ + public function __construct($content, DocBlock $docblock = null) + { + $this->setContent($content)->setDocBlock($docblock); + } + + /** + * Gets the text of this description. + * + * @return string + */ + public function getContents() + { + return $this->contents; + } + + /** + * Sets the text of this description. + * + * @param string $content The new text of this description. + * + * @return $this + */ + public function setContent($content) + { + $this->contents = trim($content); + + $this->parsedContents = null; + return $this; + } + + /** + * Returns the parsed text of this description. + * + * @return array An array of strings and tag objects, in the order they + * occur within the description. + */ + public function getParsedContents() + { + if (null === $this->parsedContents) { + $this->parsedContents = preg_split( + '/\{ + # "{@}" is not a valid inline tag. This ensures that + # we do not treat it as one, but treat it literally. + (?!@\}) + # We want to capture the whole tag line, but without the + # inline tag delimiters. + (\@ + # Match everything up to the next delimiter. + [^{}]* + # Nested inline tag content should not be captured, or + # it will appear in the result separately. + (?: + # Match nested inline tags. + (?: + # Because we did not catch the tag delimiters + # earlier, we must be explicit with them here. + # Notice that this also matches "{}", as a way + # to later introduce it as an escape sequence. + \{(?1)?\} + | + # Make sure we match hanging "{". + \{ + ) + # Match content after the nested inline tag. + [^{}]* + )* # If there are more inline tags, match them as well. + # We use "*" since there may not be any nested inline + # tags. + ) + \}/Sux', + $this->contents, + null, + PREG_SPLIT_DELIM_CAPTURE + ); + + $count = count($this->parsedContents); + for ($i=1; $i<$count; $i += 2) { + $this->parsedContents[$i] = Tag::createInstance( + $this->parsedContents[$i], + $this->docblock + ); + } + + //In order to allow "literal" inline tags, the otherwise invalid + //sequence "{@}" is changed to "@", and "{}" is changed to "}". + //See unit tests for examples. + for ($i=0; $i<$count; $i += 2) { + $this->parsedContents[$i] = str_replace( + array('{@}', '{}'), + array('@', '}'), + $this->parsedContents[$i] + ); + } + } + return $this->parsedContents; + } + + /** + * Return a formatted variant of the Long Description using MarkDown. + * + * @todo this should become a more intelligent piece of code where the + * configuration contains a setting what format long descriptions are. + * + * @codeCoverageIgnore Will be removed soon, in favor of adapters at + * PhpDocumentor itself that will process text in various formats. + * + * @return string + */ + public function getFormattedContents() + { + $result = $this->contents; + + // if the long description contains a plain HTML element, surround + // it with a pre element. Please note that we explicitly used str_replace + // and not preg_replace to gain performance + if (strpos($result, '') !== false) { + $result = str_replace( + array('', "\r\n", "\n", "\r", ''), + array('
    ', '', '', '', '
    '), + $result + ); + } + + if (class_exists('Parsedown')) { + $markdown = \Parsedown::instance(); + $result = $markdown->parse($result); + } elseif (class_exists('dflydev\markdown\MarkdownExtraParser')) { + $markdown = new \dflydev\markdown\MarkdownExtraParser(); + $result = $markdown->transformMarkdown($result); + } + + return trim($result); + } + + /** + * Gets the docblock this tag belongs to. + * + * @return DocBlock The docblock this description belongs to. + */ + public function getDocBlock() + { + return $this->docblock; + } + + /** + * Sets the docblock this tag belongs to. + * + * @param DocBlock $docblock The new docblock this description belongs to. + * Setting NULL removes any association. + * + * @return $this + */ + public function setDocBlock(DocBlock $docblock = null) + { + $this->docblock = $docblock; + + return $this; + } + + /** + * Builds a string representation of this object. + * + * @todo determine the exact format as used by PHP Reflection + * and implement it. + * + * @return void + * @codeCoverageIgnore Not yet implemented + */ + public static function export() + { + throw new \Exception('Not yet implemented'); + } + + /** + * Returns the long description as a string. + * + * @return string + */ + public function __toString() + { + return $this->getContents(); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Location.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Location.php new file mode 100755 index 000000000..966ed44d7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Location.php @@ -0,0 +1,76 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +/** + * The location a DocBlock occurs within a file. + * + * @author Vasil Rangelov + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class Location +{ + /** @var int Line where the DocBlock text starts. */ + protected $lineNumber = 0; + + /** @var int Column where the DocBlock text starts. */ + protected $columnNumber = 0; + + public function __construct( + $lineNumber = 0, + $columnNumber = 0 + ) { + $this->setLineNumber($lineNumber)->setColumnNumber($columnNumber); + } + + /** + * @return int Line where the DocBlock text starts. + */ + public function getLineNumber() + { + return $this->lineNumber; + } + + /** + * + * @param type $lineNumber + * @return $this + */ + public function setLineNumber($lineNumber) + { + $this->lineNumber = (int)$lineNumber; + + return $this; + } + + /** + * @return int Column where the DocBlock text starts. + */ + public function getColumnNumber() + { + return $this->columnNumber; + } + + /** + * + * @param int $columnNumber + * @return $this + */ + public function setColumnNumber($columnNumber) + { + $this->columnNumber = (int)$columnNumber; + + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Serializer.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Serializer.php new file mode 100755 index 000000000..c1617850e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Serializer.php @@ -0,0 +1,198 @@ + + * @copyright 2013 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock; + +/** + * Serializes a DocBlock instance. + * + * @author Barry vd. Heuvel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class Serializer +{ + + /** @var string The string to indent the comment with. */ + protected $indentString = ' '; + + /** @var int The number of times the indent string is repeated. */ + protected $indent = 0; + + /** @var bool Whether to indent the first line. */ + protected $isFirstLineIndented = true; + + /** @var int|null The max length of a line. */ + protected $lineLength = null; + + /** + * Create a Serializer instance. + * + * @param int $indent The number of times the indent string is + * repeated. + * @param string $indentString The string to indent the comment with. + * @param bool $indentFirstLine Whether to indent the first line. + * @param int|null $lineLength The max length of a line or NULL to + * disable line wrapping. + */ + public function __construct( + $indent = 0, + $indentString = ' ', + $indentFirstLine = true, + $lineLength = null + ) { + $this->setIndentationString($indentString); + $this->setIndent($indent); + $this->setIsFirstLineIndented($indentFirstLine); + $this->setLineLength($lineLength); + } + + /** + * Sets the string to indent comments with. + * + * @param string $indentationString The string to indent comments with. + * + * @return $this This serializer object. + */ + public function setIndentationString($indentString) + { + $this->indentString = (string)$indentString; + return $this; + } + + /** + * Gets the string to indent comments with. + * + * @return string The indent string. + */ + public function getIndentationString() + { + return $this->indentString; + } + + /** + * Sets the number of indents. + * + * @param int $indent The number of times the indent string is repeated. + * + * @return $this This serializer object. + */ + public function setIndent($indent) + { + $this->indent = (int)$indent; + return $this; + } + + /** + * Gets the number of indents. + * + * @return int The number of times the indent string is repeated. + */ + public function getIndent() + { + return $this->indent; + } + + /** + * Sets whether or not the first line should be indented. + * + * Sets whether or not the first line (the one with the "/**") should be + * indented. + * + * @param bool $indentFirstLine The new value for this setting. + * + * @return $this This serializer object. + */ + public function setIsFirstLineIndented($indentFirstLine) + { + $this->isFirstLineIndented = (bool)$indentFirstLine; + return $this; + } + + /** + * Gets whether or not the first line should be indented. + * + * @return bool Whether or not the first line should be indented. + */ + public function isFirstLineIndented() + { + return $this->isFirstLineIndented; + } + + /** + * Sets the line length. + * + * Sets the length of each line in the serialization. Content will be + * wrapped within this limit. + * + * @param int|null $lineLength The length of each line. NULL to disable line + * wrapping altogether. + * + * @return $this This serializer object. + */ + public function setLineLength($lineLength) + { + $this->lineLength = null === $lineLength ? null : (int)$lineLength; + return $this; + } + + /** + * Gets the line length. + * + * @return int|null The length of each line or NULL if line wrapping is + * disabled. + */ + public function getLineLength() + { + return $this->lineLength; + } + + /** + * Generate a DocBlock comment. + * + * @param DocBlock The DocBlock to serialize. + * + * @return string The serialized doc block. + */ + public function getDocComment(DocBlock $docblock) + { + $indent = str_repeat($this->indentString, $this->indent); + $firstIndent = $this->isFirstLineIndented ? $indent : ''; + + $text = $docblock->getText(); + if ($this->lineLength) { + //3 === strlen(' * ') + $wrapLength = $this->lineLength - strlen($indent) - 3; + $text = wordwrap($text, $wrapLength); + } + $text = str_replace("\n", "\n{$indent} * ", $text); + + $comment = "{$firstIndent}/**\n{$indent} * {$text}\n{$indent} *\n"; + + /** @var Tag $tag */ + foreach ($docblock->getTags() as $tag) { + $tagText = (string) $tag; + if ($this->lineLength) { + $tagText = wordwrap($tagText, $wrapLength); + } + $tagText = str_replace("\n", "\n{$indent} * ", $tagText); + + $comment .= "{$indent} * {$tagText}\n"; + } + + $comment .= $indent . ' */'; + + return $comment; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag.php new file mode 100755 index 000000000..a96db0952 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag.php @@ -0,0 +1,377 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock; + +/** + * Parses a tag definition for a DocBlock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class Tag implements \Reflector +{ + /** + * PCRE regular expression matching a tag name. + */ + const REGEX_TAGNAME = '[\w\-\_\\\\]+'; + + /** @var string Name of the tag */ + protected $tag = ''; + + /** + * @var string|null Content of the tag. + * When set to NULL, it means it needs to be regenerated. + */ + protected $content = ''; + + /** @var string Description of the content of this tag */ + protected $description = ''; + + /** + * @var array|null The description, as an array of strings and Tag objects. + * When set to NULL, it means it needs to be regenerated. + */ + protected $parsedDescription = null; + + /** @var Location Location of the tag. */ + protected $location = null; + + /** @var DocBlock The DocBlock which this tag belongs to. */ + protected $docblock = null; + + /** + * @var array An array with a tag as a key, and an FQCN to a class that + * handles it as an array value. The class is expected to inherit this + * class. + */ + private static $tagHandlerMappings = array( + 'author' + => '\phpDocumentor\Reflection\DocBlock\Tag\AuthorTag', + 'covers' + => '\phpDocumentor\Reflection\DocBlock\Tag\CoversTag', + 'deprecated' + => '\phpDocumentor\Reflection\DocBlock\Tag\DeprecatedTag', + 'example' + => '\phpDocumentor\Reflection\DocBlock\Tag\ExampleTag', + 'link' + => '\phpDocumentor\Reflection\DocBlock\Tag\LinkTag', + 'method' + => '\phpDocumentor\Reflection\DocBlock\Tag\MethodTag', + 'param' + => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', + 'property-read' + => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyReadTag', + 'property' + => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyTag', + 'property-write' + => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyWriteTag', + 'return' + => '\phpDocumentor\Reflection\DocBlock\Tag\ReturnTag', + 'see' + => '\phpDocumentor\Reflection\DocBlock\Tag\SeeTag', + 'since' + => '\phpDocumentor\Reflection\DocBlock\Tag\SinceTag', + 'source' + => '\phpDocumentor\Reflection\DocBlock\Tag\SourceTag', + 'throw' + => '\phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag', + 'throws' + => '\phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag', + 'uses' + => '\phpDocumentor\Reflection\DocBlock\Tag\UsesTag', + 'var' + => '\phpDocumentor\Reflection\DocBlock\Tag\VarTag', + 'version' + => '\phpDocumentor\Reflection\DocBlock\Tag\VersionTag' + ); + + /** + * Factory method responsible for instantiating the correct sub type. + * + * @param string $tag_line The text for this tag, including description. + * @param DocBlock $docblock The DocBlock which this tag belongs to. + * @param Location $location Location of the tag. + * + * @throws \InvalidArgumentException if an invalid tag line was presented. + * + * @return static A new tag object. + */ + final public static function createInstance( + $tag_line, + DocBlock $docblock = null, + Location $location = null + ) { + if (!preg_match( + '/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us', + $tag_line, + $matches + )) { + throw new \InvalidArgumentException( + 'Invalid tag_line detected: ' . $tag_line + ); + } + + $handler = __CLASS__; + if (isset(self::$tagHandlerMappings[$matches[1]])) { + $handler = self::$tagHandlerMappings[$matches[1]]; + } elseif (isset($docblock)) { + $tagName = (string)new Type\Collection( + array($matches[1]), + $docblock->getContext() + ); + + if (isset(self::$tagHandlerMappings[$tagName])) { + $handler = self::$tagHandlerMappings[$tagName]; + } + } + + return new $handler( + $matches[1], + isset($matches[2]) ? $matches[2] : '', + $docblock, + $location + ); + } + + /** + * Registers a handler for tags. + * + * Registers a handler for tags. The class specified is autoloaded if it's + * not available. It must inherit from this class. + * + * @param string $tag Name of tag to regiser a handler for. When + * registering a namespaced tag, the full name, along with a prefixing + * slash MUST be provided. + * @param string|null $handler FQCN of handler. Specifing NULL removes the + * handler for the specified tag, if any. + * + * @return bool TRUE on success, FALSE on failure. + */ + final public static function registerTagHandler($tag, $handler) + { + $tag = trim((string)$tag); + + if (null === $handler) { + unset(self::$tagHandlerMappings[$tag]); + return true; + } + + if ('' !== $tag + && class_exists($handler, true) + && is_subclass_of($handler, __CLASS__) + && !strpos($tag, '\\') //Accept no slash, and 1st slash at offset 0. + ) { + self::$tagHandlerMappings[$tag] = $handler; + return true; + } + + return false; + } + + /** + * Parses a tag and populates the member variables. + * + * @param string $name Name of the tag. + * @param string $content The contents of the given tag. + * @param DocBlock $docblock The DocBlock which this tag belongs to. + * @param Location $location Location of the tag. + */ + public function __construct( + $name, + $content, + DocBlock $docblock = null, + Location $location = null + ) { + $this + ->setName($name) + ->setContent($content) + ->setDocBlock($docblock) + ->setLocation($location); + } + + /** + * Gets the name of this tag. + * + * @return string The name of this tag. + */ + public function getName() + { + return $this->tag; + } + + /** + * Sets the name of this tag. + * + * @param string $name The new name of this tag. + * + * @return $this + * @throws \InvalidArgumentException When an invalid tag name is provided. + */ + public function setName($name) + { + if (!preg_match('/^' . self::REGEX_TAGNAME . '$/u', $name)) { + throw new \InvalidArgumentException( + 'Invalid tag name supplied: ' . $name + ); + } + + $this->tag = $name; + + return $this; + } + + /** + * Gets the content of this tag. + * + * @return string + */ + public function getContent() + { + if (null === $this->content) { + $this->content = $this->description; + } + + return $this->content; + } + + /** + * Sets the content of this tag. + * + * @param string $content The new content of this tag. + * + * @return $this + */ + public function setContent($content) + { + $this->setDescription($content); + $this->content = $content; + + return $this; + } + + /** + * Gets the description component of this tag. + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the description component of this tag. + * + * @param string $description The new description component of this tag. + * + * @return $this + */ + public function setDescription($description) + { + $this->content = null; + $this->parsedDescription = null; + $this->description = trim($description); + + return $this; + } + + /** + * Gets the parsed text of this description. + * + * @return array An array of strings and tag objects, in the order they + * occur within the description. + */ + public function getParsedDescription() + { + if (null === $this->parsedDescription) { + $description = new Description($this->description, $this->docblock); + $this->parsedDescription = $description->getParsedContents(); + } + return $this->parsedDescription; + } + + /** + * Gets the docblock this tag belongs to. + * + * @return DocBlock The docblock this tag belongs to. + */ + public function getDocBlock() + { + return $this->docblock; + } + + /** + * Sets the docblock this tag belongs to. + * + * @param DocBlock $docblock The new docblock this tag belongs to. Setting + * NULL removes any association. + * + * @return $this + */ + public function setDocBlock(DocBlock $docblock = null) + { + $this->docblock = $docblock; + + return $this; + } + + /** + * Gets the location of the tag. + * + * @return Location The tag's location. + */ + public function getLocation() + { + return $this->location; + } + + /** + * Sets the location of the tag. + * + * @param Location $location The new location of the tag. + * + * @return $this + */ + public function setLocation(Location $location = null) + { + $this->location = $location; + + return $this; + } + + /** + * Builds a string representation of this object. + * + * @todo determine the exact format as used by PHP Reflection and implement it. + * + * @return void + * @codeCoverageIgnore Not yet implemented + */ + public static function export() + { + throw new \Exception('Not yet implemented'); + } + + /** + * Returns the tag as a serialized string + * + * @return string + */ + public function __toString() + { + return "@{$this->getName()} {$this->getContent()}"; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.php new file mode 100755 index 000000000..bacf52ebe --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.php @@ -0,0 +1,131 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for an @author tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class AuthorTag extends Tag +{ + /** + * PCRE regular expression matching any valid value for the name component. + */ + const REGEX_AUTHOR_NAME = '[^\<]*'; + + /** + * PCRE regular expression matching any valid value for the email component. + */ + const REGEX_AUTHOR_EMAIL = '[^\>]*'; + + /** @var string The name of the author */ + protected $authorName = ''; + + /** @var string The email of the author */ + protected $authorEmail = ''; + + public function getContent() + { + if (null === $this->content) { + $this->content = $this->authorName; + if ('' != $this->authorEmail) { + $this->content .= "<{$this->authorEmail}>"; + } + } + + return $this->content; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + parent::setContent($content); + if (preg_match( + '/^(' . self::REGEX_AUTHOR_NAME . + ')(\<(' . self::REGEX_AUTHOR_EMAIL . + ')\>)?$/u', + $this->description, + $matches + )) { + $this->authorName = trim($matches[1]); + if (isset($matches[3])) { + $this->authorEmail = trim($matches[3]); + } + } + + return $this; + } + + /** + * Gets the author's name. + * + * @return string The author's name. + */ + public function getAuthorName() + { + return $this->authorName; + } + + /** + * Sets the author's name. + * + * @param string $authorName The new author name. + * An invalid value will set an empty string. + * + * @return $this + */ + public function setAuthorName($authorName) + { + $this->content = null; + $this->authorName + = preg_match('/^' . self::REGEX_AUTHOR_NAME . '$/u', $authorName) + ? $authorName : ''; + + return $this; + } + + /** + * Gets the author's email. + * + * @return string The author's email. + */ + public function getAuthorEmail() + { + return $this->authorEmail; + } + + /** + * Sets the author's email. + * + * @param string $authorEmail The new author email. + * An invalid value will set an empty string. + * + * @return $this + */ + public function setAuthorEmail($authorEmail) + { + $this->authorEmail + = preg_match('/^' . self::REGEX_AUTHOR_EMAIL . '$/u', $authorEmail) + ? $authorEmail : ''; + + $this->content = null; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.php new file mode 100755 index 000000000..bd31b56bf --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.php @@ -0,0 +1,24 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @covers tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class CoversTag extends SeeTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php new file mode 100755 index 000000000..7226316b7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php @@ -0,0 +1,26 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag\VersionTag; + +/** + * Reflection class for a @deprecated tag in a Docblock. + * + * @author Vasil Rangelov + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class DeprecatedTag extends VersionTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php new file mode 100755 index 000000000..0e163ea01 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php @@ -0,0 +1,156 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @example tag in a Docblock. + * + * @author Vasil Rangelov + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ExampleTag extends SourceTag +{ + /** + * @var string Path to a file to use as an example. + * May also be an absolute URI. + */ + protected $filePath = ''; + + /** + * @var bool Whether the file path component represents an URI. + * This determines how the file portion appears at {@link getContent()}. + */ + protected $isURI = false; + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->content) { + $filePath = ''; + if ($this->isURI) { + if (false === strpos($this->filePath, ':')) { + $filePath = str_replace( + '%2F', + '/', + rawurlencode($this->filePath) + ); + } else { + $filePath = $this->filePath; + } + } else { + $filePath = '"' . $this->filePath . '"'; + } + + $this->content = $filePath . ' ' . parent::getContent(); + } + + return $this->content; + } + /** + * {@inheritdoc} + */ + public function setContent($content) + { + Tag::setContent($content); + if (preg_match( + '/^ + # File component + (?: + # File path in quotes + \"([^\"]+)\" + | + # File URI + (\S+) + ) + # Remaining content (parsed by SourceTag) + (?:\s+(.*))? + $/sux', + $this->description, + $matches + )) { + if ('' !== $matches[1]) { + $this->setFilePath($matches[1]); + } else { + $this->setFileURI($matches[2]); + } + + if (isset($matches[3])) { + parent::setContent($matches[3]); + } else { + $this->setDescription(''); + } + $this->content = $content; + } + + return $this; + } + + /** + * Returns the file path. + * + * @return string Path to a file to use as an example. + * May also be an absolute URI. + */ + public function getFilePath() + { + return $this->filePath; + } + + /** + * Sets the file path. + * + * @param string $filePath The new file path to use for the example. + * + * @return $this + */ + public function setFilePath($filePath) + { + $this->isURI = false; + $this->filePath = trim($filePath); + + $this->content = null; + return $this; + } + + /** + * Sets the file path as an URI. + * + * This function is equivalent to {@link setFilePath()}, except that it + * convers an URI to a file path before that. + * + * There is no getFileURI(), as {@link getFilePath()} is compatible. + * + * @param type $uri The new file URI to use as an example. + */ + public function setFileURI($uri) + { + $this->isURI = true; + if (false === strpos($uri, ':')) { + //Relative URL + $this->filePath = rawurldecode( + str_replace(array('/', '\\'), '%2F', $uri) + ); + } else { + //Absolute URL or URI. + $this->filePath = $uri; + } + + $this->content = null; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.php new file mode 100755 index 000000000..f79f25dd8 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.php @@ -0,0 +1,81 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @link tag in a Docblock. + * + * @author Ben Selby + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class LinkTag extends Tag +{ + /** @var string */ + protected $link = ''; + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->content) { + $this->content = "{$this->link} {$this->description}"; + } + + return $this->content; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + parent::setContent($content); + $parts = preg_split('/\s+/Su', $this->description, 2); + + $this->link = $parts[0]; + + $this->setDescription(isset($parts[1]) ? $parts[1] : $parts[0]); + + $this->content = $content; + return $this; + } + + /** + * Gets the link + * + * @return string + */ + public function getLink() + { + return $this->link; + } + + /** + * Sets the link + * + * @param string $link The link + * + * @return $this + */ + public function setLink($link) + { + $this->link = $link; + + $this->content = null; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php new file mode 100755 index 000000000..7a5ce7908 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php @@ -0,0 +1,209 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @method in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class MethodTag extends ReturnTag +{ + + /** @var string */ + protected $method_name = ''; + + /** @var string */ + protected $arguments = ''; + + /** @var bool */ + protected $isStatic = false; + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->content) { + $this->content = ''; + if ($this->isStatic) { + $this->content .= 'static '; + } + $this->content .= $this->type . + " {$this->method_name}({$this->arguments}) " . + $this->description; + } + + return $this->content; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + Tag::setContent($content); + // 1. none or more whitespace + // 2. optionally the keyword "static" followed by whitespace + // 3. optionally a word with underscores followed by whitespace : as + // type for the return value + // 4. then optionally a word with underscores followed by () and + // whitespace : as method name as used by phpDocumentor + // 5. then a word with underscores, followed by ( and any character + // until a ) and whitespace : as method name with signature + // 6. any remaining text : as description + if (preg_match( + '/^ + # Static keyword + # Declates a static method ONLY if type is also present + (?: + (static) + \s+ + )? + # Return type + (?: + ([\w\|_\\\\]+) + \s+ + )? + # Legacy method name (not captured) + (?: + [\w_]+\(\)\s+ + )? + # Method name + ([\w\|_\\\\]+) + # Arguments + \(([^\)]*)\) + \s* + # Description + (.*) + $/sux', + $this->description, + $matches + )) { + list( + , + $static, + $this->type, + $this->method_name, + $this->arguments, + $this->description + ) = $matches; + if ($static) { + if (!$this->type) { + $this->type = 'static'; + } else { + $this->isStatic = true; + } + } else { + if (!$this->type) { + $this->type = 'void'; + } + } + $this->parsedDescription = null; + } + + return $this; + } + + /** + * Sets the name of this method. + * + * @param string $method_name The name of the method. + * + * @return $this + */ + public function setMethodName($method_name) + { + $this->method_name = $method_name; + + $this->content = null; + return $this; + } + + /** + * Retrieves the method name. + * + * @return string + */ + public function getMethodName() + { + return $this->method_name; + } + + /** + * Sets the arguments for this method. + * + * @param string $arguments A comma-separated arguments line. + * + * @return void + */ + public function setArguments($arguments) + { + $this->arguments = $arguments; + + $this->content = null; + return $this; + } + + /** + * Returns an array containing each argument as array of type and name. + * + * Please note that the argument sub-array may only contain 1 element if no + * type was specified. + * + * @return string[] + */ + public function getArguments() + { + if (empty($this->arguments)) { + return array(); + } + + $arguments = explode(',', $this->arguments); + foreach ($arguments as $key => $value) { + $arguments[$key] = explode(' ', trim($value)); + } + + return $arguments; + } + + /** + * Checks whether the method tag describes a static method or not. + * + * @return bool TRUE if the method declaration is for a static method, FALSE + * otherwise. + */ + public function isStatic() + { + return $this->isStatic; + } + + /** + * Sets a new value for whether the method is static or not. + * + * @param bool $isStatic The new value to set. + * + * @return $this + */ + public function setIsStatic($isStatic) + { + $this->isStatic = $isStatic; + + $this->content = null; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php new file mode 100755 index 000000000..9bc0270dd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.php @@ -0,0 +1,119 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @param tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ParamTag extends ReturnTag +{ + /** @var string */ + protected $variableName = ''; + + /** @var bool determines whether this is a variadic argument */ + protected $isVariadic = false; + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->content) { + $this->content + = "{$this->type} {$this->variableName} {$this->description}"; + } + return $this->content; + } + /** + * {@inheritdoc} + */ + public function setContent($content) + { + Tag::setContent($content); + $parts = preg_split( + '/(\s+)/Su', + $this->description, + 3, + PREG_SPLIT_DELIM_CAPTURE + ); + + // if the first item that is encountered is not a variable; it is a type + if (isset($parts[0]) + && (strlen($parts[0]) > 0) + && ($parts[0][0] !== '$') + ) { + $this->type = array_shift($parts); + array_shift($parts); + } + + // if the next item starts with a $ or ...$ it must be the variable name + if (isset($parts[0]) + && (strlen($parts[0]) > 0) + && ($parts[0][0] == '$' || substr($parts[0], 0, 4) === '...$') + ) { + $this->variableName = array_shift($parts); + array_shift($parts); + + if (substr($this->variableName, 0, 3) === '...') { + $this->isVariadic = true; + $this->variableName = substr($this->variableName, 3); + } + } + + $this->setDescription(implode('', $parts)); + + $this->content = $content; + return $this; + } + + /** + * Returns the variable's name. + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * Sets the variable's name. + * + * @param string $name The new name for this variable. + * + * @return $this + */ + public function setVariableName($name) + { + $this->variableName = $name; + + $this->content = null; + return $this; + } + + /** + * Returns whether this tag is variadic. + * + * @return boolean + */ + public function isVariadic() + { + return $this->isVariadic; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php new file mode 100755 index 000000000..33406026a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php @@ -0,0 +1,24 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @property-read tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class PropertyReadTag extends PropertyTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.php new file mode 100755 index 000000000..288ecff87 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.php @@ -0,0 +1,24 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @property tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class PropertyTag extends ParamTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php new file mode 100755 index 000000000..ec4e866d4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php @@ -0,0 +1,24 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @property-write tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class PropertyWriteTag extends PropertyTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php new file mode 100755 index 000000000..9293db924 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.php @@ -0,0 +1,99 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Type\Collection; + +/** + * Reflection class for a @return tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ReturnTag extends Tag +{ + /** @var string The raw type component. */ + protected $type = ''; + + /** @var Collection The parsed type component. */ + protected $types = null; + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->content) { + $this->content = "{$this->type} {$this->description}"; + } + + return $this->content; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + parent::setContent($content); + + $parts = preg_split('/\s+/Su', $this->description, 2); + + // any output is considered a type + $this->type = $parts[0]; + $this->types = null; + + $this->setDescription(isset($parts[1]) ? $parts[1] : ''); + + $this->content = $content; + return $this; + } + + /** + * Returns the unique types of the variable. + * + * @return string[] + */ + public function getTypes() + { + return $this->getTypesCollection()->getArrayCopy(); + } + + /** + * Returns the type section of the variable. + * + * @return string + */ + public function getType() + { + return (string) $this->getTypesCollection(); + } + + /** + * Returns the type collection. + * + * @return void + */ + protected function getTypesCollection() + { + if (null === $this->types) { + $this->types = new Collection( + array($this->type), + $this->docblock ? $this->docblock->getContext() : null + ); + } + return $this->types; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.php new file mode 100755 index 000000000..4f5f22ce1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.php @@ -0,0 +1,81 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @see tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class SeeTag extends Tag +{ + /** @var string */ + protected $refers = null; + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->content) { + $this->content = "{$this->refers} {$this->description}"; + } + return $this->content; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + parent::setContent($content); + $parts = preg_split('/\s+/Su', $this->description, 2); + + // any output is considered a type + $this->refers = $parts[0]; + + $this->setDescription(isset($parts[1]) ? $parts[1] : ''); + + $this->content = $content; + return $this; + } + + /** + * Gets the structural element this tag refers to. + * + * @return string + */ + public function getReference() + { + return $this->refers; + } + + /** + * Sets the structural element this tag refers to. + * + * @param string $refers The new type this tag refers to. + * + * @return $this + */ + public function setReference($refers) + { + $this->refers = $refers; + + $this->content = null; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php new file mode 100755 index 000000000..ba009c447 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php @@ -0,0 +1,26 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag\VersionTag; + +/** + * Reflection class for a @since tag in a Docblock. + * + * @author Vasil Rangelov + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class SinceTag extends VersionTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php new file mode 100755 index 000000000..3400220ea --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php @@ -0,0 +1,137 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @source tag in a Docblock. + * + * @author Vasil Rangelov + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class SourceTag extends Tag +{ + /** + * @var int The starting line, relative to the structural element's + * location. + */ + protected $startingLine = 1; + + /** + * @var int|null The number of lines, relative to the starting line. NULL + * means "to the end". + */ + protected $lineCount = null; + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->content) { + $this->content + = "{$this->startingLine} {$this->lineCount} {$this->description}"; + } + + return $this->content; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + parent::setContent($content); + if (preg_match( + '/^ + # Starting line + ([1-9]\d*) + \s* + # Number of lines + (?: + ((?1)) + \s+ + )? + # Description + (.*) + $/sux', + $this->description, + $matches + )) { + $this->startingLine = (int)$matches[1]; + if (isset($matches[2]) && '' !== $matches[2]) { + $this->lineCount = (int)$matches[2]; + } + $this->setDescription($matches[3]); + $this->content = $content; + } + + return $this; + } + + /** + * Gets the starting line. + * + * @return int The starting line, relative to the structural element's + * location. + */ + public function getStartingLine() + { + return $this->startingLine; + } + + /** + * Sets the starting line. + * + * @param int $startingLine The new starting line, relative to the + * structural element's location. + * + * @return $this + */ + public function setStartingLine($startingLine) + { + $this->startingLine = $startingLine; + + $this->content = null; + return $this; + } + + /** + * Returns the number of lines. + * + * @return int|null The number of lines, relative to the starting line. NULL + * means "to the end". + */ + public function getLineCount() + { + return $this->lineCount; + } + + /** + * Sets the number of lines. + * + * @param int|null $lineCount The new number of lines, relative to the + * starting line. NULL means "to the end". + * + * @return $this + */ + public function setLineCount($lineCount) + { + $this->lineCount = $lineCount; + + $this->content = null; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.php new file mode 100755 index 000000000..58ee44a42 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.php @@ -0,0 +1,24 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @throws tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ThrowsTag extends ReturnTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.php new file mode 100755 index 000000000..da0d66381 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.php @@ -0,0 +1,24 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @uses tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class UsesTag extends SeeTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VarTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VarTag.php new file mode 100755 index 000000000..236b2c8b0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VarTag.php @@ -0,0 +1,24 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @var tag in a Docblock. + * + * @author Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class VarTag extends ParamTag +{ +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.php new file mode 100755 index 000000000..260f6984f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.php @@ -0,0 +1,108 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +use phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Reflection class for a @version tag in a Docblock. + * + * @author Vasil Rangelov + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class VersionTag extends Tag +{ + /** + * PCRE regular expression matching a version vector. + * Assumes the "x" modifier. + */ + const REGEX_VECTOR = '(?: + # Normal release vectors. + \d\S* + | + # VCS version vectors. Per PHPCS, they are expected to + # follow the form of the VCS name, followed by ":", followed + # by the version vector itself. + # By convention, popular VCSes like CVS, SVN and GIT use "$" + # around the actual version vector. + [^\s\:]+\:\s*\$[^\$]+\$ + )'; + + /** @var string The version vector. */ + protected $version = ''; + + public function getContent() + { + if (null === $this->content) { + $this->content = "{$this->version} {$this->description}"; + } + + return $this->content; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + parent::setContent($content); + + if (preg_match( + '/^ + # The version vector + (' . self::REGEX_VECTOR . ') + \s* + # The description + (.+)? + $/sux', + $this->description, + $matches + )) { + $this->version = $matches[1]; + $this->setDescription(isset($matches[2]) ? $matches[2] : ''); + $this->content = $content; + } + + return $this; + } + + /** + * Gets the version section of the tag. + * + * @return string The version section of the tag. + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the version section of the tag. + * + * @param string $version The new version section of the tag. + * An invalid value will set an empty string. + * + * @return $this + */ + public function setVersion($version) + { + $this->version + = preg_match('/^' . self::REGEX_VECTOR . '$/ux', $version) + ? $version + : ''; + + $this->content = null; + return $this; + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Type/Collection.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Type/Collection.php new file mode 100755 index 000000000..90ead3ff4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock/Type/Collection.php @@ -0,0 +1,221 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Type; + +use phpDocumentor\Reflection\DocBlock\Context; + +/** + * Collection + * + * @author Mike van Riel + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class Collection extends \ArrayObject +{ + /** @var string Definition of the OR operator for types */ + const OPERATOR_OR = '|'; + + /** @var string Definition of the ARRAY operator for types */ + const OPERATOR_ARRAY = '[]'; + + /** @var string Definition of the NAMESPACE operator in PHP */ + const OPERATOR_NAMESPACE = '\\'; + + /** @var string[] List of recognized keywords */ + protected static $keywords = array( + 'string', 'int', 'integer', 'bool', 'boolean', 'float', 'double', + 'object', 'mixed', 'array', 'resource', 'void', 'null', 'scalar', + 'callback', 'callable', 'false', 'true', 'self', '$this', 'static' + ); + + /** + * Current invoking location. + * + * This is used to prepend to type with a relative location. + * May also be 'default' or 'global', in which case they are ignored. + * + * @var Context + */ + protected $context = null; + + /** + * Registers the namespace and aliases; uses that to add and expand the + * given types. + * + * @param string[] $types Array containing a list of types to add to this + * container. + * @param Context $location The current invoking location. + */ + public function __construct( + array $types = array(), + Context $context = null + ) { + $this->context = null === $context ? new Context() : $context; + + foreach ($types as $type) { + $this->add($type); + } + } + + /** + * Returns the current invoking location. + * + * @return Context + */ + public function getContext() + { + return $this->context; + } + + /** + * Adds a new type to the collection and expands it if it contains a + * relative namespace. + * + * If a class in the type contains a relative namespace than this collection + * will try to expand that into a FQCN. + * + * @param string $type A 'Type' as defined in the phpDocumentor + * documentation. + * + * @throws \InvalidArgumentException if a non-string argument is passed. + * + * @see http://phpdoc.org/docs/latest/for-users/types.html for the + * definition of a type. + * + * @return void + */ + public function add($type) + { + if (!is_string($type)) { + throw new \InvalidArgumentException( + 'A type should be represented by a string, received: ' + .var_export($type, true) + ); + } + + // separate the type by the OR operator + $type_parts = explode(self::OPERATOR_OR, $type); + foreach ($type_parts as $part) { + $expanded_type = $this->expand($part); + if ($expanded_type) { + $this[] = $expanded_type; + } + } + } + + /** + * Returns a string representation of the collection. + * + * @return string The resolved types across the collection, separated with + * {@link self::OPERATOR_OR}. + */ + public function __toString() + { + return implode(self::OPERATOR_OR, $this->getArrayCopy()); + } + + /** + * Analyzes the given type and returns the FQCN variant. + * + * When a type is provided this method checks whether it is not a keyword or + * Fully Qualified Class Name. If so it will use the given namespace and + * aliases to expand the type to a FQCN representation. + * + * This method only works as expected if the namespace and aliases are set; + * no dynamic reflection is being performed here. + * + * @param string $type The relative or absolute type. + * + * @uses getNamespace to determine with what to prefix the type name. + * @uses getNamespaceAliases to check whether the first part of the relative + * type name should not be replaced with another namespace. + * + * @return string + */ + protected function expand($type) + { + $type = trim($type); + if (!$type) { + return ''; + } + + if ($this->isTypeAnArray($type)) { + return $this->expand(substr($type, 0, -2)) . self::OPERATOR_ARRAY; + } + + if ($this->isRelativeType($type) && !$this->isTypeAKeyword($type)) { + $type_parts = explode(self::OPERATOR_NAMESPACE, $type, 2); + + $namespace_aliases = $this->context->getNamespaceAliases(); + // if the first segment is not an alias; prepend namespace name and + // return + if (!isset($namespace_aliases[$type_parts[0]])) { + $namespace = $this->context->getNamespace(); + if ('' !== $namespace) { + $namespace .= self::OPERATOR_NAMESPACE; + } + return self::OPERATOR_NAMESPACE . $namespace . $type; + } + + $type_parts[0] = $namespace_aliases[$type_parts[0]]; + $type = implode(self::OPERATOR_NAMESPACE, $type_parts); + } + + return $type; + } + + /** + * Detects whether the given type represents an array. + * + * @param string $type A relative or absolute type as defined in the + * phpDocumentor documentation. + * + * @return bool + */ + protected function isTypeAnArray($type) + { + return substr($type, -2) === self::OPERATOR_ARRAY; + } + + /** + * Detects whether the given type represents a PHPDoc keyword. + * + * @param string $type A relative or absolute type as defined in the + * phpDocumentor documentation. + * + * @return bool + */ + protected function isTypeAKeyword($type) + { + return in_array(strtolower($type), static::$keywords, true); + } + + /** + * Detects whether the given type represents a relative or absolute path. + * + * This method will detect keywords as being absolute; even though they are + * not preceeded by a namespace separator. + * + * @param string $type A relative or absolute type as defined in the + * phpDocumentor documentation. + * + * @return bool + */ + protected function isRelativeType($type) + { + return ($type[0] !== self::OPERATOR_NAMESPACE) + || $this->isTypeAKeyword($type); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/DescriptionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/DescriptionTest.php new file mode 100755 index 000000000..a6ca7b37e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/DescriptionTest.php @@ -0,0 +1,245 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Description + * + * @author Vasil Rangelov + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class DescriptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstruct() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(1, $parsedContents); + $this->assertSame($fixture, $parsedContents[0]); + } + + public function testInlineTagParsing() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(3, $parsedContents); + $this->assertSame('This is text for a ', $parsedContents[0]); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag\LinkTag', + $parsedContents[1] + ); + $this->assertSame( + ' that uses inline +tags.', + $parsedContents[2] + ); + } + + public function testInlineTagAtStartParsing() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(3, $parsedContents); + + $this->assertSame('', $parsedContents[0]); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag\LinkTag', + $parsedContents[1] + ); + $this->assertSame( + ' is text for a description that uses inline +tags.', + $parsedContents[2] + ); + } + + public function testNestedInlineTagParsing() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(3, $parsedContents); + + $this->assertSame( + 'This is text for a description with ', + $parsedContents[0] + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $parsedContents[1] + ); + $this->assertSame('.', $parsedContents[2]); + + $parsedDescription = $parsedContents[1]->getParsedDescription(); + $this->assertCount(3, $parsedDescription); + $this->assertSame("inline tag with\n", $parsedDescription[0]); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag\LinkTag', + $parsedDescription[1] + ); + $this->assertSame(' in it', $parsedDescription[2]); + } + + public function testLiteralOpeningDelimiter() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(1, $parsedContents); + $this->assertSame($fixture, $parsedContents[0]); + } + + public function testNestedLiteralOpeningDelimiter() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(3, $parsedContents); + $this->assertSame( + 'This is text for a description containing ', + $parsedContents[0] + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $parsedContents[1] + ); + $this->assertSame('.', $parsedContents[2]); + + $this->assertSame( + array('inline tag that has { that +is literal'), + $parsedContents[1]->getParsedDescription() + ); + } + + public function testLiteralClosingDelimiter() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(1, $parsedContents); + $this->assertSame( + 'This is text for a description with } that is not a tag.', + $parsedContents[0] + ); + } + + public function testNestedLiteralClosingDelimiter() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(3, $parsedContents); + $this->assertSame( + 'This is text for a description with ', + $parsedContents[0] + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $parsedContents[1] + ); + $this->assertSame('.', $parsedContents[2]); + + $this->assertSame( + array('inline tag with } that is not an +inline tag'), + $parsedContents[1]->getParsedDescription() + ); + } + + public function testInlineTagEscapingSequence() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(1, $parsedContents); + $this->assertSame( + 'This is text for a description with literal {@link}.', + $parsedContents[0] + ); + } + + public function testNestedInlineTagEscapingSequence() + { + $fixture = <<assertSame($fixture, $object->getContents()); + + $parsedContents = $object->getParsedContents(); + $this->assertCount(3, $parsedContents); + $this->assertSame( + 'This is text for a description with an ', + $parsedContents[0] + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $parsedContents[1] + ); + $this->assertSame('.', $parsedContents[2]); + + $this->assertSame( + array('inline tag with literal +{@link} in it'), + $parsedContents[1]->getParsedDescription() + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/CoversTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/CoversTagTest.php new file mode 100755 index 000000000..ff257aa19 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/CoversTagTest.php @@ -0,0 +1,86 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\CoversTag + * + * @author Daniel O'Connor + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class CoversTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\CoversTag can create + * a link for the covers doc block. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exReference + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\CoversTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exReference + ) { + $tag = new CoversTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exReference, $tag->getReference()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exReference + return array( + array( + 'covers', + 'Foo::bar()', + 'Foo::bar()', + '', + 'Foo::bar()' + ), + array( + 'covers', + 'Foo::bar() Testing', + 'Foo::bar() Testing', + 'Testing', + 'Foo::bar()', + ), + array( + 'covers', + 'Foo::bar() Testing comments', + 'Foo::bar() Testing comments', + 'Testing comments', + 'Foo::bar()', + ), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTagTest.php new file mode 100755 index 000000000..7a75e79ce --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTagTest.php @@ -0,0 +1,115 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\DeprecatedTag + * + * @author Vasil Rangelov + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class DeprecatedTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\LinkTag can create + * a link for the @deprecated doc block. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exDescription + * @param string $exVersion + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\DeprecatedTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exVersion + ) { + $tag = new DeprecatedTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exVersion, $tag->getVersion()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exVersion + return array( + array( + 'deprecated', + '1.0 First release.', + '1.0 First release.', + 'First release.', + '1.0' + ), + array( + 'deprecated', + "1.0\nFirst release.", + "1.0\nFirst release.", + 'First release.', + '1.0' + ), + array( + 'deprecated', + "1.0\nFirst\nrelease.", + "1.0\nFirst\nrelease.", + "First\nrelease.", + '1.0' + ), + array( + 'deprecated', + 'Unfinished release', + 'Unfinished release', + 'Unfinished release', + '' + ), + array( + 'deprecated', + '1.0', + '1.0', + '', + '1.0' + ), + array( + 'deprecated', + 'GIT: $Id$', + 'GIT: $Id$', + '', + 'GIT: $Id$' + ), + array( + 'deprecated', + 'GIT: $Id$ Dev build', + 'GIT: $Id$ Dev build', + 'Dev build', + 'GIT: $Id$' + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ExampleTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ExampleTagTest.php new file mode 100755 index 000000000..519a61b3a --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ExampleTagTest.php @@ -0,0 +1,203 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\ExampleTag + * + * @author Vasil Rangelov + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ExampleTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\SourceTag can + * understand the @source DocBlock. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exStartingLine + * @param string $exLineCount + * @param string $exFilepath + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\ExampleTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exStartingLine, + $exLineCount, + $exFilePath + ) { + $tag = new ExampleTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exStartingLine, $tag->getStartingLine()); + $this->assertEquals($exLineCount, $tag->getLineCount()); + $this->assertEquals($exFilePath, $tag->getFilePath()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, + // $content, + // $exContent, + // $exDescription, + // $exStartingLine, + // $exLineCount, + // $exFilePath + return array( + array( + 'example', + 'file.php', + 'file.php', + '', + 1, + null, + 'file.php' + ), + array( + 'example', + 'Testing comments', + 'Testing comments', + 'comments', + 1, + null, + 'Testing' + ), + array( + 'example', + 'file.php 2 Testing', + 'file.php 2 Testing', + 'Testing', + 2, + null, + 'file.php' + ), + array( + 'example', + 'file.php 2 3 Testing comments', + 'file.php 2 3 Testing comments', + 'Testing comments', + 2, + 3, + 'file.php' + ), + array( + 'example', + 'file.php 2 -1 Testing comments', + 'file.php 2 -1 Testing comments', + '-1 Testing comments', + 2, + null, + 'file.php' + ), + array( + 'example', + 'file.php -1 1 Testing comments', + 'file.php -1 1 Testing comments', + '-1 1 Testing comments', + 1, + null, + 'file.php' + ), + array( + 'example', + '"file with spaces.php" Testing comments', + '"file with spaces.php" Testing comments', + 'Testing comments', + 1, + null, + 'file with spaces.php' + ), + array( + 'example', + '"file with spaces.php" 2 Testing comments', + '"file with spaces.php" 2 Testing comments', + 'Testing comments', + 2, + null, + 'file with spaces.php' + ), + array( + 'example', + '"file with spaces.php" 2 3 Testing comments', + '"file with spaces.php" 2 3 Testing comments', + 'Testing comments', + 2, + 3, + 'file with spaces.php' + ), + array( + 'example', + '"file with spaces.php" 2 -3 Testing comments', + '"file with spaces.php" 2 -3 Testing comments', + '-3 Testing comments', + 2, + null, + 'file with spaces.php' + ), + array( + 'example', + '"file with spaces.php" -2 3 Testing comments', + '"file with spaces.php" -2 3 Testing comments', + '-2 3 Testing comments', + 1, + null, + 'file with spaces.php' + ), + array( + 'example', + 'file%20with%20spaces.php Testing comments', + 'file%20with%20spaces.php Testing comments', + 'Testing comments', + 1, + null, + 'file with spaces.php' + ), + array( + 'example', + 'folder/file%20with%20spaces.php Testing comments', + 'folder/file%20with%20spaces.php Testing comments', + 'Testing comments', + 1, + null, + 'folder/file with spaces.php' + ), + array( + 'example', + 'http://example.com/file%20with%20spaces.php Testing comments', + 'http://example.com/file%20with%20spaces.php Testing comments', + 'Testing comments', + 1, + null, + 'http://example.com/file%20with%20spaces.php' + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/LinkTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/LinkTagTest.php new file mode 100755 index 000000000..0c64ed086 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/LinkTagTest.php @@ -0,0 +1,87 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\LinkTag + * + * @author Ben Selby + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class LinkTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\LinkTag can create + * a link for the @link doc block. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exDescription + * @param string $exLink + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\LinkTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exLink + ) { + $tag = new LinkTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exLink, $tag->getLink()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exLink + return array( + array( + 'link', + 'http://www.phpdoc.org/', + 'http://www.phpdoc.org/', + 'http://www.phpdoc.org/', + 'http://www.phpdoc.org/' + ), + array( + 'link', + 'http://www.phpdoc.org/ Testing', + 'http://www.phpdoc.org/ Testing', + 'Testing', + 'http://www.phpdoc.org/' + ), + array( + 'link', + 'http://www.phpdoc.org/ Testing comments', + 'http://www.phpdoc.org/ Testing comments', + 'Testing comments', + 'http://www.phpdoc.org/' + ), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/MethodTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/MethodTagTest.php new file mode 100755 index 000000000..efc3a15b5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/MethodTagTest.php @@ -0,0 +1,146 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\MethodTag + * + * @author Mike van Riel + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class MethodTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @param string $signature The signature to test. + * @param bool $valid Whether the given signature is expected to + * be valid. + * @param string $expected_name The method name that is expected from this + * signature. + * @param string $expected_return The return type that is expected from this + * signature. + * @param bool $paramCount Number of parameters in the signature. + * @param string $description The short description mentioned in the + * signature. + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\MethodTag + * @dataProvider getTestSignatures + * + * @return void + */ + public function testConstruct( + $signature, + $valid, + $expected_name, + $expected_return, + $expected_isStatic, + $paramCount, + $description + ) { + ob_start(); + $tag = new MethodTag('method', $signature); + $stdout = ob_get_clean(); + + $this->assertSame( + $valid, + empty($stdout), + 'No error should have been output if the signature is valid' + ); + + if (!$valid) { + return; + } + + $this->assertEquals($expected_name, $tag->getMethodName()); + $this->assertEquals($expected_return, $tag->getType()); + $this->assertEquals($description, $tag->getDescription()); + $this->assertEquals($expected_isStatic, $tag->isStatic()); + $this->assertCount($paramCount, $tag->getArguments()); + } + + public function getTestSignatures() + { + return array( + // TODO: Verify this case +// array( +// 'foo', +// false, 'foo', '', false, 0, '' +// ), + array( + 'foo()', + true, 'foo', 'void', false, 0, '' + ), + array( + 'foo() description', + true, 'foo', 'void', false, 0, 'description' + ), + array( + 'int foo()', + true, 'foo', 'int', false, 0, '' + ), + array( + 'int foo() description', + true, 'foo', 'int', false, 0, 'description' + ), + array( + 'int foo($a, $b)', + true, 'foo', 'int', false, 2, '' + ), + array( + 'int foo() foo(int $a, int $b)', + true, 'foo', 'int', false, 2, '' + ), + array( + 'int foo(int $a, int $b)', + true, 'foo', 'int', false, 2, '' + ), + array( + 'null|int foo(int $a, int $b)', + true, 'foo', 'null|int', false, 2, '' + ), + array( + 'int foo(null|int $a, int $b)', + true, 'foo', 'int', false, 2, '' + ), + array( + '\Exception foo() foo(Exception $a, Exception $b)', + true, 'foo', '\Exception', false, 2, '' + ), + array( + 'int foo() foo(Exception $a, Exception $b) description', + true, 'foo', 'int', false, 2, 'description' + ), + array( + 'int foo() foo(\Exception $a, \Exception $b) description', + true, 'foo', 'int', false, 2, 'description' + ), + array( + 'void()', + true, 'void', 'void', false, 0, '' + ), + array( + 'static foo()', + true, 'foo', 'static', false, 0, '' + ), + array( + 'static void foo()', + true, 'foo', 'void', true, 0, '' + ), + array( + 'static static foo()', + true, 'foo', 'static', true, 0, '' + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ParamTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ParamTagTest.php new file mode 100755 index 000000000..0e05382fa --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ParamTagTest.php @@ -0,0 +1,118 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\ParamTag + * + * @author Mike van Riel + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ParamTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\ParamTag can + * understand the @param DocBlock. + * + * @param string $type + * @param string $content + * @param string $extractedType + * @param string $extractedTypes + * @param string $extractedVarName + * @param string $extractedDescription + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\ParamTag + * @dataProvider provideDataForConstructor + * + * @return void + */ + public function testConstructorParsesInputsIntoCorrectFields( + $type, + $content, + $extractedType, + $extractedTypes, + $extractedVarName, + $extractedDescription + ) { + $tag = new ParamTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($extractedType, $tag->getType()); + $this->assertEquals($extractedTypes, $tag->getTypes()); + $this->assertEquals($extractedVarName, $tag->getVariableName()); + $this->assertEquals($extractedDescription, $tag->getDescription()); + } + + /** + * Data provider for testConstructorParsesInputsIntoCorrectFields() + * + * @return array + */ + public function provideDataForConstructor() + { + return array( + array('param', 'int', 'int', array('int'), '', ''), + array('param', '$bob', '', array(), '$bob', ''), + array( + 'param', + 'int Number of bobs', + 'int', + array('int'), + '', + 'Number of bobs' + ), + array( + 'param', + 'int $bob', + 'int', + array('int'), + '$bob', + '' + ), + array( + 'param', + 'int $bob Number of bobs', + 'int', + array('int'), + '$bob', + 'Number of bobs' + ), + array( + 'param', + "int Description \n on multiple lines", + 'int', + array('int'), + '', + "Description \n on multiple lines" + ), + array( + 'param', + "int \n\$bob Variable name on a new line", + 'int', + array('int'), + '$bob', + "Variable name on a new line" + ), + array( + 'param', + "\nint \$bob Type on a new line", + 'int', + array('int'), + '$bob', + "Type on a new line" + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ReturnTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ReturnTagTest.php new file mode 100755 index 000000000..9e2aec0d1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ReturnTagTest.php @@ -0,0 +1,102 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\ReturnTag + * + * @author Mike van Riel + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ReturnTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\ReturnTag can + * understand the @return DocBlock. + * + * @param string $type + * @param string $content + * @param string $extractedType + * @param string $extractedTypes + * @param string $extractedDescription + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\ReturnTag + * @dataProvider provideDataForConstructor + * + * @return void + */ + public function testConstructorParsesInputsIntoCorrectFields( + $type, + $content, + $extractedType, + $extractedTypes, + $extractedDescription + ) { + $tag = new ReturnTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($extractedType, $tag->getType()); + $this->assertEquals($extractedTypes, $tag->getTypes()); + $this->assertEquals($extractedDescription, $tag->getDescription()); + } + + /** + * Data provider for testConstructorParsesInputsIntoCorrectFields() + * + * @return array + */ + public function provideDataForConstructor() + { + return array( + array('return', '', '', array(), ''), + array('return', 'int', 'int', array('int'), ''), + array( + 'return', + 'int Number of Bobs', + 'int', + array('int'), + 'Number of Bobs' + ), + array( + 'return', + 'int|double Number of Bobs', + 'int|double', + array('int', 'double'), + 'Number of Bobs' + ), + array( + 'return', + "int Number of \n Bobs", + 'int', + array('int'), + "Number of \n Bobs" + ), + array( + 'return', + " int Number of Bobs", + 'int', + array('int'), + "Number of Bobs" + ), + array( + 'return', + "int\nNumber of Bobs", + 'int', + array('int'), + "Number of Bobs" + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SeeTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SeeTagTest.php new file mode 100755 index 000000000..6829b0460 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SeeTagTest.php @@ -0,0 +1,86 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\SeeTag + * + * @author Daniel O'Connor + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class SeeTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the phpDocumentor_Reflection_DocBlock_Tag_See can create a link + * for the @see doc block. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exReference + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\SeeTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exReference + ) { + $tag = new SeeTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exReference, $tag->getReference()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exReference + return array( + array( + 'see', + 'Foo::bar()', + 'Foo::bar()', + '', + 'Foo::bar()' + ), + array( + 'see', + 'Foo::bar() Testing', + 'Foo::bar() Testing', + 'Testing', + 'Foo::bar()', + ), + array( + 'see', + 'Foo::bar() Testing comments', + 'Foo::bar() Testing comments', + 'Testing comments', + 'Foo::bar()', + ), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SinceTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SinceTagTest.php new file mode 100755 index 000000000..8caf25d1c --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SinceTagTest.php @@ -0,0 +1,115 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\SinceTag + * + * @author Vasil Rangelov + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class SinceTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\LinkTag can create + * a link for the @since doc block. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exDescription + * @param string $exVersion + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\SinceTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exVersion + ) { + $tag = new SinceTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exVersion, $tag->getVersion()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exVersion + return array( + array( + 'since', + '1.0 First release.', + '1.0 First release.', + 'First release.', + '1.0' + ), + array( + 'since', + "1.0\nFirst release.", + "1.0\nFirst release.", + 'First release.', + '1.0' + ), + array( + 'since', + "1.0\nFirst\nrelease.", + "1.0\nFirst\nrelease.", + "First\nrelease.", + '1.0' + ), + array( + 'since', + 'Unfinished release', + 'Unfinished release', + 'Unfinished release', + '' + ), + array( + 'since', + '1.0', + '1.0', + '', + '1.0' + ), + array( + 'since', + 'GIT: $Id$', + 'GIT: $Id$', + '', + 'GIT: $Id$' + ), + array( + 'since', + 'GIT: $Id$ Dev build', + 'GIT: $Id$ Dev build', + 'Dev build', + 'GIT: $Id$' + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SourceTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SourceTagTest.php new file mode 100755 index 000000000..2a40e0aa3 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/SourceTagTest.php @@ -0,0 +1,116 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\SourceTag + * + * @author Vasil Rangelov + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class SourceTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\SourceTag can + * understand the @source DocBlock. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exStartingLine + * @param string $exLineCount + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\SourceTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exStartingLine, + $exLineCount + ) { + $tag = new SourceTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exStartingLine, $tag->getStartingLine()); + $this->assertEquals($exLineCount, $tag->getLineCount()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exStartingLine, $exLineCount + return array( + array( + 'source', + '2', + '2', + '', + 2, + null + ), + array( + 'source', + 'Testing', + 'Testing', + 'Testing', + 1, + null + ), + array( + 'source', + '2 Testing', + '2 Testing', + 'Testing', + 2, + null + ), + array( + 'source', + '2 3 Testing comments', + '2 3 Testing comments', + 'Testing comments', + 2, + 3 + ), + array( + 'source', + '2 -1 Testing comments', + '2 -1 Testing comments', + '-1 Testing comments', + 2, + null + ), + array( + 'source', + '-1 1 Testing comments', + '-1 1 Testing comments', + '-1 1 Testing comments', + 1, + null + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTagTest.php new file mode 100755 index 000000000..3c669d558 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTagTest.php @@ -0,0 +1,102 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\ThrowsTag + * + * @author Mike van Riel + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class ThrowsTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag can + * understand the @throws DocBlock. + * + * @param string $type + * @param string $content + * @param string $extractedType + * @param string $extractedTypes + * @param string $extractedDescription + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag + * @dataProvider provideDataForConstructor + * + * @return void + */ + public function testConstructorParsesInputsIntoCorrectFields( + $type, + $content, + $extractedType, + $extractedTypes, + $extractedDescription + ) { + $tag = new ThrowsTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($extractedType, $tag->getType()); + $this->assertEquals($extractedTypes, $tag->getTypes()); + $this->assertEquals($extractedDescription, $tag->getDescription()); + } + + /** + * Data provider for testConstructorParsesInputsIntoCorrectFields() + * + * @return array + */ + public function provideDataForConstructor() + { + return array( + array('throws', '', '', array(), ''), + array('throws', 'int', 'int', array('int'), ''), + array( + 'throws', + 'int Number of Bobs', + 'int', + array('int'), + 'Number of Bobs' + ), + array( + 'throws', + 'int|double Number of Bobs', + 'int|double', + array('int', 'double'), + 'Number of Bobs' + ), + array( + 'throws', + "int Number of \n Bobs", + 'int', + array('int'), + "Number of \n Bobs" + ), + array( + 'throws', + " int Number of Bobs", + 'int', + array('int'), + "Number of Bobs" + ), + array( + 'throws', + "int\nNumber of Bobs", + 'int', + array('int'), + "Number of Bobs" + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/UsesTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/UsesTagTest.php new file mode 100755 index 000000000..45868d73e --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/UsesTagTest.php @@ -0,0 +1,86 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\UsesTag + * + * @author Daniel O'Connor + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class UsesTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\UsesTag can create + * a link for the @uses doc block. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exReference + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\UsesTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exReference + ) { + $tag = new UsesTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exReference, $tag->getReference()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exReference + return array( + array( + 'uses', + 'Foo::bar()', + 'Foo::bar()', + '', + 'Foo::bar()' + ), + array( + 'uses', + 'Foo::bar() Testing', + 'Foo::bar() Testing', + 'Testing', + 'Foo::bar()', + ), + array( + 'uses', + 'Foo::bar() Testing comments', + 'Foo::bar() Testing comments', + 'Testing comments', + 'Foo::bar()', + ), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VarTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VarTagTest.php new file mode 100755 index 000000000..9ae2aa5f7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VarTagTest.php @@ -0,0 +1,94 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\VarTag + * + * @author Daniel O'Connor + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class VarTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\VarTag can + * understand the @var doc block. + * + * @param string $type + * @param string $content + * @param string $exType + * @param string $exVariable + * @param string $exDescription + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\VarTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exType, + $exVariable, + $exDescription + ) { + $tag = new VarTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exType, $tag->getType()); + $this->assertEquals($exVariable, $tag->getVariableName()); + $this->assertEquals($exDescription, $tag->getDescription()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exType, $exVariable, $exDescription + return array( + array( + 'var', + 'int', + 'int', + '', + '' + ), + array( + 'var', + 'int $bob', + 'int', + '$bob', + '' + ), + array( + 'var', + 'int $bob Number of bobs', + 'int', + '$bob', + 'Number of bobs' + ), + array( + 'var', + '', + '', + '', + '' + ), + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VersionTagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VersionTagTest.php new file mode 100755 index 000000000..e145386d4 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Tag/VersionTagTest.php @@ -0,0 +1,115 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tag; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\VersionTag + * + * @author Vasil Rangelov + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class VersionTagTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\LinkTag can create + * a link for the @version doc block. + * + * @param string $type + * @param string $content + * @param string $exContent + * @param string $exDescription + * @param string $exVersion + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag\VersionTag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exContent, + $exDescription, + $exVersion + ) { + $tag = new VersionTag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($exContent, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + $this->assertEquals($exVersion, $tag->getVersion()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exContent, $exDescription, $exVersion + return array( + array( + 'version', + '1.0 First release.', + '1.0 First release.', + 'First release.', + '1.0' + ), + array( + 'version', + "1.0\nFirst release.", + "1.0\nFirst release.", + 'First release.', + '1.0' + ), + array( + 'version', + "1.0\nFirst\nrelease.", + "1.0\nFirst\nrelease.", + "First\nrelease.", + '1.0' + ), + array( + 'version', + 'Unfinished release', + 'Unfinished release', + 'Unfinished release', + '' + ), + array( + 'version', + '1.0', + '1.0', + '', + '1.0' + ), + array( + 'version', + 'GIT: $Id$', + 'GIT: $Id$', + '', + 'GIT: $Id$' + ), + array( + 'version', + 'GIT: $Id$ Dev build', + 'GIT: $Id$ Dev build', + 'Dev build', + 'GIT: $Id$' + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/TagTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/TagTest.php new file mode 100755 index 000000000..9e873ecb5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/TagTest.php @@ -0,0 +1,313 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\DocBlock\Context; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Tag\VarTag + * + * @author Daniel O'Connor + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class TagTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @expectedException \InvalidArgumentException + * + * @return void + */ + public function testInvalidTagLine() + { + Tag::createInstance('Invalid tag line'); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock\Tag::registerTagHandler + * + * @return void + */ + public function testTagHandlerUnregistration() + { + $currentHandler = __NAMESPACE__ . '\Tag\VarTag'; + $tagPreUnreg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPreUnreg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreUnreg + ); + + Tag::registerTagHandler('var', null); + + $tagPostUnreg = Tag::createInstance('@var mixed'); + $this->assertNotInstanceOf( + $currentHandler, + $tagPostUnreg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostUnreg + ); + + Tag::registerTagHandler('var', $currentHandler); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock\Tag::registerTagHandler + * + * @return void + */ + public function testTagHandlerCorrectRegistration() + { + if (0 == ini_get('allow_url_include')) { + $this->markTestSkipped('"data" URIs for includes are required.'); + } + $currentHandler = __NAMESPACE__ . '\Tag\VarTag'; + $tagPreReg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPreReg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreReg + ); + + include 'data:text/plain;base64,'. base64_encode( +<<assertTrue(Tag::registerTagHandler('var', '\MyTagHandler')); + + $tagPostReg = Tag::createInstance('@var mixed'); + $this->assertNotInstanceOf( + $currentHandler, + $tagPostReg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostReg + ); + $this->assertInstanceOf( + '\MyTagHandler', + $tagPostReg + ); + + $this->assertTrue(Tag::registerTagHandler('var', $currentHandler)); + } + + /** + * @depends testTagHandlerCorrectRegistration + * @covers \phpDocumentor\Reflection\DocBlock\Tag::registerTagHandler + * @covers \phpDocumentor\Reflection\DocBlock\Tag::createInstance + * + * @return void + */ + public function testNamespacedTagHandlerCorrectRegistration() + { + $tagPreReg = Tag::createInstance('@T something'); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreReg + ); + $this->assertNotInstanceOf( + '\MyTagHandler', + $tagPreReg + ); + + $this->assertTrue( + Tag::registerTagHandler('\MyNamespace\MyTag', '\MyTagHandler') + ); + + $tagPostReg = Tag::createInstance( + '@T something', + new DocBlock( + '', + new Context('', array('T' => '\MyNamespace\MyTag')) + ) + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostReg + ); + $this->assertInstanceOf( + '\MyTagHandler', + $tagPostReg + ); + + $this->assertTrue( + Tag::registerTagHandler('\MyNamespace\MyTag', null) + ); + } + + /** + * @depends testTagHandlerCorrectRegistration + * @covers \phpDocumentor\Reflection\DocBlock\Tag::registerTagHandler + * @covers \phpDocumentor\Reflection\DocBlock\Tag::createInstance + * + * @return void + */ + public function testNamespacedTagHandlerIncorrectRegistration() + { + $tagPreReg = Tag::createInstance('@T something'); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreReg + ); + $this->assertNotInstanceOf( + '\MyTagHandler', + $tagPreReg + ); + + $this->assertFalse( + Tag::registerTagHandler('MyNamespace\MyTag', '\MyTagHandler') + ); + + $tagPostReg = Tag::createInstance( + '@T something', + new DocBlock( + '', + new Context('', array('T' => '\MyNamespace\MyTag')) + ) + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostReg + ); + $this->assertNotInstanceOf( + '\MyTagHandler', + $tagPostReg + ); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock\Tag::registerTagHandler + * + * @return void + */ + public function testNonExistentTagHandlerRegistration() + { + $currentHandler = __NAMESPACE__ . '\Tag\VarTag'; + $tagPreReg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPreReg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreReg + ); + + $this->assertFalse(Tag::registerTagHandler('var', 'Non existent')); + + $tagPostReg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPostReg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostReg + ); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock\Tag::registerTagHandler + * + * @return void + */ + public function testIncompatibleTagHandlerRegistration() + { + $currentHandler = __NAMESPACE__ . '\Tag\VarTag'; + $tagPreReg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPreReg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreReg + ); + + $this->assertFalse( + Tag::registerTagHandler('var', __NAMESPACE__ . '\TagTest') + ); + + $tagPostReg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPostReg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostReg + ); + } + + /** + * Test that the \phpDocumentor\Reflection\DocBlock\Tag\VarTag can + * understand the @var doc block. + * + * @param string $type + * @param string $content + * @param string $exDescription + * + * @covers \phpDocumentor\Reflection\DocBlock\Tag + * @dataProvider provideDataForConstuctor + * + * @return void + */ + public function testConstructorParesInputsIntoCorrectFields( + $type, + $content, + $exDescription + ) { + $tag = new Tag($type, $content); + + $this->assertEquals($type, $tag->getName()); + $this->assertEquals($content, $tag->getContent()); + $this->assertEquals($exDescription, $tag->getDescription()); + } + + /** + * Data provider for testConstructorParesInputsIntoCorrectFields + * + * @return array + */ + public function provideDataForConstuctor() + { + // $type, $content, $exDescription + return array( + array( + 'unknown', + 'some content', + 'some content', + ), + array( + 'unknown', + '', + '', + ) + ); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Type/CollectionTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Type/CollectionTest.php new file mode 100755 index 000000000..78c7306d6 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlock/Type/CollectionTest.php @@ -0,0 +1,195 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Type; + +use phpDocumentor\Reflection\DocBlock\Context; + +/** + * Test class for \phpDocumentor\Reflection\DocBlock\Type\Collection + * + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection + * + * @author Mike van Riel + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class CollectionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::getContext + * + * @return void + */ + public function testConstruct() + { + $collection = new Collection(); + $this->assertCount(0, $collection); + $this->assertEquals('', $collection->getContext()->getNamespace()); + $this->assertCount(0, $collection->getContext()->getNamespaceAliases()); + } + + /** + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct + * + * @return void + */ + public function testConstructWithTypes() + { + $collection = new Collection(array('integer', 'string')); + $this->assertCount(2, $collection); + } + + /** + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct + * + * @return void + */ + public function testConstructWithNamespace() + { + $collection = new Collection(array(), new Context('\My\Space')); + $this->assertEquals('My\Space', $collection->getContext()->getNamespace()); + + $collection = new Collection(array(), new Context('My\Space')); + $this->assertEquals('My\Space', $collection->getContext()->getNamespace()); + + $collection = new Collection(array(), null); + $this->assertEquals('', $collection->getContext()->getNamespace()); + } + + /** + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::__construct + * + * @return void + */ + public function testConstructWithNamespaceAliases() + { + $fixture = array('a' => 'b'); + $collection = new Collection(array(), new Context(null, $fixture)); + $this->assertEquals( + array('a' => '\b'), + $collection->getContext()->getNamespaceAliases() + ); + } + + /** + * @param string $fixture + * @param array $expected + * + * @dataProvider provideTypesToExpand + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::add + * + * @return void + */ + public function testAdd($fixture, $expected) + { + $collection = new Collection( + array(), + new Context('\My\Space', array('Alias' => '\My\Space\Aliasing')) + ); + $collection->add($fixture); + + $this->assertSame($expected, $collection->getArrayCopy()); + } + + /** + * @param string $fixture + * @param array $expected + * + * @dataProvider provideTypesToExpandWithoutNamespace + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::add + * + * @return void + */ + public function testAddWithoutNamespace($fixture, $expected) + { + $collection = new Collection( + array(), + new Context(null, array('Alias' => '\My\Space\Aliasing')) + ); + $collection->add($fixture); + + $this->assertSame($expected, $collection->getArrayCopy()); + } + + /** + * @covers phpDocumentor\Reflection\DocBlock\Type\Collection::add + * @expectedException InvalidArgumentException + * + * @return void + */ + public function testAddWithInvalidArgument() + { + $collection = new Collection(); + $collection->add(array()); + } + + /** + * Returns the types and their expected values to test the retrieval of + * types. + * + * @param string $method Name of the method consuming this data provider. + * @param string $namespace Name of the namespace to user as basis. + * + * @return string[] + */ + public function provideTypesToExpand($method, $namespace = '\My\Space\\') + { + return array( + array('', array()), + array(' ', array()), + array('int', array('int')), + array('int ', array('int')), + array('string', array('string')), + array('DocBlock', array($namespace.'DocBlock')), + array('DocBlock[]', array($namespace.'DocBlock[]')), + array(' DocBlock ', array($namespace.'DocBlock')), + array('\My\Space\DocBlock', array('\My\Space\DocBlock')), + array('Alias\DocBlock', array('\My\Space\Aliasing\DocBlock')), + array( + 'DocBlock|Tag', + array($namespace .'DocBlock', $namespace .'Tag') + ), + array( + 'DocBlock|null', + array($namespace.'DocBlock', 'null') + ), + array( + '\My\Space\DocBlock|Tag', + array('\My\Space\DocBlock', $namespace.'Tag') + ), + array( + 'DocBlock[]|null', + array($namespace.'DocBlock[]', 'null') + ), + array( + 'DocBlock[]|int[]', + array($namespace.'DocBlock[]', 'int[]') + ), + ); + } + + /** + * Returns the types and their expected values to test the retrieval of + * types when no namespace is available. + * + * @param string $method Name of the method consuming this data provider. + * + * @return string[] + */ + public function provideTypesToExpandWithoutNamespace($method) + { + return $this->provideTypesToExpand($method, '\\'); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlockTest.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlockTest.php new file mode 100755 index 000000000..30eedfc58 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpdocumentor/reflection-docblock/tests/phpDocumentor/Reflection/DocBlockTest.php @@ -0,0 +1,337 @@ + + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\Context; +use phpDocumentor\Reflection\DocBlock\Location; +use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag; + +/** + * Test class for phpDocumentor\Reflection\DocBlock + * + * @author Mike van Riel + * @copyright 2010-2011 Mike van Riel / Naenius. (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ +class DocBlockTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers \phpDocumentor\Reflection\DocBlock + * + * @return void + */ + public function testConstruct() + { + $fixture = << '\phpDocumentor')), + new Location(2) + ); + $this->assertEquals( + 'This is a short description', + $object->getShortDescription() + ); + $this->assertEquals( + 'This is a long description', + $object->getLongDescription()->getContents() + ); + $this->assertCount(2, $object->getTags()); + $this->assertTrue($object->hasTag('see')); + $this->assertTrue($object->hasTag('return')); + $this->assertFalse($object->hasTag('category')); + + $this->assertSame('MyNamespace', $object->getContext()->getNamespace()); + $this->assertSame( + array('PHPDoc' => '\phpDocumentor'), + $object->getContext()->getNamespaceAliases() + ); + $this->assertSame(2, $object->getLocation()->getLineNumber()); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock::splitDocBlock + * + * @return void + */ + public function testConstructWithTagsOnly() + { + $fixture = <<assertEquals('', $object->getShortDescription()); + $this->assertEquals('', $object->getLongDescription()->getContents()); + $this->assertCount(2, $object->getTags()); + $this->assertTrue($object->hasTag('see')); + $this->assertTrue($object->hasTag('return')); + $this->assertFalse($object->hasTag('category')); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock::isTemplateStart + */ + public function testIfStartOfTemplateIsDiscovered() + { + $fixture = <<assertEquals('', $object->getShortDescription()); + $this->assertEquals('', $object->getLongDescription()->getContents()); + $this->assertCount(2, $object->getTags()); + $this->assertTrue($object->hasTag('see')); + $this->assertTrue($object->hasTag('return')); + $this->assertFalse($object->hasTag('category')); + $this->assertTrue($object->isTemplateStart()); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock::isTemplateEnd + */ + public function testIfEndOfTemplateIsDiscovered() + { + $fixture = <<assertEquals('', $object->getShortDescription()); + $this->assertEquals('', $object->getLongDescription()->getContents()); + $this->assertTrue($object->isTemplateEnd()); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock::cleanInput + * + * @return void + */ + public function testConstructOneLiner() + { + $fixture = '/** Short description and nothing more. */'; + $object = new DocBlock($fixture); + $this->assertEquals( + 'Short description and nothing more.', + $object->getShortDescription() + ); + $this->assertEquals('', $object->getLongDescription()->getContents()); + $this->assertCount(0, $object->getTags()); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock::__construct + * + * @return void + */ + public function testConstructFromReflector() + { + $object = new DocBlock(new \ReflectionClass($this)); + $this->assertEquals( + 'Test class for phpDocumentor\Reflection\DocBlock', + $object->getShortDescription() + ); + $this->assertEquals('', $object->getLongDescription()->getContents()); + $this->assertCount(4, $object->getTags()); + $this->assertTrue($object->hasTag('author')); + $this->assertTrue($object->hasTag('copyright')); + $this->assertTrue($object->hasTag('license')); + $this->assertTrue($object->hasTag('link')); + $this->assertFalse($object->hasTag('category')); + } + + /** + * @expectedException \InvalidArgumentException + * + * @return void + */ + public function testExceptionOnInvalidObject() + { + new DocBlock($this); + } + + public function testDotSeperation() + { + $fixture = <<assertEquals( + 'This is a short description.', + $object->getShortDescription() + ); + $this->assertEquals( + "This is a long description.\nThis is a continuation of the long " + ."description.", + $object->getLongDescription()->getContents() + ); + } + + /** + * @covers \phpDocumentor\Reflection\DocBlock::parseTags + * @expectedException \LogicException + * + * @return void + */ + public function testInvalidTagBlock() + { + if (0 == ini_get('allow_url_include')) { + $this->markTestSkipped('"data" URIs for includes are required.'); + } + + include 'data:text/plain;base64,'. base64_encode( + <<assertEquals( + 'This is a short description.', + $object->getShortDescription() + ); + $this->assertEquals( + 'This is a long description.', + $object->getLongDescription()->getContents() + ); + $tags = $object->getTags(); + $this->assertCount(2, $tags); + $this->assertTrue($object->hasTag('method')); + $this->assertTrue($object->hasTag('Method')); + $this->assertInstanceOf( + __NAMESPACE__ . '\DocBlock\Tag\MethodTag', + $tags[0] + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\DocBlock\Tag', + $tags[1] + ); + $this->assertNotInstanceOf( + __NAMESPACE__ . '\DocBlock\Tag\MethodTag', + $tags[1] + ); + } + + /** + * @depends testConstructFromReflector + * @covers \phpDocumentor\Reflection\DocBlock::getTagsByName + * + * @return void + */ + public function testGetTagsByNameZeroAndOneMatch() + { + $object = new DocBlock(new \ReflectionClass($this)); + $this->assertEmpty($object->getTagsByName('category')); + $this->assertCount(1, $object->getTagsByName('author')); + } + + /** + * @depends testConstructWithTagsOnly + * @covers \phpDocumentor\Reflection\DocBlock::parseTags + * + * @return void + */ + public function testParseMultilineTag() + { + $fixture = <<assertCount(1, $object->getTags()); + } + + /** + * @depends testConstructWithTagsOnly + * @covers \phpDocumentor\Reflection\DocBlock::parseTags + * + * @return void + */ + public function testParseMultilineTagWithLineBreaks() + { + $fixture = <<assertCount(1, $tags = $object->getTags()); + /** @var ReturnTag $tag */ + $tag = reset($tags); + $this->assertEquals("Content on\n multiple lines.\n\n One more, after the break.", $tag->getDescription()); + } + + /** + * @depends testConstructWithTagsOnly + * @covers \phpDocumentor\Reflection\DocBlock::getTagsByName + * + * @return void + */ + public function testGetTagsByNameMultipleMatch() + { + $fixture = <<assertEmpty($object->getTagsByName('category')); + $this->assertCount(1, $object->getTagsByName('return')); + $this->assertCount(2, $object->getTagsByName('param')); + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/README b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/README new file mode 100755 index 000000000..f596115fe --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/README @@ -0,0 +1,58 @@ +PHP Diff Class +-------------- + +Introduction +------------ +A comprehensive library for generating differences between +two hashable objects (strings or arrays). Generated differences can be +rendered in all of the standard formats including: + * Unified + * Context + * Inline HTML + * Side by Side HTML + +The logic behind the core of the diff engine (ie, the sequence matcher) +is primarily based on the Python difflib package. The reason for doing +so is primarily because of its high degree of accuracy. + +Example Use +----------- +A quick usage example can be found in the example/ directory and under +example.php. + +More complete documentation will be available shortly. + +Todo +---- + * Ability to ignore blank line changes + * 3 way diff support + * Performance optimizations + +License (BSD License) +--------------------- +Copyright (c) 2009 Chris Boulton +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Chris Boulton nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/composer.json new file mode 100755 index 000000000..5e91b0f9d --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/composer.json @@ -0,0 +1,19 @@ +{ + "name": "phpspec/php-diff", + "type": "library", + "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", + "license": "BSD-3-Clause", + + "authors": [ + { + "name": "Chris Boulton", + "homepage": "http://github.com/chrisboulton" + } + ], + + "autoload": { + "psr-0": { + "Diff": "lib/" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/a.txt b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/a.txt new file mode 100755 index 000000000..6f3897b2f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/a.txt @@ -0,0 +1,13 @@ + + + + Hello World! + + +

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    + +

    A heading we'll be removing

    + +

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    + + \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/b.txt b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/b.txt new file mode 100755 index 000000000..5918964d0 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/b.txt @@ -0,0 +1,14 @@ + + + + Goodbye Cruel World! + + +

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    + + +

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

    + +

    Just a small amount of new text...

    + + \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/example.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/example.php new file mode 100755 index 000000000..234bc2c87 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/example.php @@ -0,0 +1,69 @@ + + + + + PHP LibDiff - Examples + + + +

    PHP LibDiff - Examples

    +
    + true, + //'ignoreCase' => true, + ); + + // Initialize the diff class + $diff = new Diff($a, $b, $options); + + ?> +

    Side by Side Diff

    + Render($renderer); + + ?> +

    Inline Diff

    + render($renderer); + + ?> +

    Unified Diff

    +
    render($renderer));
    +
    +		?>
    +		
    +

    Context Diff

    +
    render($renderer));
    +		?>
    +		
    + + \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/styles.css b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/styles.css new file mode 100755 index 000000000..5454896fd --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/example/styles.css @@ -0,0 +1,93 @@ +body { + background: #fff; + font-family: Arial; + font-size: 12px; +} +.Differences { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + empty-cells: show; +} + +.Differences thead th { + text-align: left; + border-bottom: 1px solid #000; + background: #aaa; + color: #000; + padding: 4px; +} +.Differences tbody th { + text-align: right; + background: #ccc; + width: 4em; + padding: 1px 2px; + border-right: 1px solid #000; + vertical-align: top; + font-size: 13px; +} + +.Differences td { + padding: 1px 2px; + font-family: Consolas, monospace; + font-size: 13px; +} + +.DifferencesSideBySide .ChangeInsert td.Left { + background: #dfd; +} + +.DifferencesSideBySide .ChangeInsert td.Right { + background: #cfc; +} + +.DifferencesSideBySide .ChangeDelete td.Left { + background: #f88; +} + +.DifferencesSideBySide .ChangeDelete td.Right { + background: #faa; +} + +.DifferencesSideBySide .ChangeReplace .Left { + background: #fe9; +} + +.DifferencesSideBySide .ChangeReplace .Right { + background: #fd8; +} + +.Differences ins, .Differences del { + text-decoration: none; +} + +.DifferencesSideBySide .ChangeReplace ins, .DifferencesSideBySide .ChangeReplace del { + background: #fc0; +} + +.Differences .Skipped { + background: #f7f7f7; +} + +.DifferencesInline .ChangeReplace .Left, +.DifferencesInline .ChangeDelete .Left { + background: #fdd; +} + +.DifferencesInline .ChangeReplace .Right, +.DifferencesInline .ChangeInsert .Right { + background: #dfd; +} + +.DifferencesInline .ChangeReplace ins { + background: #9e9; +} + +.DifferencesInline .ChangeReplace del { + background: #e99; +} + +pre { + width: 100%; + overflow: auto; +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff.php new file mode 100755 index 000000000..d6cecb790 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff.php @@ -0,0 +1,177 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package Diff + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +class Diff +{ + /** + * @var array The "old" sequence to use as the basis for the comparison. + */ + private $a = null; + + /** + * @var array The "new" sequence to generate the changes for. + */ + private $b = null; + + /** + * @var array Array containing the generated opcodes for the differences between the two items. + */ + private $groupedCodes = null; + + /** + * @var array Associative array of the default options available for the diff class and their default value. + */ + private $defaultOptions = array( + 'context' => 3, + 'ignoreNewLines' => false, + 'ignoreWhitespace' => false, + 'ignoreCase' => false + ); + + /** + * @var array Array of the options that have been applied for generating the diff. + */ + private $options = array(); + + /** + * The constructor. + * + * @param array $a Array containing the lines of the first string to compare. + * @param array $b Array containing the lines for the second string to compare. + * @param array $options + */ + public function __construct($a, $b, $options=array()) + { + $this->a = $a; + $this->b = $b; + + $this->options = array_merge($this->defaultOptions, $options); + } + + /** + * Render a diff using the supplied rendering class and return it. + * + * @param Diff_Renderer_Abstract $renderer An instance of the rendering object to use for generating the diff. + * @return mixed The generated diff. Exact return value depends on the rendered. + */ + public function render(Diff_Renderer_Abstract $renderer) + { + $renderer->diff = $this; + return $renderer->render(); + } + + /** + * Get a range of lines from $start to $end from the first comparison string + * and return them as an array. If no values are supplied, the entire string + * is returned. It's also possible to specify just one line to return only + * that line. + * + * @param int $start The starting number. + * @param int $end The ending number. If not supplied, only the item in $start will be returned. + * @return array Array of all of the lines between the specified range. + */ + public function getA($start=0, $end=null) + { + if($start == 0 && $end === null) { + return $this->a; + } + + if($end === null) { + $length = 1; + } + else { + $length = $end - $start; + } + + return array_slice($this->a, $start, $length); + + } + + /** + * Get a range of lines from $start to $end from the second comparison string + * and return them as an array. If no values are supplied, the entire string + * is returned. It's also possible to specify just one line to return only + * that line. + * + * @param int $start The starting number. + * @param int $end The ending number. If not supplied, only the item in $start will be returned. + * @return array Array of all of the lines between the specified range. + */ + public function getB($start=0, $end=null) + { + if($start == 0 && $end === null) { + return $this->b; + } + + if($end === null) { + $length = 1; + } + else { + $length = $end - $start; + } + + return array_slice($this->b, $start, $length); + } + + /** + * Generate a list of the compiled and grouped opcodes for the differences between the + * two strings. Generally called by the renderer, this class instantiates the sequence + * matcher and performs the actual diff generation and return an array of the opcodes + * for it. Once generated, the results are cached in the diff class instance. + * + * @return array Array of the grouped opcodes for the generated diff. + */ + public function getGroupedOpcodes() + { + if(!is_null($this->groupedCodes)) { + return $this->groupedCodes; + } + + require_once dirname(__FILE__).'/Diff/SequenceMatcher.php'; + $sequenceMatcher = new Diff_SequenceMatcher($this->a, $this->b, null, $this->options); + $this->groupedCodes = $sequenceMatcher->getGroupedOpcodes(); + return $this->groupedCodes; + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Abstract.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Abstract.php new file mode 100755 index 000000000..f63c3e7f7 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Abstract.php @@ -0,0 +1,82 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +abstract class Diff_Renderer_Abstract +{ + /** + * @var object Instance of the diff class that this renderer is generating the rendered diff for. + */ + public $diff; + + /** + * @var array Array of the default options that apply to this renderer. + */ + protected $defaultOptions = array(); + + /** + * @var array Array containing the user applied and merged default options for the renderer. + */ + protected $options = array(); + + /** + * The constructor. Instantiates the rendering engine and if options are passed, + * sets the options for the renderer. + * + * @param array $options Optionally, an array of the options for the renderer. + */ + public function __construct(array $options = array()) + { + $this->setOptions($options); + } + + /** + * Set the options of the renderer to those supplied in the passed in array. + * Options are merged with the default to ensure that there aren't any missing + * options. + * + * @param array $options Array of options to set. + */ + public function setOptions(array $options) + { + $this->options = array_merge($this->defaultOptions, $options); + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Array.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Array.php new file mode 100755 index 000000000..7113a1747 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Array.php @@ -0,0 +1,225 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/../Abstract.php'; + +class Diff_Renderer_Html_Array extends Diff_Renderer_Abstract +{ + /** + * @var array Array of the default options that apply to this renderer. + */ + protected $defaultOptions = array( + 'tabSize' => 4 + ); + + /** + * Render and return an array structure suitable for generating HTML + * based differences. Generally called by subclasses that generate a + * HTML based diff and return an array of the changes to show in the diff. + * + * @return array An array of the generated chances, suitable for presentation in HTML. + */ + public function render() + { + // As we'll be modifying a & b to include our change markers, + // we need to get the contents and store them here. That way + // we're not going to destroy the original data + $a = $this->diff->getA(); + $b = $this->diff->getB(); + + $changes = array(); + $opCodes = $this->diff->getGroupedOpcodes(); + foreach($opCodes as $group) { + $blocks = array(); + $lastTag = null; + $lastBlock = 0; + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + + if($tag == 'replace' && $i2 - $i1 == $j2 - $j1) { + for($i = 0; $i < ($i2 - $i1); ++$i) { + $fromLine = $a[$i1 + $i]; + $toLine = $b[$j1 + $i]; + + list($start, $end) = $this->getChangeExtent($fromLine, $toLine); + if($start != 0 || $end != 0) { + $last = $end + strlen($fromLine); + $fromLine = substr_replace($fromLine, "\0", $start, 0); + $fromLine = substr_replace($fromLine, "\1", $last + 1, 0); + $last = $end + strlen($toLine); + $toLine = substr_replace($toLine, "\0", $start, 0); + $toLine = substr_replace($toLine, "\1", $last + 1, 0); + $a[$i1 + $i] = $fromLine; + $b[$j1 + $i] = $toLine; + } + } + } + + if($tag != $lastTag) { + $blocks[] = array( + 'tag' => $tag, + 'base' => array( + 'offset' => $i1, + 'lines' => array() + ), + 'changed' => array( + 'offset' => $j1, + 'lines' => array() + ) + ); + $lastBlock = count($blocks)-1; + } + + $lastTag = $tag; + + if($tag == 'equal') { + $lines = array_slice($a, $i1, ($i2 - $i1)); + $blocks[$lastBlock]['base']['lines'] += $this->formatLines($lines); + $lines = array_slice($b, $j1, ($j2 - $j1)); + $blocks[$lastBlock]['changed']['lines'] += $this->formatLines($lines); + } + else { + if($tag == 'replace' || $tag == 'delete') { + $lines = array_slice($a, $i1, ($i2 - $i1)); + $lines = $this->formatLines($lines); + $lines = str_replace(array("\0", "\1"), array('', ''), $lines); + $blocks[$lastBlock]['base']['lines'] += $lines; + } + + if($tag == 'replace' || $tag == 'insert') { + $lines = array_slice($b, $j1, ($j2 - $j1)); + $lines = $this->formatLines($lines); + $lines = str_replace(array("\0", "\1"), array('', ''), $lines); + $blocks[$lastBlock]['changed']['lines'] += $lines; + } + } + } + $changes[] = $blocks; + } + return $changes; + } + + /** + * Given two strings, determine where the changes in the two strings + * begin, and where the changes in the two strings end. + * + * @param string $fromLine The first string. + * @param string $toLine The second string. + * @return array Array containing the starting position (0 by default) and the ending position (-1 by default) + */ + private function getChangeExtent($fromLine, $toLine) + { + $start = 0; + $limit = min(strlen($fromLine), strlen($toLine)); + while($start < $limit && $fromLine{$start} == $toLine{$start}) { + ++$start; + } + $end = -1; + $limit = $limit - $start; + while(-$end <= $limit && substr($fromLine, $end, 1) == substr($toLine, $end, 1)) { + --$end; + } + return array( + $start, + $end + 1 + ); + } + + /** + * Format a series of lines suitable for output in a HTML rendered diff. + * This involves replacing tab characters with spaces, making the HTML safe + * for output, ensuring that double spaces are replaced with   etc. + * + * @param array $lines Array of lines to format. + * @return array Array of the formatted lines. + */ + private function formatLines($lines) + { + $lines = array_map(array($this, 'ExpandTabs'), $lines); + $lines = array_map(array($this, 'HtmlSafe'), $lines); + foreach($lines as &$line) { + $line = preg_replace_callback('# ( +)|^ #', __CLASS__."::fixSpaces", $line); + } + return $lines; + } + + /** + * Replace a string containing spaces with a HTML representation using  . + * + * @param string $matches Regex matches array. + * @return string The HTML representation of the string. + */ + public static function fixSpaces($matches) + { + $spaces = isset($matches[1]) ? $matches[1] : ''; + $count = strlen($spaces); + if($count == 0) { + return ''; + } + + $div = floor($count / 2); + $mod = $count % 2; + return str_repeat('  ', $div).str_repeat(' ', $mod); + } + + /** + * Replace tabs in a single line with a number of spaces as defined by the tabSize option. + * + * @param string $line The containing tabs to convert. + * @return string The line with the tabs converted to spaces. + */ + private function expandTabs($line) + { + return str_replace("\t", str_repeat(' ', $this->options['tabSize']), $line); + } + + /** + * Make a string containing HTML safe for output on a page. + * + * @param string $string The string. + * @return string The string with the HTML characters replaced by entities. + */ + private function htmlSafe($string) + { + return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8'); + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Inline.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Inline.php new file mode 100755 index 000000000..60e8005a5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/Inline.php @@ -0,0 +1,143 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/Array.php'; + +class Diff_Renderer_Html_Inline extends Diff_Renderer_Html_Array +{ + /** + * Render a and return diff with changes between the two sequences + * displayed inline (under each other) + * + * @return string The generated inline diff. + */ + public function render() + { + $changes = parent::render(); + $html = ''; + if(empty($changes)) { + return $html; + } + + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + foreach($changes as $i => $blocks) { + // If this is a separate block, we're condensing code so output ..., + // indicating a significant portion of the code has been collapsed as + // it is the same + if($i > 0) { + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + foreach($blocks as $change) { + $html .= ''; + // Equal changes should be shown on both sides of the diff + if($change['tag'] == 'equal') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Added lines only on the right side + else if($change['tag'] == 'insert') { + foreach($change['changed']['lines'] as $no => $line) { + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show deleted lines only on the left side + else if($change['tag'] == 'delete') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show modified lines on both sides + else if($change['tag'] == 'replace') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + foreach($change['changed']['lines'] as $no => $line) { + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + $html .= ''; + } + } + $html .= '
    OldNewDifferences
     
    '.$fromLine.''.$toLine.''.$line.'
     '.$toLine.''.$line.' 
    '.$fromLine.' '.$line.' 
    '.$fromLine.' '.$line.'
    '.$toLine.' '.$line.'
    '; + return $html; + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/SideBySide.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/SideBySide.php new file mode 100755 index 000000000..307af1c32 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Html/SideBySide.php @@ -0,0 +1,163 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/Array.php'; + +class Diff_Renderer_Html_SideBySide extends Diff_Renderer_Html_Array +{ + /** + * Render a and return diff with changes between the two sequences + * displayed side by side. + * + * @return string The generated side by side diff. + */ + public function render() + { + $changes = parent::render(); + + $html = ''; + if(empty($changes)) { + return $html; + } + + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + foreach($changes as $i => $blocks) { + if($i > 0) { + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + foreach($blocks as $change) { + $html .= ''; + // Equal changes should be shown on both sides of the diff + if($change['tag'] == 'equal') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Added lines only on the right side + else if($change['tag'] == 'insert') { + foreach($change['changed']['lines'] as $no => $line) { + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show deleted lines only on the left side + else if($change['tag'] == 'delete') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show modified lines on both sides + else if($change['tag'] == 'replace') { + if(count($change['base']['lines']) >= count($change['changed']['lines'])) { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + if(!isset($change['changed']['lines'][$no])) { + $toLine = ' '; + $changedLine = ' '; + } + else { + $toLine = $change['base']['offset'] + $no + 1; + $changedLine = ''.$change['changed']['lines'][$no].''; + } + $html .= ''; + $html .= ''; + $html .= ''; + } + } + else { + foreach($change['changed']['lines'] as $no => $changedLine) { + if(!isset($change['base']['lines'][$no])) { + $fromLine = ' '; + $line = ' '; + } + else { + $fromLine = $change['base']['offset'] + $no + 1; + $line = ''.$change['base']['lines'][$no].''; + } + $html .= ''; + $html .= ''; + $html .= ''; + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + } + $html .= ''; + } + } + $html .= '
    Old VersionNew Version
      
    '.$fromLine.''.$line.' '.$toLine.''.$line.' 
      '.$toLine.''.$line.' 
    '.$fromLine.''.$line.'   
    '.$fromLine.''.$line.' '.$toLine.''.$changedLine.'
    '.$fromLine.''.$line.' '.$toLine.''.$changedLine.'
    '; + return $html; + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Context.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Context.php new file mode 100755 index 000000000..1200b01c9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Context.php @@ -0,0 +1,128 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/../Abstract.php'; + +class Diff_Renderer_Text_Context extends Diff_Renderer_Abstract +{ + /** + * @var array Array of the different opcode tags and how they map to the context diff equivalent. + */ + private $tagMap = array( + 'insert' => '+', + 'delete' => '-', + 'replace' => '!', + 'equal' => ' ' + ); + + /** + * Render and return a context formatted (old school!) diff file. + * + * @return string The generated context diff. + */ + public function render() + { + $diff = ''; + $opCodes = $this->diff->getGroupedOpcodes(); + foreach($opCodes as $group) { + $diff .= "***************\n"; + $lastItem = count($group)-1; + $i1 = $group[0][1]; + $i2 = $group[$lastItem][2]; + $j1 = $group[0][3]; + $j2 = $group[$lastItem][4]; + + if($i2 - $i1 >= 2) { + $diff .= '*** '.($group[0][1] + 1).','.$i2." ****\n"; + } + else { + $diff .= '*** '.$i2." ****\n"; + } + + if($j2 - $j1 >= 2) { + $separator = '--- '.($j1 + 1).','.$j2." ----\n"; + } + else { + $separator = '--- '.$j2." ----\n"; + } + + $hasVisible = false; + foreach($group as $code) { + if($code[0] == 'replace' || $code[0] == 'delete') { + $hasVisible = true; + break; + } + } + + if($hasVisible) { + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'insert') { + continue; + } + $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetA($i1, $i2))."\n"; + } + } + + $hasVisible = false; + foreach($group as $code) { + if($code[0] == 'replace' || $code[0] == 'insert') { + $hasVisible = true; + break; + } + } + + $diff .= $separator; + + if($hasVisible) { + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'delete') { + continue; + } + $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetB($j1, $j2))."\n"; + } + } + } + return $diff; + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Unified.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Unified.php new file mode 100755 index 000000000..e94d951d5 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/Renderer/Text/Unified.php @@ -0,0 +1,87 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/../Abstract.php'; + +class Diff_Renderer_Text_Unified extends Diff_Renderer_Abstract +{ + /** + * Render and return a unified diff. + * + * @return string The unified diff. + */ + public function render() + { + $diff = ''; + $opCodes = $this->diff->getGroupedOpcodes(); + foreach($opCodes as $group) { + $lastItem = count($group)-1; + $i1 = $group[0][1]; + $i2 = $group[$lastItem][2]; + $j1 = $group[0][3]; + $j2 = $group[$lastItem][4]; + + if($i1 == 0 && $i2 == 0) { + $i1 = -1; + $i2 = -1; + } + + $diff .= '@@ -'.($i1 + 1).','.($i2 - $i1).' +'.($j1 + 1).','.($j2 - $j1)." @@\n"; + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'equal') { + $diff .= ' '.implode("\n ", $this->diff->GetA($i1, $i2))."\n"; + } + else { + if($tag == 'replace' || $tag == 'delete') { + $diff .= '-'.implode("\n-", $this->diff->GetA($i1, $i2))."\n"; + } + + if($tag == 'replace' || $tag == 'insert') { + $diff .= '+'.implode("\n+", $this->diff->GetB($j1, $j2))."\n"; + } + } + } + } + return $diff; + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/SequenceMatcher.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/SequenceMatcher.php new file mode 100755 index 000000000..67a903b39 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/php-diff/lib/Diff/SequenceMatcher.php @@ -0,0 +1,748 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package Diff + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +class Diff_SequenceMatcher +{ + /** + * @var string|array Either a string or an array containing a callback function to determine if a line is "junk" or not. + */ + private $junkCallback = null; + + /** + * @var array The first sequence to compare against. + */ + private $a = null; + + /** + * @var array The second sequence. + */ + private $b = null; + + /** + * @var array Array of characters that are considered junk from the second sequence. Characters are the array key. + */ + private $junkDict = array(); + + /** + * @var array Array of indices that do not contain junk elements. + */ + private $b2j = array(); + + private $options = array(); + + private $defaultOptions = array( + 'ignoreNewLines' => false, + 'ignoreWhitespace' => false, + 'ignoreCase' => false + ); + + /** + * The constructor. With the sequences being passed, they'll be set for the + * sequence matcher and it will perform a basic cleanup & calculate junk + * elements. + * + * @param string|array $a A string or array containing the lines to compare against. + * @param string|array $b A string or array containing the lines to compare. + * @param string|array $junkCallback Either an array or string that references a callback function (if there is one) to determine 'junk' characters. + * @param array $options + */ + public function __construct($a, $b, $junkCallback=null, $options) + { + $this->a = null; + $this->b = null; + $this->junkCallback = $junkCallback; + $this->setOptions($options); + $this->setSequences($a, $b); + } + + /** + * Set new options + * + * @param array $options + */ + public function setOptions($options) + { + $this->options = array_merge($this->defaultOptions, $options); + } + + /** + * Set the first and second sequences to use with the sequence matcher. + * + * @param string|array $a A string or array containing the lines to compare against. + * @param string|array $b A string or array containing the lines to compare. + */ + public function setSequences($a, $b) + { + $this->setSeq1($a); + $this->setSeq2($b); + } + + /** + * Set the first sequence ($a) and reset any internal caches to indicate that + * when calling the calculation methods, we need to recalculate them. + * + * @param string|array $a The sequence to set as the first sequence. + */ + public function setSeq1($a) + { + if(!is_array($a)) { + $a = str_split($a); + } + if($a == $this->a) { + return; + } + + $this->a= $a; + $this->matchingBlocks = null; + $this->opCodes = null; + } + + /** + * Set the second sequence ($b) and reset any internal caches to indicate that + * when calling the calculation methods, we need to recalculate them. + * + * @param string|array $b The sequence to set as the second sequence. + */ + public function setSeq2($b) + { + if(!is_array($b)) { + $b = str_split($b); + } + if($b == $this->b) { + return; + } + + $this->b = $b; + $this->matchingBlocks = null; + $this->opCodes = null; + $this->fullBCount = null; + $this->chainB(); + } + + /** + * Generate the internal arrays containing the list of junk and non-junk + * characters for the second ($b) sequence. + */ + private function chainB() + { + $length = count ($this->b); + $this->b2j = array(); + $popularDict = array(); + + for($i = 0; $i < $length; ++$i) { + $char = $this->b[$i]; + if(isset($this->b2j[$char])) { + if($length >= 200 && count($this->b2j[$char]) * 100 > $length) { + $popularDict[$char] = 1; + unset($this->b2j[$char]); + } + else { + $this->b2j[$char][] = $i; + } + } + else { + $this->b2j[$char] = array( + $i + ); + } + } + + // Remove leftovers + foreach(array_keys($popularDict) as $char) { + unset($this->b2j[$char]); + } + + $this->junkDict = array(); + if(is_callable($this->junkCallback)) { + foreach(array_keys($popularDict) as $char) { + if(call_user_func($this->junkCallback, $char)) { + $this->junkDict[$char] = 1; + unset($popularDict[$char]); + } + } + + foreach(array_keys($this->b2j) as $char) { + if(call_user_func($this->junkCallback, $char)) { + $this->junkDict[$char] = 1; + unset($this->b2j[$char]); + } + } + } + } + + /** + * Checks if a particular character is in the junk dictionary + * for the list of junk characters. + * @param $b + * @return boolean True if the character is considered junk. False if not. + */ + private function isBJunk($b) + { + if(isset($this->juncDict[$b])) { + return true; + } + + return false; + } + + /** + * Find the longest matching block in the two sequences, as defined by the + * lower and upper constraints for each sequence. (for the first sequence, + * $alo - $ahi and for the second sequence, $blo - $bhi) + * + * Essentially, of all of the maximal matching blocks, return the one that + * startest earliest in $a, and all of those maximal matching blocks that + * start earliest in $a, return the one that starts earliest in $b. + * + * If the junk callback is defined, do the above but with the restriction + * that the junk element appears in the block. Extend it as far as possible + * by matching only junk elements in both $a and $b. + * + * @param int $alo The lower constraint for the first sequence. + * @param int $ahi The upper constraint for the first sequence. + * @param int $blo The lower constraint for the second sequence. + * @param int $bhi The upper constraint for the second sequence. + * @return array Array containing the longest match that includes the starting position in $a, start in $b and the length/size. + */ + public function findLongestMatch($alo, $ahi, $blo, $bhi) + { + $a = $this->a; + $b = $this->b; + + $bestI = $alo; + $bestJ = $blo; + $bestSize = 0; + + $j2Len = array(); + $nothing = array(); + + for($i = $alo; $i < $ahi; ++$i) { + $newJ2Len = array(); + $jDict = $this->arrayGetDefault($this->b2j, $a[$i], $nothing); + foreach($jDict as $jKey => $j) { + if($j < $blo) { + continue; + } + else if($j >= $bhi) { + break; + } + + $k = $this->arrayGetDefault($j2Len, $j -1, 0) + 1; + $newJ2Len[$j] = $k; + if($k > $bestSize) { + $bestI = $i - $k + 1; + $bestJ = $j - $k + 1; + $bestSize = $k; + } + } + + $j2Len = $newJ2Len; + } + + while($bestI > $alo && $bestJ > $blo && !$this->isBJunk($b[$bestJ - 1]) && + !$this->linesAreDifferent($bestI - 1, $bestJ - 1)) { + --$bestI; + --$bestJ; + ++$bestSize; + } + + while($bestI + $bestSize < $ahi && ($bestJ + $bestSize) < $bhi && + !$this->isBJunk($b[$bestJ + $bestSize]) && !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) { + ++$bestSize; + } + + while($bestI > $alo && $bestJ > $blo && $this->isBJunk($b[$bestJ - 1]) && + !$this->isLineDifferent($bestI - 1, $bestJ - 1)) { + --$bestI; + --$bestJ; + ++$bestSize; + } + + while($bestI + $bestSize < $ahi && $bestJ + $bestSize < $bhi && + $this->isBJunk($b[$bestJ + $bestSize]) && !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) { + ++$bestSize; + } + + return array( + $bestI, + $bestJ, + $bestSize + ); + } + + /** + * Check if the two lines at the given indexes are different or not. + * + * @param int $aIndex Line number to check against in a. + * @param int $bIndex Line number to check against in b. + * @return boolean True if the lines are different and false if not. + */ + public function linesAreDifferent($aIndex, $bIndex) + { + $lineA = $this->a[$aIndex]; + $lineB = $this->b[$bIndex]; + + if($this->options['ignoreWhitespace']) { + $replace = array("\t", ' '); + $lineA = str_replace($replace, '', $lineA); + $lineB = str_replace($replace, '', $lineB); + } + + if($this->options['ignoreCase']) { + $lineA = strtolower($lineA); + $lineB = strtolower($lineB); + } + + if($lineA != $lineB) { + return true; + } + + return false; + } + + /** + * Return a nested set of arrays for all of the matching sub-sequences + * in the strings $a and $b. + * + * Each block contains the lower constraint of the block in $a, the lower + * constraint of the block in $b and finally the number of lines that the + * block continues for. + * + * @return array Nested array of the matching blocks, as described by the function. + */ + public function getMatchingBlocks() + { + if(!empty($this->matchingBlocks)) { + return $this->matchingBlocks; + } + + $aLength = count($this->a); + $bLength = count($this->b); + + $queue = array( + array( + 0, + $aLength, + 0, + $bLength + ) + ); + + $matchingBlocks = array(); + while(!empty($queue)) { + list($alo, $ahi, $blo, $bhi) = array_pop($queue); + $x = $this->findLongestMatch($alo, $ahi, $blo, $bhi); + list($i, $j, $k) = $x; + if($k) { + $matchingBlocks[] = $x; + if($alo < $i && $blo < $j) { + $queue[] = array( + $alo, + $i, + $blo, + $j + ); + } + + if($i + $k < $ahi && $j + $k < $bhi) { + $queue[] = array( + $i + $k, + $ahi, + $j + $k, + $bhi + ); + } + } + } + + usort($matchingBlocks, array($this, 'tupleSort')); + + $i1 = 0; + $j1 = 0; + $k1 = 0; + $nonAdjacent = array(); + foreach($matchingBlocks as $block) { + list($i2, $j2, $k2) = $block; + if($i1 + $k1 == $i2 && $j1 + $k1 == $j2) { + $k1 += $k2; + } + else { + if($k1) { + $nonAdjacent[] = array( + $i1, + $j1, + $k1 + ); + } + + $i1 = $i2; + $j1 = $j2; + $k1 = $k2; + } + } + + if($k1) { + $nonAdjacent[] = array( + $i1, + $j1, + $k1 + ); + } + + $nonAdjacent[] = array( + $aLength, + $bLength, + 0 + ); + + $this->matchingBlocks = $nonAdjacent; + return $this->matchingBlocks; + } + + /** + * Return a list of all of the opcodes for the differences between the + * two strings. + * + * The nested array returned contains an array describing the opcode + * which includes: + * 0 - The type of tag (as described below) for the opcode. + * 1 - The beginning line in the first sequence. + * 2 - The end line in the first sequence. + * 3 - The beginning line in the second sequence. + * 4 - The end line in the second sequence. + * + * The different types of tags include: + * replace - The string from $i1 to $i2 in $a should be replaced by + * the string in $b from $j1 to $j2. + * delete - The string in $a from $i1 to $j2 should be deleted. + * insert - The string in $b from $j1 to $j2 should be inserted at + * $i1 in $a. + * equal - The two strings with the specified ranges are equal. + * + * @return array Array of the opcodes describing the differences between the strings. + */ + public function getOpCodes() + { + if(!empty($this->opCodes)) { + return $this->opCodes; + } + + $i = 0; + $j = 0; + $this->opCodes = array(); + + $blocks = $this->getMatchingBlocks(); + foreach($blocks as $block) { + list($ai, $bj, $size) = $block; + $tag = ''; + if($i < $ai && $j < $bj) { + $tag = 'replace'; + } + else if($i < $ai) { + $tag = 'delete'; + } + else if($j < $bj) { + $tag = 'insert'; + } + + if($tag) { + $this->opCodes[] = array( + $tag, + $i, + $ai, + $j, + $bj + ); + } + + $i = $ai + $size; + $j = $bj + $size; + + if($size) { + $this->opCodes[] = array( + 'equal', + $ai, + $i, + $bj, + $j + ); + } + } + return $this->opCodes; + } + + /** + * Return a series of nested arrays containing different groups of generated + * opcodes for the differences between the strings with up to $context lines + * of surrounding content. + * + * Essentially what happens here is any big equal blocks of strings are stripped + * out, the smaller subsets of changes are then arranged in to their groups. + * This means that the sequence matcher and diffs do not need to include the full + * content of the different files but can still provide context as to where the + * changes are. + * + * @param int $context The number of lines of context to provide around the groups. + * @return array Nested array of all of the grouped opcodes. + */ + public function getGroupedOpcodes($context=3) + { + $opCodes = $this->getOpCodes(); + if(empty($opCodes)) { + $opCodes = array( + array( + 'equal', + 0, + 1, + 0, + 1 + ) + ); + } + + if($opCodes[0][0] == 'equal') { + $opCodes[0] = array( + $opCodes[0][0], + max($opCodes[0][1], $opCodes[0][2] - $context), + $opCodes[0][2], + max($opCodes[0][3], $opCodes[0][4] - $context), + $opCodes[0][4] + ); + } + + $lastItem = count($opCodes) - 1; + if($opCodes[$lastItem][0] == 'equal') { + list($tag, $i1, $i2, $j1, $j2) = $opCodes[$lastItem]; + $opCodes[$lastItem] = array( + $tag, + $i1, + min($i2, $i1 + $context), + $j1, + min($j2, $j1 + $context) + ); + } + + $maxRange = $context * 2; + $groups = array(); + $group = array(); + foreach($opCodes as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'equal' && $i2 - $i1 > $maxRange) { + $group[] = array( + $tag, + $i1, + min($i2, $i1 + $context), + $j1, + min($j2, $j1 + $context) + ); + $groups[] = $group; + $group = array(); + $i1 = max($i1, $i2 - $context); + $j1 = max($j1, $j2 - $context); + } + $group[] = array( + $tag, + $i1, + $i2, + $j1, + $j2 + ); + } + + if(!empty($group) && !(count($group) == 1 && $group[0][0] == 'equal')) { + $groups[] = $group; + } + + return $groups; + } + + /** + * Return a measure of the similarity between the two sequences. + * This will be a float value between 0 and 1. + * + * Out of all of the ratio calculation functions, this is the most + * expensive to call if getMatchingBlocks or getOpCodes is yet to be + * called. The other calculation methods (quickRatio and realquickRatio) + * can be used to perform quicker calculations but may be less accurate. + * + * The ratio is calculated as (2 * number of matches) / total number of + * elements in both sequences. + * + * @return float The calculated ratio. + */ + public function Ratio() + { + $matches = array_reduce($this->getMatchingBlocks(), array($this, 'ratioReduce'), 0); + return $this->calculateRatio($matches, count ($this->a) + count ($this->b)); + } + + /** + * Helper function to calculate the number of matches for Ratio(). + * + * @param int $sum The running total for the number of matches. + * @param array $triple Array containing the matching block triple to add to the running total. + * @return int The new running total for the number of matches. + */ + private function ratioReduce($sum, $triple) + { + return $sum + ($triple[count($triple) - 1]); + } + + /** + * Quickly return an upper bound ratio for the similarity of the strings. + * This is quicker to compute than Ratio(). + * + * @return float The calculated ratio. + */ + private function quickRatio() + { + if($this->fullBCount === null) { + $this->fullBCount = array(); + $bLength = count ($this->b); + for($i = 0; $i < $bLength; ++$i) { + $char = $this->b[$i]; + $this->fullBCount[$char] = $this->arrayGetDefault($this->fullBCount, $char, 0) + 1; + } + } + + $avail = array(); + $matches = 0; + $aLength = count ($this->a); + for($i = 0; $i < $aLength; ++$i) { + $char = $this->a[$i]; + if(isset($avail[$char])) { + $numb = $avail[$char]; + } + else { + $numb = $this->arrayGetDefault($this->fullBCount, $char, 0); + } + $avail[$char] = $numb - 1; + if($numb > 0) { + ++$matches; + } + } + + $this->calculateRatio($matches, count ($this->a) + count ($this->b)); + } + + /** + * Return an upper bound ratio really quickly for the similarity of the strings. + * This is quicker to compute than Ratio() and quickRatio(). + * + * @return float The calculated ratio. + */ + private function realquickRatio() + { + $aLength = count ($this->a); + $bLength = count ($this->b); + + return $this->calculateRatio(min($aLength, $bLength), $aLength + $bLength); + } + + /** + * Helper function for calculating the ratio to measure similarity for the strings. + * The ratio is defined as being 2 * (number of matches / total length) + * + * @param int $matches The number of matches in the two strings. + * @param int $length The length of the two strings. + * @return float The calculated ratio. + */ + private function calculateRatio($matches, $length=0) + { + if($length) { + return 2 * ($matches / $length); + } + else { + return 1; + } + } + + /** + * Helper function that provides the ability to return the value for a key + * in an array of it exists, or if it doesn't then return a default value. + * Essentially cleaner than doing a series of if(isset()) {} else {} calls. + * + * @param array $array The array to search. + * @param string $key The key to check that exists. + * @param mixed $default The value to return as the default value if the key doesn't exist. + * @return mixed The value from the array if the key exists or otherwise the default. + */ + private function arrayGetDefault($array, $key, $default) + { + if(isset($array[$key])) { + return $array[$key]; + } + else { + return $default; + } + } + + /** + * Sort an array by the nested arrays it contains. Helper function for getMatchingBlocks + * + * @param array $a First array to compare. + * @param array $b Second array to compare. + * @return int -1, 0 or 1, as expected by the usort function. + */ + private function tupleSort($a, $b) + { + $max = max(count($a), count($b)); + for($i = 0; $i < $max; ++$i) { + if($a[$i] < $b[$i]) { + return -1; + } + else if($a[$i] > $b[$i]) { + return 1; + } + } + + if(count($a) == count($b)) { + return 0; + } + else if(count($a) < count($b)) { + return -1; + } + else { + return 1; + } + } +} \ No newline at end of file diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitattributes b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitattributes new file mode 100755 index 000000000..46a059651 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitattributes @@ -0,0 +1,4 @@ +* text=auto +*.bat eol=crlf + +docs export-ignore diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitignore b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitignore new file mode 100755 index 000000000..4d8371b53 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.gitignore @@ -0,0 +1,6 @@ +*.tgz +*.phar +behat.yml +vendor +composer.lock +phpspec.phar diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.scrutinizer.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.scrutinizer.yml new file mode 100755 index 000000000..98fe8e081 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.scrutinizer.yml @@ -0,0 +1,26 @@ +imports: + - javascript + - php + +tools: + php_code_sniffer: + filter: + excluded-paths: [ spec/*, integration/*, features/* ] + config: + standard: PSR2 + + php_analyzer: + filter: + excluded-paths: [ spec/*, integration/* ] + + php_sim: + filter: + excluded-paths: [ spec/*, integration/* ] + +build_failure_conditions: + - 'issues.label("coding-style").exists' + +filter: + excluded_paths: + - docs/* + - vendor/* diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.travis.yml b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.travis.yml new file mode 100755 index 000000000..e98f08c2b --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/.travis.yml @@ -0,0 +1,49 @@ +language: php + +sudo: false + +addons: + apt: + packages: + - expect + +cache: + directories: + - $HOME/.composer/cache + +matrix: + include: + - php: 5.3 + - php: 5.3.3 + env: DEPENDENCIES='low' + SMOKE='true' + - php: 5.4 + - php: 5.5 + - php: 5.6 + env: DEPENDENCIES='dev' + - php: 5.6 + env: SMOKE='true' + - php: hhvm + env: SMOKE='true' + - php: 7.0 + allow_failures: + - php: 7.0 + - env: DEPENDENCIES='dev' + fast_finish: true + +before_install: + - composer selfupdate + +install: + - export COMPOSER_ROOT_VERSION=dev-master + - if [ "$DEPENDENCIES" == "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi; + - if [ "$DEPENDENCIES" != "low" ]; then composer update; fi; + - if [ "$DEPENDENCIES" == "low" ]; then composer update --prefer-lowest; fi; + +before_script: + - echo "= 50400) echo ',@php5.4';" > php_version_tags.php + +script: + - bin/phpspec run --format=pretty + - ./vendor/bin/phpunit --testdox + - ./vendor/bin/behat --format=pretty `if [ "$SMOKE" == "true" ]; then echo '--profile=smoke'; fi;` --tags '~@php-version'`php php_version_tags.php` diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CHANGES.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CHANGES.md new file mode 100755 index 000000000..2c66b1446 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CHANGES.md @@ -0,0 +1,169 @@ +2.2.1 / 2015-05-30 +================== + +* Fix false positives in `shouldHaveKeyWithValue` matcher +* Fix fatal error in edge case when method call parameters don't match expectations + +2.2.0 / 2015-04-18 +================== + +* No changes from rc1 + +2.2.0-rc1 / 2015-04-13 +====================== + +* No changes from beta2 + +2.2.0-beta2 / 2015-04-03 +======================== + + * Better diffs when presenting unexpected method arguments + * Better handling of methods delclared inside Traits when faking + +2.2.0-beta / 2015-03-28 +======================= + + * Offer to generate interfaces for missing typehinted collaborators + * Support for TAP format output + * Remove deprecated usage of Symfony DialogHelper + * New array `shouldHaveKeyWithValue` matcher + * Clearer error message when specs have incorrect namespace prefix + * Fix suite rerunning for HHVM + +Backward Compatibility +---------------------- + + * The unused `ask` and `askAndValidate` methods on `Console\IO` have been removed + +2.1.1 / 2015-01-09 +================== + + * Smoother rendering for progress bar + * Fixed progress bar for case where no examples are found + * Tidier output alignment + block width + * Removed deprecated calls to Yaml::parse + * More accurate lower bounds for composer installation + +2.1.0 / 2014-12-14 +================== + + * No changes from RC3 + +2.1.0-RC3 / 2014-12-04 +====================== + + * Removed minor BC break introduced in RC2 + +2.1.0-RC2 / 2014-11-14 +====================== + + * Specify bootstrap file via configuration + * Correct error codes while using --stop-on-failure + * Better detection of empty specs + * Fixed issue where non-spec files in spec folder caused errors + * Better PSR-4 support + +2.1.0-RC1 / 2014-09-14 +====================== + + * Allow objects to be instantiated via static factory methods + * Automatic generation of return statements using '--fake' + * Test suite is automatically rerun when classes or methods have been generated + * Allow examples to mark themselves as skipped + * PSR-4 support + * PSR-0 locator now supports underscores correctly + * Ability to specify a custom bootstrap file using '--bootstrap' (for autoloader registration etc) + * Ability to have a personal .phpspec.yml in home folder + * Progress bar grows from left to right and flickers less + * Improved diffs for object comparison + * Throw an exception when construction method is redefined + * Non-zero exit code when dependencies are missing + * Respect exit code of commands other than 'run' + * Higher CLI verbosity levels are handled properly + * Code Generation and Stop on Failure are configurable through phpspec.yml + * Fixes for object instantiation changes in newer versions of PHP + * PHP 5.6 support + * Fixes for progress bar sometimes rounding up to 100% when not all specs passed + * Support for non-standard Composer autoloader location + * Improved hhvm support + * Extensions can now register new command + * Resource locator de-duplicates resources (supports custom locators in extensions) + +2.0.1 / 2014-07-01 +================== + + * Fixed the loading of the autoloader for projects using a custom composer vendor folder + +2.0.0 / 2014-03-19 +================== + + * Improve support to windows + * Improve support to hhvm + * Improve acceptance tests coverage with Behat + +2.0.0-RC4 / 2014-02-21 +====================== + + * Revamped junit formatter + * Fixed #269 Problem with exception masking and generation for not found class + * HHVM is officially supported + * Add psr0 validator + * Remove Nyan from core + * Added an exception if the specified config file does not exist + * Fixed a problem with generating a constructor when it is first time added + * Improved help + * Fixed the suite runner in fast machines + +2.0.0-RC3 / 2014-01-01 +====================== + + * Fixed the Prophecy constraint as the new release is 1.1 + * Refactored formatters to be defined as services + +2.0.0-RC2 / 2013-12-30 +====================== + + * Fixed the invocation of methods expecting an argument passed by reference + * Fixed the instantiation of the wrapped object in shouldThrow + +2.0.0-RC1 / 2013-12-26 +====================== + + * Bump the Prophecy requirement to ``~1.0.5@dev`` + * Added a JUnit formatter + * Added the ``--stop-on-failure`` option + * Fixed the support of the ``--no-interaction`` option + * Added more events to add extension points + * Added the number of specs in the console output + * Fixed the handling of Windows line endings in the StringEngine and in reading doc comments + * Added extension points in the template loading + * Added a constructor generator + * Added a HTML formatter + * Added a nyan cat formatter + +2.0.0beta4 / 2013-05-19 +======================= + + * Add collaborator constructor setter + * Fix couple of bugs in Prophecy integration layer + * New (old) dot formatter + +2.0.0beta3 / 2013-05-01 +======================= + + * Prevent loading of unexisting PHP files + * Fix typos in the error messages + +2.0.0beta2 / 2013-04-30 +======================= + + * Bump required Prophecy version to 1.0.1 + * Support non-string values with ArrayContain matcher + * Create `src` folder if does not exist + * Fix stack trace and matchers failure printing + +2.0.0beta1 / 2013-04-29 +======================= + + * Initial release + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CONTRIBUTING.md b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CONTRIBUTING.md new file mode 100755 index 000000000..74d9cb161 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/CONTRIBUTING.md @@ -0,0 +1,25 @@ +Contributing +============ + +PhpSpec is an open source, community-driven project. If you'd like to contribute, +feel free to do this, but remember to follow this few simple rules: + +Branching strategy +------------------- + +- __Always__ base your changes on the `master` branch (all new development happens here), +- When you create Pull Request, always select `master` branch as target, otherwise it +will be closed (this is selected by default). + +Coverage +-------- + +- All classes that interact solely with the core logic should be covered by Specs +- Any infrastructure adaptors should be covered by integration tests using PHPUnit +- All features should be covered with .feature descriptions automated with Behat + +Code style / Formatting +----------------------- + +- All new classes must carry the standard copyright notice docblock +- All code in the `src` folder must follow the PSR-2 standard diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/LICENSE b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/LICENSE new file mode 100755 index 000000000..6ca534840 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013-2014 Konstantin Kudryashov + Marcello Duarte + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/Makefile b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/Makefile new file mode 100755 index 000000000..b75c34c69 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/Makefile @@ -0,0 +1,13 @@ +all: + @echo "Only build-phar target is currently supported." + +build-phar: + @echo "--> Checking for composer command line tool" + command -v composer >/dev/null && continue || { echo "composer command not found."; exit 1; } + @echo "--> Cleaning vendor directory" + rm -Rfv vendor + @echo "--> Installing dependencies without dev" + composer install --no-dev + @echo "--> Building Phar" + box build + @echo "--> Success" diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/README.rst b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/README.rst new file mode 100755 index 000000000..5300eb9eb --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/README.rst @@ -0,0 +1,29 @@ +phpspec +======= + +The main website with documentation is at `http://www.phpspec.net `_. + +.. image:: https://travis-ci.org/phpspec/phpspec.svg?branch=master + :target: http://travis-ci.org/phpspec/phpspec + :alt: Master Travis Build Status + +.. image:: https://scrutinizer-ci.com/g/phpspec/phpspec/badges/quality-score.png?b=master + :target: https://scrutinizer-ci.com/g/phpspec/phpspec/build-status/master + :alt: Master Scrutinizer Quality Score + +Installing Dependencies +----------------------- + +Dependencies are handled via `composer `_:: + + wget -nc http://getcomposer.org/composer.phar + php composer.phar install + +Developer's mailing list +------------------------ + +For development discussion subscribe to `phpspec-dev@googlegroups.com `_. + +Community +--------- +Check out #phpspec on irc.freenode.net. diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/behat.yml.dist b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/behat.yml.dist new file mode 100755 index 000000000..926ea78ff --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/behat.yml.dist @@ -0,0 +1,13 @@ +default: + suites: + application: + contexts: [ ApplicationContext, FilesystemContext ] + formatters: + progress: ~ + +smoke: + suites: + smoke: + contexts: [ IsolatedProcessContext, FilesystemContext ] + filters: { tags: @smoke } + diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/bin/phpspec b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/bin/phpspec new file mode 100755 index 000000000..9f72a4c7f --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/bin/phpspec @@ -0,0 +1,26 @@ +#!/usr/bin/env php +run(); diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/box.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/box.json new file mode 100755 index 000000000..ba5ef43c1 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/box.json @@ -0,0 +1,19 @@ +{ + "chmod": "0755", + "directories": [ + "src" + ], + "files": [ + "LICENSE" + ], + "finder": [ + { + "name": "*.php", + "exclude": ["Tests"], + "in": "vendor" + } + ], + "main": "bin/phpspec", + "output": "phpspec.phar", + "stub": true +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/composer.json b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/composer.json new file mode 100755 index 000000000..263e8ba97 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/composer.json @@ -0,0 +1,64 @@ +{ + "name": "phpspec/phpspec", + "description": "Specification-oriented BDD framework for PHP 5.3+", + "keywords": ["BDD", "SpecBDD", "TDD", "spec", "specification", "tests", "testing"], + "homepage": "http://phpspec.net/", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "homepage": "http://marcelloduarte.net/" + } + ], + + "require": { + "php": ">=5.3.3", + "phpspec/prophecy": "~1.4", + "phpspec/php-diff": "~1.0.0", + "sebastian/exporter": "~1.0", + "symfony/console": "~2.3", + "symfony/event-dispatcher": "~2.1", + "symfony/finder": "~2.1", + "symfony/process": "~2.1", + "symfony/yaml": "~2.1", + "doctrine/instantiator": "^1.0.1" + }, + + "require-dev": { + "behat/behat": "^3.0.11", + "bossa/phpspec2-expect": "~1.0", + "symfony/filesystem": "~2.1", + "symfony/process": "~2.1", + "phpunit/phpunit": "~4.4" + }, + + "suggest": { + "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" + }, + + "autoload": { + "psr-0": { + "PhpSpec": "src/" + } + }, + + "autoload-dev": { + "psr-0": { + "spec\\PhpSpec": "." + } + }, + + "bin": ["bin/phpspec"], + + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + } +} diff --git a/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/ApplicationContext.php b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/ApplicationContext.php new file mode 100755 index 000000000..ac170eea9 --- /dev/null +++ b/modules/devshop/devshop_cloud/providers/digitalocean/digital-ocean-master/vendor/phpspec/phpspec/features/bootstrap/ApplicationContext.php @@ -0,0 +1,244 @@ +application = new Application('2.1-dev'); + $this->application->setAutoExit(false); + + $this->tester = new ApplicationTester($this->application); + + $this->setupReRunner(); + $this->setupPrompter(); + } + + private function setupPrompter() + { + $this->prompter = new Prompter(); + + $this->application->getContainer()->set('console.prompter', $this->prompter); + } + + private function setupReRunner() + { + $this->reRunner = new ReRunner; + $this->application->getContainer()->set('process.rerunner.platformspecific', $this->reRunner); + } + + /** + * @Given I have started describing the :class class + * @Given I start describing the :class class + */ + public function iDescribeTheClass($class) + { + $arguments = array( + 'command' => 'describe', + 'class' => $class + ); + + expect($this->tester->run($arguments, array('interactive' => false)))->toBe(0); + } + + /** + * @When I run phpspec (non interactively) + * @When I run phpspec using the :formatter format + * @When I run phpspec with the :option option + * @When /I run phpspec with option (?P
    - -
    + +
    + + site}/tasks")): ?> + + site/tasks"); ?>" class="btn btn-text btn-sm text-muted" title=""> + + + + site}/errors")): ?> - site/errors"); ?>" class="btn btn-text text-muted small" title=""> - - + site/errors"); ?>" class="btn btn-text btn-sm text-muted" title=""> + + - site/backups"); ?>" class="btn btn-text text-muted small" title=""> + site/backups"); ?>" class="btn btn-text btn-sm text-muted" title=""> + + + +
    + + +
    @@ -546,117 +623,6 @@
    - -
    - - settings->deploy as $hook_type => $enabled): ?> - -
    - - -
    - - -
    -
    - git_sha): ?> diff --git a/themes/boots/template.php b/themes/boots/template.php index 03509fc4f..f7a9967cd 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -208,6 +208,16 @@ function boots_preprocess_environment(&$vars) { } $vars['environment'] = $environment; + + // Detect hooks.yml file. + if (file_exists($environment->repo_root . '/.hooks.yaml') + || file_exists($environment->repo_root . '/.hooks.yml') + || file_exists($environment->repo_root . '/.hooks')) { + $vars['hooks_yml_note'] = t('!file found:'); + } + else { + $vars['hooks_yml_note'] = t('Unable to find a file named .hooks, .hooks.yml, or .hooks.yaml. Add one or disable "Run deploy commands in the .hooks file" in project or environment settings.'); + } } /** From f5268e770f29adef37addc0d42473d859318dbcd Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 15:40:34 -0400 Subject: [PATCH 2780/3476] Fixing the bad link to task logs in the dropdown. --- themes/boots/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/boots/template.php b/themes/boots/template.php index f7a9967cd..393a0216f 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -111,7 +111,7 @@ function boots_preprocess_environment(&$vars) { $environment->active_tasks = 0; $items = array(); - $items[] = l(' ' . t('Task Logs'), "node/$project->nid/logs/$environment->name", array( + $items[] = l(' ' . t('Task Logs'), "node/$environment->site/logs", array( 'html' => TRUE, 'attributes' => array( 'class' => array( From 48446932d92e14735e70c60e119bb8ef7777d077 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 15:44:53 -0400 Subject: [PATCH 2781/3476] Fixing the projects dashboard with matchHeight() --- themes/boots/js/boots.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/themes/boots/js/boots.js b/themes/boots/js/boots.js index 6dba70a22..eeafa166a 100644 --- a/themes/boots/js/boots.js +++ b/themes/boots/js/boots.js @@ -1,7 +1,9 @@ (function ($) { + Drupal.behaviors.devshopTasks = { + attach: function (context, settings) { - // Match the height of environments so the grid doesn't break. - $('.environment-wrapper').matchHeight(); - - + // Match the height of environments so the grid doesn't break. + $('.environment-wrapper').matchHeight(); + }, + } })(jQuery); From dfbb5926776689f09dcaea53ab1a7c1fd548e23c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 15:48:14 -0400 Subject: [PATCH 2782/3476] Fix edit domains link. --- themes/boots/environment.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/boots/environment.tpl.php b/themes/boots/environment.tpl.php index 1e5fb1d89..2268ae47b 100644 --- a/themes/boots/environment.tpl.php +++ b/themes/boots/environment.tpl.php @@ -325,7 +325,7 @@
  •  
  • -
  • nid . '/edit/' . $environment->name, array('query'=> drupal_get_destination())); ?>
  • +
  • site . '/edit'); ?>
  • From d215e6ef48258ea4496d4ce0cd9496b195c86417 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 15:59:58 -0400 Subject: [PATCH 2783/3476] Put environment settings fieldsets in vertical tabs! --- themes/boots/template.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/themes/boots/template.php b/themes/boots/template.php index 393a0216f..a52e8e469 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -836,3 +836,33 @@ function boots_menu_local_tasks(&$variables) { return $output; } + + + +/** + * Implements hook_form_FORM_ID_alter() for node_site_form + * + * "Environment" Settings form. + */ +function boots_form_site_node_form_alter(&$form, &$form_state, $form_id) { + + // Alter form for better UX + + // Project Settings Vertical Tabs + $form['environment_settings'] = array( + '#type' => 'vertical_tabs', + '#weight' => -11, + ); + + $form['environment']['settings']['#group'] = 'environment_settings'; + $form['environment']['settings']['#weight'] = -100; + $form['environment']['settings']['deploy']['#group'] = 'environment_settings'; + $form['hosting_backup_queue']['#group'] = 'environment_settings'; + $form['http_basic_auth']['#group'] = 'environment_settings'; + $form['hosting_logs']['#group'] = 'environment_settings'; + + $form['aliases_wrapper']['#group'] = 'environment_settings'; + $form['aliases_wrapper']['#title'] = t('Domain Names'); + $form['aliases_wrapper']['#prefix'] = ''; + $form['aliases_wrapper']['#suffix'] = ''; +} \ No newline at end of file From 34386d8ce4dd071000f0788020aad2939d44e5b9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:07:15 -0400 Subject: [PATCH 2784/3476] unset menu option for sites as well. --- modules/devshop/devshop_projects/devshop_projects.install | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/devshop/devshop_projects/devshop_projects.install b/modules/devshop/devshop_projects/devshop_projects.install index 71e2b8fc4..0170ff7e5 100644 --- a/modules/devshop/devshop_projects/devshop_projects.install +++ b/modules/devshop/devshop_projects/devshop_projects.install @@ -344,4 +344,11 @@ function devshop_projects_update_6011 () { */ function devshop_projects_update_7000 () { variable_set('menu_options_project', array()); +} + +/** + * Remove the menu form from the project node form. + */ +function devshop_projects_update_7001 () { + variable_set('menu_options_site', array()); } \ No newline at end of file From f5efe9902ecc4b0c19841bfff99376d8048bc778 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:07:36 -0400 Subject: [PATCH 2785/3476] Update comment. --- modules/devshop/devshop_projects/devshop_projects.install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/devshop/devshop_projects/devshop_projects.install b/modules/devshop/devshop_projects/devshop_projects.install index 0170ff7e5..1c2126073 100644 --- a/modules/devshop/devshop_projects/devshop_projects.install +++ b/modules/devshop/devshop_projects/devshop_projects.install @@ -347,7 +347,7 @@ function devshop_projects_update_7000 () { } /** - * Remove the menu form from the project node form. + * Remove the menu form from the site node form. */ function devshop_projects_update_7001 () { variable_set('menu_options_site', array()); From 8f53ba59f562f690f57d1c0a31488f313e19f350 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:08:46 -0400 Subject: [PATCH 2786/3476] Don't try to parse a missing hooks file. --- .../devshop_dothooks/devshop_dothooks.module | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/devshop/devshop_dothooks/devshop_dothooks.module b/modules/devshop/devshop_dothooks/devshop_dothooks.module index 373cc00f2..219155228 100644 --- a/modules/devshop/devshop_dothooks/devshop_dothooks.module +++ b/modules/devshop/devshop_dothooks/devshop_dothooks.module @@ -41,17 +41,18 @@ function devshop_dothooks_devshop_environment_alter(&$environment, $project) { $environment->dothooks_file_path = $hooks_path; // Attempt to parse - try { - $environment->dothooks = $yaml->parse(file_get_contents($hooks_path)); - } - catch (\Symfony\Component\Yaml\Exception\ParseException $e) { - $environment->warnings[] = array( - 'text' => t('Invalid YAML in !file: !message', array( - '!file' => $hooks_path, - '!message' => $e->getMessage(), - )), - 'type' => 'error', - ); + if (!empty($environment->dothooks_file_name)) { + try { + $environment->dothooks = $yaml->parse(file_get_contents($hooks_path)); + } catch (\Symfony\Component\Yaml\Exception\ParseException $e) { + $environment->warnings[] = array( + 'text' => t('Invalid YAML in !file: !message', array( + '!file' => $hooks_path, + '!message' => $e->getMessage(), + )), + 'type' => 'error', + ); + } } } From 182a61fe5da7eec050dee2000fb75cabf3255d8c Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:12:33 -0400 Subject: [PATCH 2787/3476] Move client field to environment settings. --- themes/boots/template.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/themes/boots/template.php b/themes/boots/template.php index a52e8e469..eb9f00e27 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -865,4 +865,7 @@ function boots_form_site_node_form_alter(&$form, &$form_state, $form_id) { $form['aliases_wrapper']['#title'] = t('Domain Names'); $form['aliases_wrapper']['#prefix'] = ''; $form['aliases_wrapper']['#suffix'] = ''; + + $form['environment']['settings']['client'] = $form['client']; + unset($form['client']); } \ No newline at end of file From 5143c109c7103c451b441b2c96ac4c95f5a5c8a9 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:15:16 -0400 Subject: [PATCH 2788/3476] Move helpful note to template.php --- modules/devshop/devshop_projects/inc/forms.inc | 5 ----- themes/boots/template.php | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/devshop/devshop_projects/inc/forms.inc b/modules/devshop/devshop_projects/inc/forms.inc index 790bf1906..fcf7c1f45 100644 --- a/modules/devshop/devshop_projects/inc/forms.inc +++ b/modules/devshop/devshop_projects/inc/forms.inc @@ -692,11 +692,6 @@ function devshop_projects_form_site_node_form_alter(&$form, &$form_state, $form_ // Remove aegir's helpful info panel. unset($form['info']); - // Help Text - $form['note'] = array( - '#markup' => t('These settings apply only to the environment !site', array('!site' => l($environment->name, $environment->url))), '#weight' => -10, - ); - $form['environment'] = array( '#weight' => -9, '#tree' => true, diff --git a/themes/boots/template.php b/themes/boots/template.php index eb9f00e27..686dac9bd 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -854,6 +854,7 @@ function boots_form_site_node_form_alter(&$form, &$form_state, $form_id) { '#weight' => -11, ); + $form['environment']['settings']['#title'] = t('General'); $form['environment']['settings']['#group'] = 'environment_settings'; $form['environment']['settings']['#weight'] = -100; $form['environment']['settings']['deploy']['#group'] = 'environment_settings'; @@ -868,4 +869,7 @@ function boots_form_site_node_form_alter(&$form, &$form_state, $form_id) { $form['environment']['settings']['client'] = $form['client']; unset($form['client']); + + // Help Text + $form['#prefix'] = '

    ' . t('Environment Settings') . ' ' . $form['#node']->environment->name . '

    '; } \ No newline at end of file From dd4bc22484bd51a3bd7484afdcfd77910b14c78d Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:18:40 -0400 Subject: [PATCH 2789/3476] Set "Edit Domains" link to go straight to Domain Names section of environment settings page! --- themes/boots/environment.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/boots/environment.tpl.php b/themes/boots/environment.tpl.php index 2268ae47b..d688704f3 100644 --- a/themes/boots/environment.tpl.php +++ b/themes/boots/environment.tpl.php @@ -325,7 +325,7 @@
  •  
  • -
  • site . '/edit'); ?>
  • +
  • site . '/edit', array('fragment' => 'edit-aliases-wrapper')); ?>
  • From 84b7e92c5752fed0cc9d882d73b3bc70b338789a Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:23:33 -0400 Subject: [PATCH 2790/3476] Move SSL settings to fieldset. Remove URL path fieldset. --- themes/boots/template.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/themes/boots/template.php b/themes/boots/template.php index 686dac9bd..f8a2cd8a7 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -854,7 +854,7 @@ function boots_form_site_node_form_alter(&$form, &$form_state, $form_id) { '#weight' => -11, ); - $form['environment']['settings']['#title'] = t('General'); + $form['environment']['settings']['#title'] = t('General Settings'); $form['environment']['settings']['#group'] = 'environment_settings'; $form['environment']['settings']['#weight'] = -100; $form['environment']['settings']['deploy']['#group'] = 'environment_settings'; @@ -870,6 +870,13 @@ function boots_form_site_node_form_alter(&$form, &$form_state, $form_id) { $form['environment']['settings']['client'] = $form['client']; unset($form['client']); + $form['hosting_ssl_wrapper']['#group'] = 'environment_settings'; + $form['hosting_ssl_wrapper']['#title'] = t('SSL'); + $form['hosting_ssl_wrapper']['#prefix'] = ''; + $form['hosting_ssl_wrapper']['#suffix'] = ''; + // Help Text $form['#prefix'] = '

    ' . t('Environment Settings') . ' ' . $form['#node']->environment->name . '

    '; + + unset($form['path']); } \ No newline at end of file From ae5f54a924b8a22281578303952e5e50f2700a36 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:25:45 -0400 Subject: [PATCH 2791/3476] Fix link to task logs. --- themes/boots/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/boots/template.php b/themes/boots/template.php index f8a2cd8a7..6a07c2c26 100644 --- a/themes/boots/template.php +++ b/themes/boots/template.php @@ -111,7 +111,7 @@ function boots_preprocess_environment(&$vars) { $environment->active_tasks = 0; $items = array(); - $items[] = l(' ' . t('Task Logs'), "node/$environment->site/logs", array( + $items[] = l(' ' . t('Task Logs'), "node/$environment->site/tasks", array( 'html' => TRUE, 'attributes' => array( 'class' => array( From a2680c93f128fc24c7f13e397ea9624093b64bce Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 16:29:54 -0400 Subject: [PATCH 2792/3476] Don't link to environment settings if site node isn't there yet. --- .../devshop_projects/project-nav.tpl.php | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/modules/devshop/devshop_projects/project-nav.tpl.php b/modules/devshop/devshop_projects/project-nav.tpl.php index 640c639ab..62824c4f9 100644 --- a/modules/devshop/devshop_projects/project-nav.tpl.php +++ b/modules/devshop/devshop_projects/project-nav.tpl.php @@ -27,12 +27,21 @@ environments as $environment): ?> -
  • - nid}/edit/env/{$environment->name}"); ?>" class="btn-sm"> - - name; ?> - -
  • + site): ?> +
  • + nid}/edit/env/{$environment->name}"); ?>" class="btn-sm"> + + name; ?> + +
  • + +
  • + + + name; ?> + +
  • + From ed9fc85b072ec9b23615542a2a2357597e8e1ee1 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 19:14:16 -0400 Subject: [PATCH 2793/3476] Make sure a logo isn't too big. --- themes/boots/boots.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/themes/boots/boots.css b/themes/boots/boots.css index 4363fbd5b..0290ed762 100644 --- a/themes/boots/boots.css +++ b/themes/boots/boots.css @@ -914,4 +914,8 @@ p.error { } .node-type-server .panel-body .form-item { margin-right: 2em; +} + +a.logo img { + max-height: 3em; } \ No newline at end of file From 949c8ca74b0eee6af7dbe28876954bdaf88a384e Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 19:39:51 -0400 Subject: [PATCH 2794/3476] Misspelled "verify" task. --- modules/devshop/devshop_projects/devshop_projects.drush.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/devshop/devshop_projects/devshop_projects.drush.inc b/modules/devshop/devshop_projects/devshop_projects.drush.inc index 798b85b08..20550ddae 100644 --- a/modules/devshop/devshop_projects/devshop_projects.drush.inc +++ b/modules/devshop/devshop_projects/devshop_projects.drush.inc @@ -382,7 +382,7 @@ function devshop_projects_post_hosting_install_task($task, $data) { // Now that environment data is stored in project data we need to verify projects a lot. if ($task->ref->type == 'site' && isset($task->ref->project->nid)) { - hosting_add_task($task->ref->project->nid, 'verfiy'); + hosting_add_task($task->ref->project->nid, 'verify'); } } @@ -393,7 +393,7 @@ function devshop_projects_post_hosting_clone_task($task, $data) { // Now that environment data is stored in project data we need to verify projects a lot. if ($task->ref->type == 'site' && isset($task->ref->project->nid)) { - hosting_add_task($task->ref->project->nid, 'verfiy'); + hosting_add_task($task->ref->project->nid, 'verify'); } } @@ -406,7 +406,7 @@ function devshop_projects_post_hosting_import_task($task, $data) { // Now that environment data is stored in project data we need to verify projects a lot. if ($task->ref->type == 'site' && isset($task->ref->project->nid)) { - hosting_add_task($task->ref->project->nid, 'verfiy'); + hosting_add_task($task->ref->project->nid, 'verify'); } // If this is a new platform created by a project, we will create a site here. From 003915fe79819976415d6a02d2465b5668f8c565 Mon Sep 17 00:00:00 2001 From: Jon Pugh Date: Sat, 11 Jun 2016 20:25:59 -0400 Subject: [PATCH 2795/3476] make sure button is small --- themes/boots/environment.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/boots/environment.tpl.php b/themes/boots/environment.tpl.php index d688704f3..8205d12c3 100644 --- a/themes/boots/environment.tpl.php +++ b/themes/boots/environment.tpl.php @@ -417,7 +417,7 @@
    -